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