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