xref: /haiku/src/apps/processcontroller/ProcessController.cpp (revision 8e8f7748d39f8407894a5ab79f7b4f93bc4f4652)
1 /*
2 	ProcessController © 2000, Georges-Edouard Berenger, All Rights Reserved.
3 	Copyright (C) 2004 beunited.org
4 	Copyright (c) 2006-2013, 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 *argv[2];
248 	char paramString[16];
249 	char idString[16];
250 	strlcpy(paramString, thread > 0 ? "--thread" : "--team", sizeof(paramString));
251 	snprintf(idString, sizeof(idString), "%" B_PRId32, thread > 0 ? thread : team);
252 	argv[0] = paramString;
253 	argv[1] = idString;
254 	status_t error = be_roster->Launch(kDebuggerSignature, 2, argv);
255 	if (error != B_OK) {
256 		// TODO: notify user
257 	}
258 }
259 
260 
261 ProcessController*
262 ProcessController::Instantiate(BMessage *data)
263 {
264 	if (!validate_instantiation(data, kClassName))
265 		return NULL;
266 
267 	return new ProcessController(data);
268 }
269 
270 
271 status_t
272 ProcessController::Archive(BMessage *data, bool deep) const
273 {
274 	BView::Archive(data, deep);
275 	data->AddString("add_on", kSignature);
276 	data->AddString("class", kClassName);
277 	return B_OK;
278 }
279 
280 
281 void
282 ProcessController::MessageReceived(BMessage *message)
283 {
284 	team_id team;
285 	thread_id thread;
286 	BAlert *alert;
287 	char	question[1000];
288 	switch (message->what) {
289 		case 'Puls':
290 			Update ();
291 			DoDraw (false);
292 			break;
293 
294 		case 'QtTm':
295 			if (message->FindInt32("team", &team) == B_OK) {
296 				resume_thread(spawn_thread(thread_quit_application,
297 					B_TRANSLATE("Quit application"), B_NORMAL_PRIORITY,
298 					(void*)(addr_t)team));
299 			}
300 			break;
301 
302 		case 'KlTm':
303 			if (message->FindInt32("team", &team) == B_OK) {
304 				info_pack infos;
305 				if (get_team_info(team, &infos.team_info) == B_OK) {
306 					get_team_name_and_icon(infos);
307 					snprintf(question, sizeof(question),
308 					B_TRANSLATE("What do you want to do with the team \"%s\"?"),
309 					infos.team_name);
310 					alert = new BAlert(B_TRANSLATE("Please confirm"), question,
311 					B_TRANSLATE("Cancel"), B_TRANSLATE("Debug this team!"),
312 					B_TRANSLATE("Kill this team!"), B_WIDTH_AS_USUAL,
313 					B_STOP_ALERT);
314 					alert->SetShortcut(0, B_ESCAPE);
315 					int result = alert->Go();
316 					switch (result) {
317 						case 1:
318 							_HandleDebugRequest(team, -1);
319 							break;
320 						case 2:
321 							kill_team(team);
322 							break;
323 						default:
324 							break;
325 					}
326 				} else {
327 					alert = new BAlert(B_TRANSLATE("Info"),
328 						B_TRANSLATE("This team is already gone"B_UTF8_ELLIPSIS),
329 						B_TRANSLATE("Ok!"), NULL, NULL, B_WIDTH_AS_USUAL,
330 						B_STOP_ALERT);
331 					alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
332 					alert->Go();
333 				}
334 			}
335 			break;
336 
337 		case 'KlTh':
338 			if (message->FindInt32("thread", &thread) == B_OK) {
339 				thread_info	thinfo;
340 				if (get_thread_info(thread, &thinfo) == B_OK) {
341 					#if DEBUG_THREADS
342 					snprintf(question, sizeof(question),
343 						B_TRANSLATE("What do you want to do "
344 						"with the thread \"%s\"?"), thinfo.name);
345 					alert = new BAlert(B_TRANSLATE("Please confirm"), question,
346 						B_TRANSLATE("Cancel"), B_TRANSLATE("Debug this thread!"),
347 						B_TRANSLATE("Kill this thread!"), B_WIDTH_AS_USUAL,
348 						B_STOP_ALERT);
349 					alert->SetShortcut(0, B_ESCAPE);
350 
351 					#define KILL 2
352 					#else
353 					snprintf(question, sizeof(question),
354 						B_TRANSLATE("Are you sure you want "
355 						"to kill the thread \"%s\"?"), thinfo.name);
356 					alert = new BAlert(B_TRANSLATE("Please confirm"), question,
357 						B_TRANSLATE("Cancel"), B_TRANSLATE("Kill this thread!"),
358 						NULL, B_WIDTH_AS_USUAL,	B_STOP_ALERT);
359 					alert->SetShortcut(0, B_ESCAPE);
360 
361 					#define KILL 1
362 					#endif
363 					alert->SetShortcut(0, B_ESCAPE);
364 					int r = alert->Go();
365 					if (r == KILL)
366 						kill_thread(thread);
367 					#if DEBUG_THREADS
368 					else if (r == 1)
369 						_HandleDebugRequest(thinfo.team, thinfo.thread);
370 					#endif
371 				} else {
372 					alert = new BAlert(B_TRANSLATE("Info"),
373 						B_TRANSLATE("This thread is already gone"B_UTF8_ELLIPSIS),
374 						B_TRANSLATE("Ok!"),	NULL, NULL,
375 						B_WIDTH_AS_USUAL, B_STOP_ALERT);
376 					alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
377 					alert->Go();
378 				}
379 			}
380 			break;
381 
382 		case 'PrTh':
383 			if (message->FindInt32("thread", &thread) == B_OK) {
384 				int32 new_priority;
385 				if (message->FindInt32("priority", &new_priority) == B_OK)
386 					set_thread_priority(thread, new_priority);
387 			}
388 			break;
389 
390 		case 'Trac':
391 		{
392 			BPath trackerPath;
393 			if (find_directory(B_SYSTEM_DIRECTORY, &trackerPath) == B_OK
394 				&& trackerPath.Append("Tracker") == B_OK) {
395 				launch(kTrackerSig, trackerPath.Path());
396 			}
397 			break;
398 		}
399 
400 		case 'Dbar':
401 		{
402 			BPath deskbarPath;
403 			if (find_directory(B_SYSTEM_DIRECTORY, &deskbarPath) == B_OK
404 				&& deskbarPath.Append("Deskbar") == B_OK) {
405 				launch(kDeskbarSig, deskbarPath.Path());
406 			}
407 			break;
408 		}
409 
410 		case 'Term':
411 		{
412 			BPath terminalPath;
413 			if (find_directory(B_SYSTEM_APPS_DIRECTORY, &terminalPath) == B_OK
414 				&& terminalPath.Append("Terminal") == B_OK) {
415 				launch(kTerminalSig, terminalPath.Path());
416 			}
417 			break;
418 		}
419 
420 		case 'AlDb':
421 		{
422 			if (!be_roster->IsRunning(kDeskbarSig)) {
423 				BPath deskbarPath;
424 				if (find_directory(B_SYSTEM_DIRECTORY, &deskbarPath) == B_OK
425 					&& deskbarPath.Append("Deskbar") == B_OK) {
426 					launch(kDeskbarSig, deskbarPath.Path());
427 				}
428 			}
429 			BDeskbar deskbar;
430 			if (gInDeskbar || deskbar.HasItem (kDeskbarItemName))
431 				deskbar.RemoveItem (kDeskbarItemName);
432 			else
433 				move_to_deskbar(deskbar);
434 			break;
435 		}
436 
437 		case 'CPU ':
438 		{
439 			uint32 cpu;
440 			if (message->FindInt32("cpu", (int32*)&cpu) == B_OK) {
441 				bool last = true;
442 				for (unsigned int p = 0; p < gCPUcount; p++) {
443 					if (p != cpu && _kern_cpu_enabled(p)) {
444 						last = false;
445 						break;
446 					}
447 				}
448 				if (last) {
449 					alert = new BAlert(B_TRANSLATE("Info"),
450 						B_TRANSLATE("This is the last active processor...\n"
451 						"You can't turn it off!"),
452 						B_TRANSLATE("That's no Fun!"), NULL, NULL,
453 						B_WIDTH_AS_USUAL, B_WARNING_ALERT);
454 					alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
455 					alert->Go();
456 				} else
457 					_kern_set_cpu_enabled(cpu, !_kern_cpu_enabled(cpu));
458 			}
459 			break;
460 		}
461 
462 		case 'Schd':
463 		{
464 			int32 mode;
465 			if (message->FindInt32 ("mode", &mode) == B_OK)
466 				set_scheduler_mode(mode);
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 = layout[gCPUcount].cpu_width;
601 	// interspace
602 	float right = left + gCPUcount * (barWidth + layout[gCPUcount].cpu_inter)
603 		- layout[gCPUcount].cpu_inter; // right of CPU frame...
604 	if (force && Parent()) {
605 		SetHighColor(Parent()->ViewColor());
606 		FillRect(BRect(right + 1, top - 1, right + 2, bottom + 1));
607 	}
608 
609 	if (force) {
610 		SetHighColor(frame_color);
611 		StrokeRect(BRect(left - 1, top - 1, right, bottom + 1));
612 		if (gCPUcount > 1 && layout[gCPUcount].cpu_inter == 1) {
613 			for (unsigned int x = 1; x < gCPUcount; x++)
614 				StrokeLine(BPoint(left + x * barWidth + x - 1, top),
615 					BPoint(left + x * barWidth + x - 1, bottom));
616 		}
617 	}
618 	float leftMem = bounds.Width() - layout[gCPUcount].mem_width;
619 	if (force)
620 		StrokeRect(BRect(leftMem - 1, top - 1,
621 			leftMem + layout[gCPUcount].mem_width, bottom + 1));
622 
623 	for (unsigned int x = 0; x < gCPUcount; x++) {
624 		right = left + barWidth - 1;
625 		float rem = fCPUTimes[x] * (h + 1);
626 		float barHeight = floorf (rem);
627 		rem -= barHeight;
628 		float limit = bottom - barHeight;	// horizontal line
629 		float previousLimit = bottom - fLastBarHeight[x];
630 		float idleTop = top;
631 
632 		if (!force && previousLimit > top)
633 			idleTop = previousLimit - 1;
634 		if (limit > idleTop) {
635 			SetHighColor(idle_color);
636 			FillRect(BRect(left, idleTop, right, limit - 1));
637 		}
638 		if (barHeight <= h) {
639 			rgb_color fraction_color;
640 			mix_colors(fraction_color, idle_color, active_color, rem);
641 			SetHighColor(fraction_color);
642 			StrokeLine(BPoint(left, bottom - barHeight), BPoint(right,
643 				bottom - barHeight));
644 		}
645 		float active_bottom = bottom;
646 		if (!force && previousLimit < bottom)
647 			active_bottom = previousLimit + 1;
648 		if (limit < active_bottom) {
649 			SetHighColor(active_color);
650 			FillRect(BRect(left, limit + 1, right, active_bottom));
651 		}
652 		left += layout[gCPUcount].cpu_width + layout[gCPUcount].cpu_inter;
653 		fLastBarHeight[x] = barHeight;
654 	}
655 
656 	float rightMem = bounds.Width() - 1;
657 	float rem = fMemoryUsage * (h + 1);
658 	float barHeight = floorf(rem);
659 	rem -= barHeight;
660 
661 	rgb_color used_memory_color;
662 	float sq = fMemoryUsage * fMemoryUsage;
663 	sq *= sq;
664 	sq *= sq;
665 	mix_colors(used_memory_color, memory_color, swap_color, sq);
666 
667 	float limit = bottom - barHeight;	// horizontal line
668 	float previousLimit = bottom - fLastMemoryHeight;
669 	float free_top = top;
670 	if (!force && previousLimit > top)
671 		free_top = previousLimit - 1;
672 	if (limit > free_top) {
673 		SetHighColor (idle_color);
674 		FillRect(BRect(leftMem, free_top, rightMem, limit - 1));
675 	}
676 	if (barHeight <= h) {
677 		rgb_color fraction_color;
678 		mix_colors(fraction_color, idle_color, used_memory_color, rem);
679 		SetHighColor(fraction_color);
680 		StrokeLine(BPoint(leftMem, bottom - barHeight), BPoint(rightMem,
681 			bottom - barHeight));
682 	}
683 	float usedBottom = bottom;
684 //	if (!force && previousLimit < bottom)
685 //		usedBottom = previousLimit + 1;
686 	if (limit < usedBottom) {
687 		SetHighColor(used_memory_color);
688 		FillRect(BRect(leftMem, limit + 1, rightMem, usedBottom));
689 	}
690 	fLastMemoryHeight = barHeight;
691 }
692 
693 
694 void
695 ProcessController::Update()
696 {
697 	system_info info;
698 	get_system_info(&info);
699 	bigtime_t now = system_time();
700 
701 	cpu_info* cpuInfos = new cpu_info[gCPUcount];
702 	get_cpu_info(0, gCPUcount, cpuInfos);
703 
704 	fMemoryUsage = float(info.used_pages) / float(info.max_pages);
705 	// Calculate work done since last call to Update() for each CPU
706 	for (unsigned int x = 0; x < gCPUcount; x++) {
707 		bigtime_t load = cpuInfos[x].active_time - fPrevActive[x];
708 		bigtime_t passed = now - fPrevTime;
709 		float cpuTime = float(load) / float(passed);
710 
711 		fPrevActive[x] = cpuInfos[x].active_time;
712 		if (load > passed)
713 			fPrevActive[x] -= load - passed; // save overload for next period...
714 		if (cpuTime < 0)
715 			cpuTime = 0;
716 		if (cpuTime > 1)
717 			cpuTime = 1;
718 		fCPUTimes[x] = cpuTime;
719 	}
720 	fPrevTime = now;
721 
722 	delete[] cpuInfos;
723 }
724 
725 
726 //	#pragma mark -
727 
728 
729 status_t
730 thread_popup(void *arg)
731 {
732 	Tpopup_param* param = (Tpopup_param*) arg;
733 	int32 mcookie, hcookie;
734 	unsigned long m;
735 	long h;
736 	BMenuItem* item;
737 	bool top = param->top;
738 
739 	system_info systemInfo;
740 	get_system_info(&systemInfo);
741 	info_pack* infos = new info_pack[systemInfo.used_teams];
742 	// TODO: this doesn't necessarily get all teams
743 	for (m = 0, mcookie = 0; m < systemInfo.used_teams; m++) {
744 		infos[m].team_icon = NULL;
745 		infos[m].team_name[0] = 0;
746 		infos[m].thread_info = NULL;
747 		if (get_next_team_info(&mcookie, &infos[m].team_info) == B_OK) {
748 			infos[m].thread_info = new thread_info[infos[m].team_info.thread_count];
749 			for (h = 0, hcookie = 0; h < infos[m].team_info.thread_count; h++) {
750 				if (get_next_thread_info(infos[m].team_info.team, &hcookie,
751 						&infos[m].thread_info[h]) != B_OK)
752 					infos[m].thread_info[h].thread = -1;
753 			}
754 			get_team_name_and_icon(infos[m], true);
755 		} else {
756 			systemInfo.used_teams = m;
757 			infos[m].team_info.team = -1;
758 		}
759 	}
760 
761 	BPopUpMenu* popup = new BPopUpMenu("Global Popup", false, false);
762 	popup->SetFont(be_plain_font);
763 
764 	// Quit section
765 	BMenu* QuitPopup = new QuitMenu(B_TRANSLATE("Quit an application"),
766 	infos, systemInfo.used_teams);
767 	QuitPopup->SetFont(be_plain_font);
768 	popup->AddItem(QuitPopup);
769 
770 	// Memory Usage section
771 	MemoryBarMenu* MemoryPopup = new MemoryBarMenu(B_TRANSLATE("Memory usage"),
772 	infos, systemInfo);
773 	int64 committedMemory = (int64)systemInfo.used_pages * B_PAGE_SIZE / 1024;
774 	for (m = 0; m < systemInfo.used_teams; m++) {
775 		if (infos[m].team_info.team >= 0) {
776 			MemoryBarMenuItem* memoryItem =
777 				new MemoryBarMenuItem(infos[m].team_name,
778 					infos[m].team_info.team, infos[m].team_icon, false, NULL);
779 			MemoryPopup->AddItem(memoryItem);
780 			memoryItem->UpdateSituation(committedMemory);
781 		}
782 	}
783 
784 	addtopbottom(MemoryPopup);
785 
786 	// CPU Load section
787 	TeamBarMenu* CPUPopup = new TeamBarMenu(B_TRANSLATE("Threads and CPU "
788 	"usage"), infos, systemInfo.used_teams);
789 	for (m = 0; m < systemInfo.used_teams; m++) {
790 		if (infos[m].team_info.team >= 0) {
791 			ThreadBarMenu* TeamPopup = new ThreadBarMenu(infos[m].team_name,
792 				infos[m].team_info.team, infos[m].team_info.thread_count);
793 			BMessage* kill_team = new BMessage('KlTm');
794 			kill_team->AddInt32("team", infos[m].team_info.team);
795 			TeamBarMenuItem* item = new TeamBarMenuItem(TeamPopup, kill_team,
796 				infos[m].team_info.team, infos[m].team_icon, false);
797 			item->SetTarget(gPCView);
798 			CPUPopup->AddItem(item);
799 		}
800 	}
801 
802 	addtopbottom(CPUPopup);
803 	addtopbottom(new BSeparatorItem());
804 
805 	// CPU on/off section
806 	if (gCPUcount > 1) {
807 		for (unsigned int i = 0; i < gCPUcount; i++) {
808 			char item_name[32];
809 			sprintf (item_name, B_TRANSLATE("Processor %d"), i + 1);
810 			BMessage* m = new BMessage ('CPU ');
811 			m->AddInt32 ("cpu", i);
812 			item = new IconMenuItem (gPCView->fProcessorIcon, item_name, m);
813 			if (_kern_cpu_enabled(i))
814 				item->SetMarked (true);
815 			item->SetTarget(gPCView);
816 			addtopbottom(item);
817 		}
818 		addtopbottom (new BSeparatorItem ());
819 	}
820 
821 	// Scheduler modes
822 	static const char* schedulerModes[] = { B_TRANSLATE_MARK("Low Latency"),
823 		B_TRANSLATE_MARK("Power Saving") };
824 	unsigned int modesCount = sizeof(schedulerModes) / sizeof(const char*);
825 	int32 currentMode = get_scheduler_mode();
826 	for (unsigned int i = 0; i < modesCount; i++) {
827 		BMessage* m = new BMessage('Schd');
828 		m->AddInt32("mode", i);
829 		item = new BMenuItem(B_TRANSLATE(schedulerModes[i]), m);
830 		if ((uint32)currentMode == i)
831 			item->SetMarked(true);
832 		item->SetTarget(gPCView);
833 		addtopbottom(item);
834 	}
835 	addtopbottom(new BSeparatorItem());
836 
837 	if (!be_roster->IsRunning(kTrackerSig)) {
838 		item = new IconMenuItem(gPCView->fTrackerIcon,
839 		B_TRANSLATE("Restart Tracker"), new BMessage('Trac'));
840 		item->SetTarget(gPCView);
841 		addtopbottom(item);
842 	}
843 	if (!be_roster->IsRunning(kDeskbarSig)) {
844 		item = new IconMenuItem(gPCView->fDeskbarIcon,
845 		B_TRANSLATE("Restart Deskbar"), new BMessage('Dbar'));
846 		item->SetTarget(gPCView);
847 		addtopbottom(item);
848 	}
849 
850 	item = new IconMenuItem(gPCView->fTerminalIcon,
851 	B_TRANSLATE("New Terminal"), new BMessage('Term'));
852 	item->SetTarget(gPCView);
853 	addtopbottom(item);
854 
855 	addtopbottom(new BSeparatorItem());
856 
857 	bool showLiveInDeskbarItem = gInDeskbar;
858 	if (!showLiveInDeskbarItem) {
859 		int32 cookie = 0;
860 		image_info info;
861 		while (get_next_image_info(B_CURRENT_TEAM, &cookie, &info) == B_OK) {
862 			if (info.type == B_APP_IMAGE) {
863 				// only show the Live in Deskbar item if a) we're running in
864 				// deskbar itself, or b) we're running in PC's team.
865 				if (strstr(info.name, "ProcessController") != NULL) {
866 					showLiveInDeskbarItem = true;
867 					break;
868 				}
869 			}
870 		}
871 	}
872 
873 	if (showLiveInDeskbarItem && be_roster->IsRunning(kDeskbarSig)) {
874 		item = new BMenuItem(B_TRANSLATE("Live in the Deskbar"),
875 			new BMessage('AlDb'));
876 		BDeskbar deskbar;
877 		item->SetMarked(gInDeskbar || deskbar.HasItem(kDeskbarItemName));
878 		item->SetTarget(gPCView);
879 		addtopbottom(item);
880 		addtopbottom(new BSeparatorItem ());
881 	}
882 
883 
884 	item = new IconMenuItem(gPCView->fProcessControllerIcon,
885 	B_TRANSLATE("About ProcessController"B_UTF8_ELLIPSIS),
886 		new BMessage(B_ABOUT_REQUESTED));
887 	item->SetTarget(gPCView);
888 	addtopbottom(item);
889 
890 	param->where.x -= 5;
891 	param->where.y -= 8;
892 	popup->Go(param->where, true, true, param->clickToOpenRect);
893 
894 	delete popup;
895 	for (m = 0; m < systemInfo.used_teams; m++) {
896 		if (infos[m].team_info.team >= 0) {
897 			delete[] infos[m].thread_info;
898 			delete infos[m].team_icon;
899 		}
900 	}
901 	delete[] infos;
902 	delete param;
903 	atomic_add (&gPopupFlag, -1);
904 	gPopupThreadID = 0;
905 
906 	return B_OK;
907 }
908 
909 
910 status_t
911 thread_quit_application(void *arg)
912 {
913 	BMessenger messenger(NULL, (addr_t)arg);
914 	messenger.SendMessage(B_QUIT_REQUESTED);
915 	return B_OK;
916 }
917 
918 
919 status_t
920 thread_debug_thread(void *arg)
921 {
922 	Tdebug_thead_param*	param = (Tdebug_thead_param*) arg;
923 #ifdef __HAIKU__
924 	debug_thread(param->thread);
925 #else	// !__HAIKU__
926 	thread_info	thinfo;
927 	get_thread_info(param->thread, &thinfo);
928 	char text[4096];
929 	sprintf(text, "db %d", int(param->thread));
930 	system(text);
931 	if (param->sem >= 0 && thinfo.state == B_THREAD_WAITING && param->sem
932 			== thinfo.sem) {
933 		snooze(1000000);
934 		get_thread_info(param->thread, &thinfo);
935 		if (thinfo.state == B_THREAD_WAITING
936 			&& param->sem == thinfo.sem
937 			&& param->totalTime == thinfo.user_time + thinfo.kernel_time) {
938 			// the thread has been waiting for this semaphore since the before
939 			// the alert, not doing anything... Let's push it out of there!
940 			sem_info sinfo;
941 			thread_info thinfo;
942 			info_pack infos;
943 
944 			if (get_sem_info(param->sem, &sinfo) == B_OK
945 				&& get_thread_info(param->thread, &thinfo) == B_OK
946 				&& get_team_info(thinfo.team, &infos.team_info) == B_OK) {
947 				sprintf (text, "This thread is waiting for the "
948 					"semaphore called \"%s\". As long as it waits for this "
949 					"semaphore, you won't be able to debug that thread.\n",
950 						sinfo.name);
951 				if (sinfo.team == thinfo.team)
952 					strcat(text, "This semaphore belongs to the "
953 						"thread's team.\n\nShould I release this semaphore?\n");
954 				else {
955 					get_team_name_and_icon(infos);
956 					char moreText[1024];
957 					sprintf(moreText, "\nWARNING! This semaphore "
958 						"belongs to the team \"%s\"!\n\nShould I release this "
959 						"semaphore anyway?\n",
960 						infos.team_name);
961 					strcat(text, moreText);
962 				}
963 
964 				BAlert* alert = new BAlert("", text, "Cancel", "Release",
965 						NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
966 				alert->SetShortcut(0, B_ESCAPE);
967 				if (alert->Go()) {
968 					get_thread_info (param->thread, &thinfo);
969 					if (thinfo.state == B_THREAD_WAITING && param->sem
970 							== thinfo.sem
971 						&& param->totalTime == thinfo.user_time
972 							+ thinfo.kernel_time)
973 						release_sem(param->sem);
974 					else {
975 						alert = new BAlert("", "The semaphore wasn't released, "
976 							"because it wasn't necessary anymore!",
977 							"OK", NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
978 						alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
979 						alert->Go();
980 					}
981 				}
982 			}
983 		}
984 	}
985 #endif	// !__HAIKU__
986 	delete param;
987 	return B_OK;
988 }
989