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