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