xref: /haiku/src/apps/processcontroller/ProcessController.cpp (revision 68d37cfb3a755a7270d772b505ee15c8b18aa5e0)
1 /*
2  * Copyright 2000, Georges-Edouard Berenger. All rights reserved.
3  * Copyright 2006-2018, Haiku, Inc. All rights reserved.
4  * Distributed under the terms of the MIT License.
5  */
6 
7 
8 #include "ProcessController.h"
9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #include <AboutWindow.h>
15 #include <Alert.h>
16 #include <Bitmap.h>
17 #include <Catalog.h>
18 #include <debugger.h>
19 #include <Deskbar.h>
20 #include <Directory.h>
21 #include <Dragger.h>
22 #include <File.h>
23 #include <FindDirectory.h>
24 #include <MessageRunner.h>
25 #include <Path.h>
26 #include <PopUpMenu.h>
27 #include <Roster.h>
28 #include <Screen.h>
29 #include <TextView.h>
30 
31 #include <scheduler.h>
32 #include <syscalls.h>
33 
34 #include "AutoIcon.h"
35 #include "Colors.h"
36 #include "IconMenuItem.h"
37 #include "MemoryBarMenu.h"
38 #include "MemoryBarMenuItem.h"
39 #include "PCWorld.h"
40 #include "Preferences.h"
41 #include "QuitMenu.h"
42 #include "TeamBarMenu.h"
43 #include "TeamBarMenuItem.h"
44 #include "ThreadBarMenu.h"
45 #include "Utilities.h"
46 
47 
48 #undef B_TRANSLATION_CONTEXT
49 #define B_TRANSLATION_CONTEXT "ProcessController"
50 
51 
52 const char* kDeskbarItemName = "ProcessController";
53 const char* kClassName = "ProcessController";
54 
55 const char* kFrameColorPref = "deskbar_frame_color";
56 const char* kIdleColorPref = "deskbar_idle_color";
57 const char* kActiveColorPref = "deskbar_active_color";
58 
59 static const char* const kDebuggerSignature
60 	= "application/x-vnd.Haiku-Debugger";
61 
62 const rgb_color kKernelBlue = {20, 20, 231,	255};
63 const rgb_color kIdleGreen = {110, 190,110,	255};
64 
65 ProcessController* gPCView;
66 uint32 gCPUcount;
67 rgb_color gUserColor;
68 rgb_color gUserColorSelected;
69 rgb_color gIdleColor;
70 rgb_color gIdleColorSelected;
71 rgb_color gKernelColor;
72 rgb_color gKernelColorSelected;
73 rgb_color gFrameColor;
74 rgb_color gFrameColorSelected;
75 rgb_color gMenuBackColorSelected;
76 rgb_color gMenuBackColor;
77 rgb_color gWhiteSelected;
78 ThreadBarMenu* gCurrentThreadBarMenu;
79 bool gInDeskbar = false;
80 
81 #define addtopbottom(x) if (top) popup->AddItem(x); else popup->AddItem(x, 0)
82 
83 status_t thread_popup(void *arg);
84 
85 int32			gPopupFlag = 0;
86 thread_id		gPopupThreadID = 0;
87 
88 typedef struct {
89 	BPoint		where;
90 	BRect		clickToOpenRect;
91 	bool		top;
92 } Tpopup_param;
93 
94 #define DEBUG_THREADS 1
95 
96 status_t thread_quit_application(void *arg);
97 status_t thread_debug_thread(void *arg);
98 
99 typedef struct {
100 	thread_id	thread;
101 	sem_id		sem;
102 	time_t		totalTime;
103 } Tdebug_thead_param;
104 
105 // Bar layout depending on number of CPUs
106 // This is used only in case the replicant width is 16
107 
108 typedef struct {
109 	float	cpu_width;
110 	float	cpu_inter;
111 	float	mem_width;
112 } layoutT;
113 
114 layoutT layout[] = {
115 	{ 1, 1, 1 },
116 	{ 5, 1, 5 },	// 1
117 	{ 3, 1, 4 },	// 2
118 	{ 2, 1, 3 },
119 	{ 2, 0, 3 },	// 4
120 	{ 1, 1, 1 },
121 	{ 1, 1, 2 },
122 	{ 1, 1, 1 },
123 	{ 1, 0, 3 },	// 8
124 	{ 1, 1, 1 },
125 	{ 1, 0, 3 },
126 	{ 1, 0, 2 },
127 	{ 1, 0, 1 }		// 12
128 };
129 
130 
131 extern "C" _EXPORT BView* instantiate_deskbar_item(float maxWidth,
132 	float maxHeight);
133 
134 extern "C" _EXPORT BView*
135 instantiate_deskbar_item(float maxWidth, float maxHeight)
136 {
137 	gInDeskbar = true;
138 	return new ProcessController(maxHeight - 1, maxHeight - 1);
139 }
140 
141 
142 //	#pragma mark -
143 
144 
145 ProcessController::ProcessController(BRect frame, bool temp)
146 	: BView(frame, kDeskbarItemName, B_FOLLOW_TOP_BOTTOM,
147 		B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
148 	fProcessControllerIcon(kSignature),
149 	fProcessorIcon(k_cpu_mini),
150 	fTrackerIcon(kTrackerSig),
151 	fDeskbarIcon(kDeskbarSig),
152 	fTerminalIcon(kTerminalSig),
153 	kCPUCount(sysconf(_SC_NPROCESSORS_CONF)),
154 	fTemp(temp),
155 	fLastBarHeight(new float[kCPUCount]),
156 	fCPUTimes(new double[kCPUCount]),
157 	fPrevActive(new bigtime_t[kCPUCount])
158 {
159 	if (!temp) {
160 		Init();
161 
162 		frame.OffsetTo(B_ORIGIN);
163 		frame.top = frame.bottom - 7;
164 		frame.left = frame.right - 7;
165 		BDragger* dragger = new BDragger(frame, this, B_FOLLOW_BOTTOM);
166 		AddChild(dragger);
167 	}
168 }
169 
170 ProcessController::ProcessController(BMessage *data)
171 	: BView(data),
172 	fProcessControllerIcon(kSignature),
173 	fProcessorIcon(k_cpu_mini),
174 	fTrackerIcon(kTrackerSig),
175 	fDeskbarIcon(kDeskbarSig),
176 	fTerminalIcon(kTerminalSig),
177 	kCPUCount(sysconf(_SC_NPROCESSORS_CONF)),
178 	fTemp(false),
179 	fLastBarHeight(new float[kCPUCount]),
180 	fCPUTimes(new double[kCPUCount]),
181 	fPrevActive(new bigtime_t[kCPUCount])
182 {
183 	Init();
184 }
185 
186 
187 ProcessController::ProcessController(float width, float height)
188 	:
189 	BView(BRect (0, 0, width, height), kDeskbarItemName, B_FOLLOW_NONE,
190 		B_WILL_DRAW),
191 	fProcessControllerIcon(kSignature),
192 	fProcessorIcon(k_cpu_mini),
193 	fTrackerIcon(kTrackerSig),
194 	fDeskbarIcon(kDeskbarSig),
195 	fTerminalIcon(kTerminalSig),
196 	kCPUCount(sysconf(_SC_NPROCESSORS_CONF)),
197 	fTemp(false),
198 	fLastBarHeight(new float[kCPUCount]),
199 	fCPUTimes(new double[kCPUCount]),
200 	fPrevActive(new bigtime_t[kCPUCount])
201 {
202 	Init();
203 }
204 
205 
206 ProcessController::~ProcessController()
207 {
208 	if (!fTemp) {
209 		if (gPopupThreadID) {
210 			status_t return_value;
211 			wait_for_thread (gPopupThreadID, &return_value);
212 		}
213 	}
214 
215 	delete fMessageRunner;
216 	gPCView = NULL;
217 
218 	delete[] fPrevActive;
219 	delete[] fCPUTimes;
220 	delete[] fLastBarHeight;
221 }
222 
223 
224 void
225 ProcessController::Init()
226 {
227 	memset(fLastBarHeight, 0, sizeof(float) * kCPUCount);
228 	memset(fCPUTimes, 0, sizeof(double) * kCPUCount);
229 	memset(fPrevActive, 0, sizeof(bigtime_t) * kCPUCount);
230 
231 	gPCView = this;
232 	fMessageRunner = NULL;
233 	fLastMemoryHeight = 0;
234 	fPrevTime = 0;
235 }
236 
237 
238 void
239 ProcessController::_HandleDebugRequest(team_id team, thread_id thread)
240 {
241 	char paramString[16];
242 	char idString[16];
243 	strlcpy(paramString, thread > 0 ? "--thread" : "--team",
244 		sizeof(paramString));
245 	snprintf(idString, sizeof(idString), "%" B_PRId32,
246 		thread > 0 ? thread : team);
247 
248 	const char* argv[] = {paramString, idString, NULL};
249 	status_t error = be_roster->Launch(kDebuggerSignature, 2, argv);
250 	if (error != B_OK) {
251 		// TODO: notify user
252 	}
253 }
254 
255 
256 ProcessController*
257 ProcessController::Instantiate(BMessage *data)
258 {
259 	if (!validate_instantiation(data, kClassName))
260 		return NULL;
261 
262 	return new ProcessController(data);
263 }
264 
265 
266 status_t
267 ProcessController::Archive(BMessage *data, bool deep) const
268 {
269 	BView::Archive(data, deep);
270 	data->AddString("add_on", kSignature);
271 	data->AddString("class", kClassName);
272 	return B_OK;
273 }
274 
275 
276 void
277 ProcessController::MessageReceived(BMessage *message)
278 {
279 	team_id team;
280 	thread_id thread;
281 	BAlert *alert;
282 	char	question[1000];
283 	switch (message->what) {
284 		case 'Puls':
285 			Update ();
286 			DoDraw (false);
287 			break;
288 
289 		case 'QtTm':
290 			if (message->FindInt32("team", &team) == B_OK) {
291 				resume_thread(spawn_thread(thread_quit_application,
292 					B_TRANSLATE("Quit application"), B_NORMAL_PRIORITY,
293 					(void*)(addr_t)team));
294 			}
295 			break;
296 
297 		case 'KlTm':
298 			if (message->FindInt32("team", &team) == B_OK) {
299 				info_pack infos;
300 				if (get_team_info(team, &infos.team_info) == B_OK) {
301 					get_team_name_and_icon(infos);
302 					snprintf(question, sizeof(question),
303 					B_TRANSLATE("What do you want to do with the team \"%s\"?"),
304 					infos.team_name);
305 					alert = new BAlert(B_TRANSLATE("Please confirm"), question,
306 					B_TRANSLATE("Cancel"), B_TRANSLATE("Debug this team!"),
307 					B_TRANSLATE("Kill this team!"), B_WIDTH_AS_USUAL,
308 					B_STOP_ALERT);
309 					alert->SetShortcut(0, B_ESCAPE);
310 					int result = alert->Go();
311 					switch (result) {
312 						case 1:
313 							_HandleDebugRequest(team, -1);
314 							break;
315 						case 2:
316 							kill_team(team);
317 							break;
318 						default:
319 							break;
320 					}
321 				} else {
322 					alert = new BAlert(B_TRANSLATE("Info"),
323 						B_TRANSLATE("This team is already gone" B_UTF8_ELLIPSIS),
324 						B_TRANSLATE("Ok!"), NULL, NULL, B_WIDTH_AS_USUAL,
325 						B_STOP_ALERT);
326 					alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
327 					alert->Go();
328 				}
329 			}
330 			break;
331 
332 		case 'KlTh':
333 			if (message->FindInt32("thread", &thread) == B_OK) {
334 				thread_info	thinfo;
335 				if (get_thread_info(thread, &thinfo) == B_OK) {
336 					#if DEBUG_THREADS
337 					snprintf(question, sizeof(question),
338 						B_TRANSLATE("What do you want to do "
339 						"with the thread \"%s\"?"), thinfo.name);
340 					alert = new BAlert(B_TRANSLATE("Please confirm"), question,
341 						B_TRANSLATE("Cancel"), B_TRANSLATE("Debug this thread!"),
342 						B_TRANSLATE("Kill this thread!"), B_WIDTH_AS_USUAL,
343 						B_STOP_ALERT);
344 					alert->SetShortcut(0, B_ESCAPE);
345 
346 					#define KILL 2
347 					#else
348 					snprintf(question, sizeof(question),
349 						B_TRANSLATE("Are you sure you want "
350 						"to kill the thread \"%s\"?"), thinfo.name);
351 					alert = new BAlert(B_TRANSLATE("Please confirm"), question,
352 						B_TRANSLATE("Cancel"), B_TRANSLATE("Kill this thread!"),
353 						NULL, B_WIDTH_AS_USUAL,	B_STOP_ALERT);
354 					alert->SetShortcut(0, B_ESCAPE);
355 
356 					#define KILL 1
357 					#endif
358 					alert->SetShortcut(0, B_ESCAPE);
359 					int r = alert->Go();
360 					if (r == KILL)
361 						kill_thread(thread);
362 					#if DEBUG_THREADS
363 					else if (r == 1)
364 						_HandleDebugRequest(thinfo.team, thinfo.thread);
365 					#endif
366 				} else {
367 					alert = new BAlert(B_TRANSLATE("Info"),
368 						B_TRANSLATE("This thread is already gone" B_UTF8_ELLIPSIS),
369 						B_TRANSLATE("Ok!"),	NULL, NULL,
370 						B_WIDTH_AS_USUAL, B_STOP_ALERT);
371 					alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
372 					alert->Go();
373 				}
374 			}
375 			break;
376 
377 		case 'PrTh':
378 			if (message->FindInt32("thread", &thread) == B_OK) {
379 				int32 new_priority;
380 				if (message->FindInt32("priority", &new_priority) == B_OK)
381 					set_thread_priority(thread, new_priority);
382 			}
383 			break;
384 
385 		case 'Trac':
386 		{
387 			BPath trackerPath;
388 			if (find_directory(B_SYSTEM_DIRECTORY, &trackerPath) == B_OK
389 				&& trackerPath.Append("Tracker") == B_OK) {
390 				launch(kTrackerSig, trackerPath.Path());
391 			}
392 			break;
393 		}
394 
395 		case 'Dbar':
396 		{
397 			BPath deskbarPath;
398 			if (find_directory(B_SYSTEM_DIRECTORY, &deskbarPath) == B_OK
399 				&& deskbarPath.Append("Deskbar") == B_OK) {
400 				launch(kDeskbarSig, deskbarPath.Path());
401 			}
402 			break;
403 		}
404 
405 		case 'Term':
406 		{
407 			BPath terminalPath;
408 			if (find_directory(B_SYSTEM_APPS_DIRECTORY, &terminalPath) == B_OK
409 				&& terminalPath.Append("Terminal") == B_OK) {
410 				launch(kTerminalSig, terminalPath.Path());
411 			}
412 			break;
413 		}
414 
415 		case 'AlDb':
416 		{
417 			if (!be_roster->IsRunning(kDeskbarSig)) {
418 				BPath deskbarPath;
419 				if (find_directory(B_SYSTEM_DIRECTORY, &deskbarPath) == B_OK
420 					&& deskbarPath.Append("Deskbar") == B_OK) {
421 					launch(kDeskbarSig, deskbarPath.Path());
422 				}
423 			}
424 			BDeskbar deskbar;
425 			if (gInDeskbar || deskbar.HasItem (kDeskbarItemName))
426 				deskbar.RemoveItem (kDeskbarItemName);
427 			else
428 				move_to_deskbar(deskbar);
429 			break;
430 		}
431 
432 		case 'CPU ':
433 		{
434 			uint32 cpu;
435 			if (message->FindInt32("cpu", (int32*)&cpu) == B_OK) {
436 				bool last = true;
437 				for (unsigned int p = 0; p < gCPUcount; p++) {
438 					if (p != cpu && _kern_cpu_enabled(p)) {
439 						last = false;
440 						break;
441 					}
442 				}
443 				if (last) {
444 					alert = new BAlert(B_TRANSLATE("Info"),
445 						B_TRANSLATE("This is the last active processor"
446 						B_UTF8_ELLIPSIS "\n"
447 						"You can't turn it off!"),
448 						B_TRANSLATE("That's no Fun!"), NULL, NULL,
449 						B_WIDTH_AS_USUAL, B_WARNING_ALERT);
450 					alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
451 					alert->Go();
452 				} else
453 					_kern_set_cpu_enabled(cpu, !_kern_cpu_enabled(cpu));
454 			}
455 			break;
456 		}
457 
458 		case 'Schd':
459 		{
460 			BMenuItem* source;
461 			if (message->FindPointer("source", (void**)&source) != B_OK)
462 				break;
463 			if (!source->IsMarked())
464 				set_scheduler_mode(SCHEDULER_MODE_POWER_SAVING);
465 			else
466 				set_scheduler_mode(SCHEDULER_MODE_LOW_LATENCY);
467 			break;
468 		}
469 
470 		case B_ABOUT_REQUESTED:
471 			AboutRequested();
472 			break;
473 
474 		default:
475 			BView::MessageReceived(message);
476 	}
477 }
478 
479 
480 void
481 ProcessController::AboutRequested()
482 {
483 	BAboutWindow* window = new BAboutWindow(
484 		B_TRANSLATE_SYSTEM_NAME("ProcessController"), kSignature);
485 
486 	const char* extraCopyrights[] = {
487 		"2004 beunited.org",
488 		"1997-2001 Georges-Edouard Berenger",
489 		NULL
490 	};
491 
492 	const char* authors[] = {
493 		"Georges-Edouard Berenger",
494 		NULL
495 	};
496 
497 	window->AddCopyright(2007, "Haiku, Inc.", extraCopyrights);
498 	window->AddAuthors(authors);
499 
500 	window->Show();
501 }
502 
503 
504 void
505 ProcessController::DefaultColors()
506 {
507 	swap_color.red = 203;
508 	swap_color.green = 0;
509 	swap_color.blue = 0;
510 	swap_color.alpha = 255;
511 	bool set = false;
512 
513 	if (!set) {
514 		active_color = kKernelBlue;
515 		active_color = tint_color (active_color, B_LIGHTEN_2_TINT);
516 		idle_color = active_color;
517 		idle_color.green /= 3;
518 		idle_color.red /= 3;
519 		idle_color.blue /= 3;
520 		frame_color = kBlack;
521 		mix_colors (memory_color, active_color, swap_color, 0.2);
522 	}
523 }
524 
525 
526 void
527 ProcessController::AttachedToWindow()
528 {
529 	BView::AttachedToWindow();
530 	if (Parent())
531 		SetViewColor(B_TRANSPARENT_COLOR);
532 	else
533 		SetViewColor(kBlack);
534 
535 	Preferences tPreferences(kPreferencesFileName, NULL, false);
536 	DefaultColors();
537 
538 	system_info info;
539 	get_system_info(&info);
540 	gCPUcount = info.cpu_count;
541 	Update();
542 
543 	gIdleColor = kIdleGreen;
544 	gIdleColorSelected = tint_color(gIdleColor, B_HIGHLIGHT_BACKGROUND_TINT);
545 	gKernelColor = kKernelBlue;
546 	gKernelColorSelected = tint_color(gKernelColor, B_HIGHLIGHT_BACKGROUND_TINT);
547 	gUserColor = tint_color(gKernelColor, B_LIGHTEN_2_TINT);
548 	gUserColorSelected = tint_color(gUserColor, B_HIGHLIGHT_BACKGROUND_TINT);
549 	gFrameColor = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
550 		B_HIGHLIGHT_BACKGROUND_TINT);
551 	gFrameColorSelected = tint_color(gFrameColor, B_HIGHLIGHT_BACKGROUND_TINT);
552 	gMenuBackColor = ui_color(B_MENU_BACKGROUND_COLOR);
553 	gMenuBackColorSelected = ui_color(B_MENU_SELECTION_BACKGROUND_COLOR);
554 	gWhiteSelected = tint_color(kWhite, B_HIGHLIGHT_BACKGROUND_TINT);
555 
556 	BMessenger messenger(this);
557 	BMessage message('Puls');
558 	fMessageRunner = new BMessageRunner(messenger, &message, 250000, -1);
559 }
560 
561 
562 
563 void
564 ProcessController::MouseDown(BPoint where)
565 {
566 	if (atomic_add(&gPopupFlag, 1) > 0) {
567 		atomic_add(&gPopupFlag, -1);
568 		return;
569 	}
570 
571 	Tpopup_param* param = new Tpopup_param;
572 	ConvertToScreen(&where);
573 	param->where = where;
574 	param->clickToOpenRect = Frame ();
575 	ConvertToScreen (&param->clickToOpenRect);
576 	param->top = where.y < BScreen(this->Window()).Frame().bottom-50;
577 
578 	gPopupThreadID = spawn_thread(thread_popup, "Popup holder thread",
579 		B_URGENT_DISPLAY_PRIORITY, param);
580 	resume_thread(gPopupThreadID);
581 }
582 
583 
584 void
585 ProcessController::Draw(BRect)
586 {
587 	SetDrawingMode(B_OP_COPY);
588 	DoDraw(true);
589 }
590 
591 
592 void
593 ProcessController::DoDraw(bool force)
594 {
595 	BRect bounds(Bounds());
596 
597 	float h = floorf(bounds.Height ()) - 2;
598 	float top = 1, left = 1;
599 	float bottom = top + h;
600 	float barWidth;
601 	float barGap;
602 	float memWidth;
603 	if (gCPUcount <= 12 && bounds.Width() == 15) {
604 		// Use fixed sizes for small icon sizes
605 		barWidth = layout[gCPUcount].cpu_width;
606 		barGap = layout[gCPUcount].cpu_inter;
607 		memWidth = layout[gCPUcount].mem_width;
608 	} else {
609 		memWidth = floorf((bounds.Height() + 1) / 8);
610 		barGap = ((bounds.Width() + 1) / gCPUcount) > 3 ? 1 : 0;
611 		barWidth = floorf((bounds.Width() - 1 - memWidth - barGap * gCPUcount)
612 			/ gCPUcount);
613 	}
614 	// interspace
615 	float right = left + gCPUcount * (barWidth + barGap) - barGap;
616 	float leftMem = bounds.Width() - memWidth;
617 		// right of CPU frame...
618 	if (force && Parent()) {
619 		SetHighColor(Parent()->ViewColor());
620 		FillRect(BRect(right + 1, top - 1, leftMem, bottom + 1));
621 	}
622 
623 	if (force) {
624 		SetHighColor(frame_color);
625 		StrokeRect(BRect(left - 1, top - 1, right, bottom + 1));
626 		if (gCPUcount > 1 && barGap == 1) {
627 			for (unsigned int x = 1; x < gCPUcount; x++)
628 				StrokeLine(BPoint(left + x * barWidth + x - 1, top),
629 					BPoint(left + x * barWidth + x - 1, bottom));
630 		}
631 	}
632 
633 	if (force)
634 		StrokeRect(BRect(leftMem - 1, top - 1, leftMem + memWidth, bottom + 1));
635 
636 	for (unsigned int x = 0; x < gCPUcount; x++) {
637 		right = left + barWidth - 1;
638 		float rem = fCPUTimes[x] * (h + 1);
639 		float barHeight = floorf (rem);
640 		rem -= barHeight;
641 		float limit = bottom - barHeight;	// horizontal line
642 		float previousLimit = bottom - fLastBarHeight[x];
643 		float idleTop = top;
644 
645 		if (!force && previousLimit > top)
646 			idleTop = previousLimit - 1;
647 		if (limit > idleTop) {
648 			SetHighColor(idle_color);
649 			FillRect(BRect(left, idleTop, right, limit - 1));
650 		}
651 		if (barHeight <= h) {
652 			rgb_color fraction_color;
653 			mix_colors(fraction_color, idle_color, active_color, rem);
654 			SetHighColor(fraction_color);
655 			StrokeLine(BPoint(left, bottom - barHeight), BPoint(right,
656 				bottom - barHeight));
657 		}
658 		float active_bottom = bottom;
659 		if (!force && previousLimit < bottom)
660 			active_bottom = previousLimit + 1;
661 		if (limit < active_bottom) {
662 			SetHighColor(active_color);
663 			FillRect(BRect(left, limit + 1, right, active_bottom));
664 		}
665 		left += barWidth + barGap;
666 		fLastBarHeight[x] = barHeight;
667 	}
668 
669 	float rightMem = bounds.Width() - 1;
670 	float rem = fMemoryUsage * (h + 1);
671 	float barHeight = floorf(rem);
672 	rem -= barHeight;
673 
674 	rgb_color used_memory_color;
675 	float sq = fMemoryUsage * fMemoryUsage;
676 	sq *= sq;
677 	sq *= sq;
678 	mix_colors(used_memory_color, memory_color, swap_color, sq);
679 
680 	float limit = bottom - barHeight;	// horizontal line
681 	float previousLimit = bottom - fLastMemoryHeight;
682 	float free_top = top;
683 	if (!force && previousLimit > top)
684 		free_top = previousLimit - 1;
685 	if (limit > free_top) {
686 		SetHighColor (idle_color);
687 		FillRect(BRect(leftMem, free_top, rightMem, limit - 1));
688 	}
689 	if (barHeight <= h) {
690 		rgb_color fraction_color;
691 		mix_colors(fraction_color, idle_color, used_memory_color, rem);
692 		SetHighColor(fraction_color);
693 		StrokeLine(BPoint(leftMem, bottom - barHeight), BPoint(rightMem,
694 			bottom - barHeight));
695 	}
696 	float usedBottom = bottom;
697 //	if (!force && previousLimit < bottom)
698 //		usedBottom = previousLimit + 1;
699 	if (limit < usedBottom) {
700 		SetHighColor(used_memory_color);
701 		FillRect(BRect(leftMem, limit + 1, rightMem, usedBottom));
702 	}
703 	fLastMemoryHeight = barHeight;
704 }
705 
706 
707 void
708 ProcessController::Update()
709 {
710 	system_info info;
711 	get_system_info(&info);
712 	bigtime_t now = system_time();
713 
714 	cpu_info* cpuInfos = new cpu_info[gCPUcount];
715 	get_cpu_info(0, gCPUcount, cpuInfos);
716 
717 	fMemoryUsage = float(info.used_pages) / float(info.max_pages);
718 	// Calculate work done since last call to Update() for each CPU
719 	for (unsigned int x = 0; x < gCPUcount; x++) {
720 		bigtime_t load = cpuInfos[x].active_time - fPrevActive[x];
721 		bigtime_t passed = now - fPrevTime;
722 		float cpuTime = float(load) / float(passed);
723 
724 		fPrevActive[x] = cpuInfos[x].active_time;
725 		if (load > passed)
726 			fPrevActive[x] -= load - passed; // save overload for next period...
727 		if (cpuTime < 0)
728 			cpuTime = 0;
729 		if (cpuTime > 1)
730 			cpuTime = 1;
731 		fCPUTimes[x] = cpuTime;
732 	}
733 	fPrevTime = now;
734 
735 	delete[] cpuInfos;
736 }
737 
738 
739 //	#pragma mark -
740 
741 
742 status_t
743 thread_popup(void *arg)
744 {
745 	Tpopup_param* param = (Tpopup_param*) arg;
746 	int32 mcookie, hcookie;
747 	unsigned long m;
748 	long h;
749 	BMenuItem* item;
750 	bool top = param->top;
751 
752 	system_info systemInfo;
753 	get_system_info(&systemInfo);
754 	info_pack* infos = new info_pack[systemInfo.used_teams];
755 	// TODO: this doesn't necessarily get all teams
756 	for (m = 0, mcookie = 0; m < systemInfo.used_teams; m++) {
757 		infos[m].team_icon = NULL;
758 		infos[m].team_name[0] = 0;
759 		infos[m].thread_info = NULL;
760 		if (get_next_team_info(&mcookie, &infos[m].team_info) == B_OK) {
761 			infos[m].thread_info = new thread_info[infos[m].team_info.thread_count];
762 			for (h = 0, hcookie = 0; h < infos[m].team_info.thread_count; h++) {
763 				if (get_next_thread_info(infos[m].team_info.team, &hcookie,
764 						&infos[m].thread_info[h]) != B_OK)
765 					infos[m].thread_info[h].thread = -1;
766 			}
767 			get_team_name_and_icon(infos[m], true);
768 		} else {
769 			systemInfo.used_teams = m;
770 			infos[m].team_info.team = -1;
771 		}
772 	}
773 
774 	BPopUpMenu* popup = new BPopUpMenu("Global Popup", false, false);
775 	popup->SetFont(be_plain_font);
776 
777 	// Quit section
778 	BMenu* QuitPopup = new QuitMenu(B_TRANSLATE("Quit an application"),
779 	infos, systemInfo.used_teams);
780 	QuitPopup->SetFont(be_plain_font);
781 	popup->AddItem(QuitPopup);
782 
783 	// Memory Usage section
784 	MemoryBarMenu* MemoryPopup = new MemoryBarMenu(B_TRANSLATE("Memory usage"),
785 	infos, systemInfo);
786 	int64 committedMemory = (int64)systemInfo.used_pages * B_PAGE_SIZE / 1024;
787 	for (m = 0; m < systemInfo.used_teams; m++) {
788 		if (infos[m].team_info.team >= 0) {
789 			MemoryBarMenuItem* memoryItem =
790 				new MemoryBarMenuItem(infos[m].team_name,
791 					infos[m].team_info.team, infos[m].team_icon, false, NULL);
792 			MemoryPopup->AddItem(memoryItem);
793 			memoryItem->UpdateSituation(committedMemory);
794 		}
795 	}
796 
797 	addtopbottom(MemoryPopup);
798 
799 	// CPU Load section
800 	TeamBarMenu* CPUPopup = new TeamBarMenu(B_TRANSLATE("Threads and CPU "
801 	"usage"), infos, systemInfo.used_teams);
802 	for (m = 0; m < systemInfo.used_teams; m++) {
803 		if (infos[m].team_info.team >= 0) {
804 			ThreadBarMenu* TeamPopup = new ThreadBarMenu(infos[m].team_name,
805 				infos[m].team_info.team, infos[m].team_info.thread_count);
806 			BMessage* kill_team = new BMessage('KlTm');
807 			kill_team->AddInt32("team", infos[m].team_info.team);
808 			TeamBarMenuItem* item = new TeamBarMenuItem(TeamPopup, kill_team,
809 				infos[m].team_info.team, infos[m].team_icon, false);
810 			item->SetTarget(gPCView);
811 			CPUPopup->AddItem(item);
812 		}
813 	}
814 
815 	addtopbottom(CPUPopup);
816 	addtopbottom(new BSeparatorItem());
817 
818 	// CPU on/off section
819 	if (gCPUcount > 1) {
820 		for (unsigned int i = 0; i < gCPUcount; i++) {
821 			char item_name[32];
822 			sprintf (item_name, B_TRANSLATE("Processor %d"), i + 1);
823 			BMessage* m = new BMessage ('CPU ');
824 			m->AddInt32 ("cpu", i);
825 			item = new IconMenuItem (gPCView->fProcessorIcon, item_name, m);
826 			if (_kern_cpu_enabled(i))
827 				item->SetMarked (true);
828 			item->SetTarget(gPCView);
829 			addtopbottom(item);
830 		}
831 		addtopbottom (new BSeparatorItem ());
832 	}
833 
834 	// Scheduler modes
835 	int32 currentMode = get_scheduler_mode();
836 	BMessage* msg = new BMessage('Schd');
837 	item = new BMenuItem(B_TRANSLATE("Power saving"), msg);
838 	if ((uint32)currentMode == SCHEDULER_MODE_POWER_SAVING)
839 		item->SetMarked(true);
840 	item->SetTarget(gPCView);
841 	addtopbottom(item);
842 	addtopbottom(new BSeparatorItem());
843 
844 	if (!be_roster->IsRunning(kTrackerSig)) {
845 		item = new IconMenuItem(gPCView->fTrackerIcon,
846 		B_TRANSLATE("Restart Tracker"), new BMessage('Trac'));
847 		item->SetTarget(gPCView);
848 		addtopbottom(item);
849 	}
850 	if (!be_roster->IsRunning(kDeskbarSig)) {
851 		item = new IconMenuItem(gPCView->fDeskbarIcon,
852 		B_TRANSLATE("Restart Deskbar"), new BMessage('Dbar'));
853 		item->SetTarget(gPCView);
854 		addtopbottom(item);
855 	}
856 
857 	item = new IconMenuItem(gPCView->fTerminalIcon,
858 	B_TRANSLATE("New Terminal"), new BMessage('Term'));
859 	item->SetTarget(gPCView);
860 	addtopbottom(item);
861 
862 	addtopbottom(new BSeparatorItem());
863 
864 	bool showLiveInDeskbarItem = gInDeskbar;
865 	if (!showLiveInDeskbarItem) {
866 		int32 cookie = 0;
867 		image_info info;
868 		while (get_next_image_info(B_CURRENT_TEAM, &cookie, &info) == B_OK) {
869 			if (info.type == B_APP_IMAGE) {
870 				// only show the Live in Deskbar item if a) we're running in
871 				// deskbar itself, or b) we're running in PC's team.
872 				if (strstr(info.name, "ProcessController") != NULL) {
873 					showLiveInDeskbarItem = true;
874 					break;
875 				}
876 			}
877 		}
878 	}
879 
880 	if (showLiveInDeskbarItem && be_roster->IsRunning(kDeskbarSig)) {
881 		item = new BMenuItem(B_TRANSLATE("Live in the Deskbar"),
882 			new BMessage('AlDb'));
883 		BDeskbar deskbar;
884 		item->SetMarked(gInDeskbar || deskbar.HasItem(kDeskbarItemName));
885 		item->SetTarget(gPCView);
886 		addtopbottom(item);
887 		addtopbottom(new BSeparatorItem ());
888 	}
889 
890 
891 	item = new IconMenuItem(gPCView->fProcessControllerIcon,
892 	B_TRANSLATE("About ProcessController" B_UTF8_ELLIPSIS),
893 		new BMessage(B_ABOUT_REQUESTED));
894 	item->SetTarget(gPCView);
895 	addtopbottom(item);
896 
897 	param->where.x -= 5;
898 	param->where.y -= 8;
899 	popup->Go(param->where, true, true, param->clickToOpenRect);
900 
901 	delete popup;
902 	for (m = 0; m < systemInfo.used_teams; m++) {
903 		if (infos[m].team_info.team >= 0) {
904 			delete[] infos[m].thread_info;
905 			delete infos[m].team_icon;
906 		}
907 	}
908 	delete[] infos;
909 	delete param;
910 	atomic_add (&gPopupFlag, -1);
911 	gPopupThreadID = 0;
912 
913 	return B_OK;
914 }
915 
916 
917 status_t
918 thread_quit_application(void *arg)
919 {
920 	BMessenger messenger(NULL, (addr_t)arg);
921 	messenger.SendMessage(B_QUIT_REQUESTED);
922 	return B_OK;
923 }
924 
925 
926 status_t
927 thread_debug_thread(void *arg)
928 {
929 	Tdebug_thead_param*	param = (Tdebug_thead_param*) arg;
930 	debug_thread(param->thread);
931 	delete param;
932 	return B_OK;
933 }
934