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