xref: /haiku/src/apps/processcontroller/ProcessController.cpp (revision acdafb571df692fab7ad89836e39590dd8d415e2)
1 /*
2  * Copyright 2000, Georges-Edouard Berenger. All rights reserved.
3  * Copyright 2006-2018, Haiku, Inc. All rights reserved.
4  * Distributed under the terms of the MIT License.
5  */
6 
7 
8 #include "ProcessController.h"
9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #include <AboutWindow.h>
15 #include <Alert.h>
16 #include <Bitmap.h>
17 #include <Catalog.h>
18 #include <debugger.h>
19 #include <Deskbar.h>
20 #include <Directory.h>
21 #include <Dragger.h>
22 #include <File.h>
23 #include <FindDirectory.h>
24 #include <MessageRunner.h>
25 #include <Path.h>
26 #include <PopUpMenu.h>
27 #include <Roster.h>
28 #include <Screen.h>
29 #include <TextView.h>
30 
31 #include <scheduler.h>
32 #include <syscalls.h>
33 
34 #include "AutoIcon.h"
35 #include "Colors.h"
36 #include "IconMenuItem.h"
37 #include "MemoryBarMenu.h"
38 #include "MemoryBarMenuItem.h"
39 #include "PCWorld.h"
40 #include "Preferences.h"
41 #include "QuitMenu.h"
42 #include "TeamBarMenu.h"
43 #include "TeamBarMenuItem.h"
44 #include "ThreadBarMenu.h"
45 #include "Utilities.h"
46 
47 
48 #undef B_TRANSLATION_CONTEXT
49 #define B_TRANSLATION_CONTEXT "ProcessController"
50 
51 
52 const char* kDeskbarItemName = "ProcessController";
53 const char* kClassName = "ProcessController";
54 
55 const char* kFrameColorPref = "deskbar_frame_color";
56 const char* kIdleColorPref = "deskbar_idle_color";
57 const char* kActiveColorPref = "deskbar_active_color";
58 
59 static const char* const kDebuggerSignature
60 	= "application/x-vnd.Haiku-Debugger";
61 
62 const rgb_color kKernelBlue = {20, 20, 231,	255};
63 const rgb_color kIdleGreen = {110, 190,110,	255};
64 
65 ProcessController* gPCView;
66 uint32 gCPUcount;
67 rgb_color gUserColor;
68 rgb_color gUserColorSelected;
69 rgb_color gIdleColor;
70 rgb_color gIdleColorSelected;
71 rgb_color gKernelColor;
72 rgb_color gKernelColorSelected;
73 rgb_color gFrameColor;
74 rgb_color gFrameColorSelected;
75 rgb_color gMenuBackColorSelected;
76 rgb_color gMenuBackColor;
77 rgb_color gWhiteSelected;
78 ThreadBarMenu* gCurrentThreadBarMenu;
79 bool gInDeskbar = false;
80 
81 #define addtopbottom(x) if (top) popup->AddItem(x); else popup->AddItem(x, 0)
82 
83 status_t thread_popup(void *arg);
84 
85 int32			gPopupFlag = 0;
86 thread_id		gPopupThreadID = 0;
87 
88 typedef struct {
89 	BPoint		where;
90 	BRect		clickToOpenRect;
91 	bool		top;
92 } Tpopup_param;
93 
94 #define DEBUG_THREADS 1
95 
96 status_t thread_quit_application(void *arg);
97 status_t thread_debug_thread(void *arg);
98 
99 typedef struct {
100 	thread_id	thread;
101 	sem_id		sem;
102 	time_t		totalTime;
103 } Tdebug_thead_param;
104 
105 // Bar layout depending on number of CPUs
106 // This is used only in case the replicant width is 16
107 
108 typedef struct {
109 	float	cpu_width;
110 	float	cpu_inter;
111 	float	mem_width;
112 } layoutT;
113 
114 layoutT layout[] = {
115 	{ 1, 1, 1 },
116 	{ 5, 1, 5 },	// 1
117 	{ 3, 1, 4 },	// 2
118 	{ 2, 1, 3 },
119 	{ 2, 0, 3 },	// 4
120 };
121 
122 
123 extern "C" _EXPORT BView* instantiate_deskbar_item(float maxWidth,
124 	float maxHeight);
125 
126 extern "C" _EXPORT BView*
127 instantiate_deskbar_item(float maxWidth, float maxHeight)
128 {
129 	gInDeskbar = true;
130 
131 	system_info info;
132 	get_system_info(&info);
133 	int width = 4;
134 	if (info.cpu_count > 4)
135 		width = info.cpu_count;
136 	if (info.cpu_count <= 16)
137 		width *= 2;
138 
139 	// For the memory bar
140 	width += 8;
141 
142 	// Damn, you got a lot of CPU
143 	if (width > maxWidth)
144 		width = (int)maxWidth;
145 
146 	return new ProcessController(width - 1, maxHeight - 1);
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(float width, float height)
196 	:
197 	BView(BRect (0, 0, width, height), kDeskbarItemName, B_FOLLOW_NONE,
198 		B_WILL_DRAW),
199 	fProcessControllerIcon(kSignature),
200 	fProcessorIcon(k_cpu_mini),
201 	fTrackerIcon(kTrackerSig),
202 	fDeskbarIcon(kDeskbarSig),
203 	fTerminalIcon(kTerminalSig),
204 	kCPUCount(sysconf(_SC_NPROCESSORS_CONF)),
205 	fTemp(false),
206 	fLastBarHeight(new float[kCPUCount]),
207 	fCPUTimes(new double[kCPUCount]),
208 	fPrevActive(new bigtime_t[kCPUCount])
209 {
210 	Init();
211 }
212 
213 
214 ProcessController::~ProcessController()
215 {
216 	if (!fTemp) {
217 		if (gPopupThreadID) {
218 			status_t return_value;
219 			wait_for_thread (gPopupThreadID, &return_value);
220 		}
221 	}
222 
223 	delete fMessageRunner;
224 	gPCView = NULL;
225 
226 	delete[] fPrevActive;
227 	delete[] fCPUTimes;
228 	delete[] fLastBarHeight;
229 }
230 
231 
232 void
233 ProcessController::Init()
234 {
235 	memset(fLastBarHeight, 0, sizeof(float) * kCPUCount);
236 	memset(fCPUTimes, 0, sizeof(double) * kCPUCount);
237 	memset(fPrevActive, 0, sizeof(bigtime_t) * kCPUCount);
238 
239 	gPCView = this;
240 	fMessageRunner = NULL;
241 	fLastMemoryHeight = 0;
242 	fPrevTime = 0;
243 }
244 
245 
246 void
247 ProcessController::_HandleDebugRequest(team_id team, thread_id thread)
248 {
249 	char paramString[16];
250 	char idString[16];
251 	strlcpy(paramString, thread > 0 ? "--thread" : "--team",
252 		sizeof(paramString));
253 	snprintf(idString, sizeof(idString), "%" B_PRId32,
254 		thread > 0 ? thread : team);
255 
256 	const char* argv[] = {paramString, idString, NULL};
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"
454 						B_UTF8_ELLIPSIS "\n"
455 						"You can't turn it off!"),
456 						B_TRANSLATE("That's no Fun!"), NULL, NULL,
457 						B_WIDTH_AS_USUAL, B_WARNING_ALERT);
458 					alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
459 					alert->Go();
460 				} else
461 					_kern_set_cpu_enabled(cpu, !_kern_cpu_enabled(cpu));
462 			}
463 			break;
464 		}
465 
466 		case 'Schd':
467 		{
468 			BMenuItem* source;
469 			if (message->FindPointer("source", (void**)&source) != B_OK)
470 				break;
471 			if (!source->IsMarked())
472 				set_scheduler_mode(SCHEDULER_MODE_POWER_SAVING);
473 			else
474 				set_scheduler_mode(SCHEDULER_MODE_LOW_LATENCY);
475 			break;
476 		}
477 
478 		case B_ABOUT_REQUESTED:
479 			AboutRequested();
480 			break;
481 
482 		default:
483 			BView::MessageReceived(message);
484 	}
485 }
486 
487 
488 void
489 ProcessController::AboutRequested()
490 {
491 	BAboutWindow* window = new BAboutWindow(
492 		B_TRANSLATE_SYSTEM_NAME("ProcessController"), kSignature);
493 
494 	const char* extraCopyrights[] = {
495 		"2004 beunited.org",
496 		"1997-2001 Georges-Edouard Berenger",
497 		NULL
498 	};
499 
500 	const char* authors[] = {
501 		"Georges-Edouard Berenger",
502 		NULL
503 	};
504 
505 	window->AddCopyright(2007, "Haiku, Inc.", extraCopyrights);
506 	window->AddAuthors(authors);
507 
508 	window->Show();
509 }
510 
511 
512 void
513 ProcessController::DefaultColors()
514 {
515 	swap_color.red = 203;
516 	swap_color.green = 0;
517 	swap_color.blue = 0;
518 	swap_color.alpha = 255;
519 	bool set = false;
520 
521 	if (!set) {
522 		active_color = kKernelBlue;
523 		active_color = tint_color (active_color, B_LIGHTEN_2_TINT);
524 		idle_color = active_color;
525 		idle_color.green /= 3;
526 		idle_color.red /= 3;
527 		idle_color.blue /= 3;
528 		frame_color = kBlack;
529 		mix_colors (memory_color, active_color, swap_color, 0.2);
530 	}
531 }
532 
533 
534 void
535 ProcessController::AttachedToWindow()
536 {
537 	BView::AttachedToWindow();
538 	if (Parent())
539 		SetViewColor(B_TRANSPARENT_COLOR);
540 	else
541 		SetViewColor(kBlack);
542 
543 	Preferences tPreferences(kPreferencesFileName, NULL, false);
544 	DefaultColors();
545 
546 	system_info info;
547 	get_system_info(&info);
548 	gCPUcount = info.cpu_count;
549 	Update();
550 
551 	gIdleColor = kIdleGreen;
552 	gIdleColorSelected = tint_color(gIdleColor, B_HIGHLIGHT_BACKGROUND_TINT);
553 	gKernelColor = kKernelBlue;
554 	gKernelColorSelected = tint_color(gKernelColor, B_HIGHLIGHT_BACKGROUND_TINT);
555 	gUserColor = tint_color(gKernelColor, B_LIGHTEN_2_TINT);
556 	gUserColorSelected = tint_color(gUserColor, B_HIGHLIGHT_BACKGROUND_TINT);
557 	gFrameColor = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
558 		B_HIGHLIGHT_BACKGROUND_TINT);
559 	gFrameColorSelected = tint_color(gFrameColor, B_HIGHLIGHT_BACKGROUND_TINT);
560 	gMenuBackColor = ui_color(B_MENU_BACKGROUND_COLOR);
561 	gMenuBackColorSelected = ui_color(B_MENU_SELECTION_BACKGROUND_COLOR);
562 	gWhiteSelected = tint_color(kWhite, B_HIGHLIGHT_BACKGROUND_TINT);
563 
564 	BMessenger messenger(this);
565 	BMessage message('Puls');
566 	fMessageRunner = new BMessageRunner(messenger, &message, 250000, -1);
567 }
568 
569 
570 
571 void
572 ProcessController::MouseDown(BPoint where)
573 {
574 	if (atomic_add(&gPopupFlag, 1) > 0) {
575 		atomic_add(&gPopupFlag, -1);
576 		return;
577 	}
578 
579 	Tpopup_param* param = new Tpopup_param;
580 	ConvertToScreen(&where);
581 	param->where = where;
582 	param->clickToOpenRect = Frame ();
583 	ConvertToScreen (&param->clickToOpenRect);
584 	param->top = where.y < BScreen(this->Window()).Frame().bottom-50;
585 
586 	gPopupThreadID = spawn_thread(thread_popup, "Popup holder thread",
587 		B_URGENT_DISPLAY_PRIORITY, param);
588 	resume_thread(gPopupThreadID);
589 }
590 
591 
592 void
593 ProcessController::Draw(BRect)
594 {
595 	SetDrawingMode(B_OP_COPY);
596 	DoDraw(true);
597 }
598 
599 
600 void
601 ProcessController::DoDraw(bool force)
602 {
603 	BRect bounds(Bounds());
604 
605 	float h = floorf(bounds.Height ()) - 2;
606 	float top = 1, left = 1;
607 	float bottom = top + h;
608 	float barWidth;
609 	float barGap;
610 	float memWidth;
611 	if (gCPUcount <= 4 && bounds.Width() == 15) {
612 		// Use fixed sizes for small CPU counts
613 		barWidth = layout[gCPUcount].cpu_width;
614 		barGap = layout[gCPUcount].cpu_inter;
615 		memWidth = layout[gCPUcount].mem_width;
616 	} else {
617 		memWidth = floorf((bounds.Height() + 1) / 8);
618 		barGap = ((bounds.Width() + 1) / gCPUcount) > 3 ? 1 : 0;
619 		barWidth = floorf((bounds.Width() - 1 - memWidth - barGap * gCPUcount)
620 			/ gCPUcount);
621 	}
622 	// interspace
623 	float right = left + gCPUcount * (barWidth + barGap) - barGap;
624 	float leftMem = bounds.Width() - memWidth;
625 		// right of CPU frame...
626 	if (force && Parent()) {
627 		SetHighColor(Parent()->ViewColor());
628 		FillRect(BRect(right + 1, top - 1, leftMem, bottom + 1));
629 	}
630 
631 	if (force) {
632 		SetHighColor(frame_color);
633 		StrokeRect(BRect(left - 1, top - 1, right, bottom + 1));
634 		if (gCPUcount > 1 && barGap == 1) {
635 			for (unsigned int x = 1; x < gCPUcount; x++)
636 				StrokeLine(BPoint(left + x * barWidth + x - 1, top),
637 					BPoint(left + x * barWidth + x - 1, bottom));
638 		}
639 	}
640 
641 	if (force)
642 		StrokeRect(BRect(leftMem - 1, top - 1, leftMem + memWidth, bottom + 1));
643 
644 	for (unsigned int x = 0; x < gCPUcount; x++) {
645 		right = left + barWidth - 1;
646 		float rem = fCPUTimes[x] * (h + 1);
647 		float barHeight = floorf (rem);
648 		rem -= barHeight;
649 		float limit = bottom - barHeight;	// horizontal line
650 		float previousLimit = bottom - fLastBarHeight[x];
651 		float idleTop = top;
652 
653 		if (!force && previousLimit > top)
654 			idleTop = previousLimit - 1;
655 		if (limit > idleTop) {
656 			SetHighColor(idle_color);
657 			FillRect(BRect(left, idleTop, right, limit - 1));
658 		}
659 		if (barHeight <= h) {
660 			rgb_color fraction_color;
661 			mix_colors(fraction_color, idle_color, active_color, rem);
662 			SetHighColor(fraction_color);
663 			StrokeLine(BPoint(left, bottom - barHeight), BPoint(right,
664 				bottom - barHeight));
665 		}
666 		float active_bottom = bottom;
667 		if (!force && previousLimit < bottom)
668 			active_bottom = previousLimit + 1;
669 		if (limit < active_bottom) {
670 			SetHighColor(active_color);
671 			FillRect(BRect(left, limit + 1, right, active_bottom));
672 		}
673 		left += barWidth + barGap;
674 		fLastBarHeight[x] = barHeight;
675 	}
676 
677 	float rightMem = bounds.Width() - 1;
678 	float rem = fMemoryUsage * (h + 1);
679 	float barHeight = floorf(rem);
680 	rem -= barHeight;
681 
682 	rgb_color used_memory_color;
683 	float sq = fMemoryUsage * fMemoryUsage;
684 	sq *= sq;
685 	sq *= sq;
686 	mix_colors(used_memory_color, memory_color, swap_color, sq);
687 
688 	float limit = bottom - barHeight;	// horizontal line
689 	float previousLimit = bottom - fLastMemoryHeight;
690 	float free_top = top;
691 	if (!force && previousLimit > top)
692 		free_top = previousLimit - 1;
693 	if (limit > free_top) {
694 		SetHighColor (idle_color);
695 		FillRect(BRect(leftMem, free_top, rightMem, limit - 1));
696 	}
697 	if (barHeight <= h) {
698 		rgb_color fraction_color;
699 		mix_colors(fraction_color, idle_color, used_memory_color, rem);
700 		SetHighColor(fraction_color);
701 		StrokeLine(BPoint(leftMem, bottom - barHeight), BPoint(rightMem,
702 			bottom - barHeight));
703 	}
704 	float usedBottom = bottom;
705 //	if (!force && previousLimit < bottom)
706 //		usedBottom = previousLimit + 1;
707 	if (limit < usedBottom) {
708 		SetHighColor(used_memory_color);
709 		FillRect(BRect(leftMem, limit + 1, rightMem, usedBottom));
710 	}
711 	fLastMemoryHeight = barHeight;
712 }
713 
714 
715 void
716 ProcessController::Update()
717 {
718 	system_info info;
719 	get_system_info(&info);
720 	bigtime_t now = system_time();
721 
722 	cpu_info* cpuInfos = new cpu_info[gCPUcount];
723 	get_cpu_info(0, gCPUcount, cpuInfos);
724 
725 	fMemoryUsage = float(info.used_pages) / float(info.max_pages);
726 	// Calculate work done since last call to Update() for each CPU
727 	for (unsigned int x = 0; x < gCPUcount; x++) {
728 		bigtime_t load = cpuInfos[x].active_time - fPrevActive[x];
729 		bigtime_t passed = now - fPrevTime;
730 		float cpuTime = float(load) / float(passed);
731 
732 		fPrevActive[x] = cpuInfos[x].active_time;
733 		if (load > passed)
734 			fPrevActive[x] -= load - passed; // save overload for next period...
735 		if (cpuTime < 0)
736 			cpuTime = 0;
737 		if (cpuTime > 1)
738 			cpuTime = 1;
739 		fCPUTimes[x] = cpuTime;
740 	}
741 	fPrevTime = now;
742 
743 	delete[] cpuInfos;
744 }
745 
746 
747 //	#pragma mark -
748 
749 
750 status_t
751 thread_popup(void *arg)
752 {
753 	Tpopup_param* param = (Tpopup_param*) arg;
754 	int32 mcookie, hcookie;
755 	unsigned long m;
756 	long h;
757 	BMenuItem* item;
758 	bool top = param->top;
759 
760 	system_info systemInfo;
761 	get_system_info(&systemInfo);
762 	info_pack* infos = new info_pack[systemInfo.used_teams];
763 	// TODO: this doesn't necessarily get all teams
764 	for (m = 0, mcookie = 0; m < systemInfo.used_teams; m++) {
765 		infos[m].team_icon = NULL;
766 		infos[m].team_name[0] = 0;
767 		infos[m].thread_info = NULL;
768 		if (get_next_team_info(&mcookie, &infos[m].team_info) == B_OK) {
769 			infos[m].thread_info = new thread_info[infos[m].team_info.thread_count];
770 			for (h = 0, hcookie = 0; h < infos[m].team_info.thread_count; h++) {
771 				if (get_next_thread_info(infos[m].team_info.team, &hcookie,
772 						&infos[m].thread_info[h]) != B_OK)
773 					infos[m].thread_info[h].thread = -1;
774 			}
775 			get_team_name_and_icon(infos[m], true);
776 		} else {
777 			systemInfo.used_teams = m;
778 			infos[m].team_info.team = -1;
779 		}
780 	}
781 
782 	BPopUpMenu* popup = new BPopUpMenu("Global Popup", false, false);
783 	popup->SetFont(be_plain_font);
784 
785 	// Quit section
786 	BMenu* QuitPopup = new QuitMenu(B_TRANSLATE("Quit an application"),
787 	infos, systemInfo.used_teams);
788 	QuitPopup->SetFont(be_plain_font);
789 	popup->AddItem(QuitPopup);
790 
791 	// Memory Usage section
792 	MemoryBarMenu* MemoryPopup = new MemoryBarMenu(B_TRANSLATE("Memory usage"),
793 	infos, systemInfo);
794 	int64 committedMemory = (int64)systemInfo.used_pages * B_PAGE_SIZE / 1024;
795 	for (m = 0; m < systemInfo.used_teams; m++) {
796 		if (infos[m].team_info.team >= 0) {
797 			MemoryBarMenuItem* memoryItem =
798 				new MemoryBarMenuItem(infos[m].team_name,
799 					infos[m].team_info.team, infos[m].team_icon, false, NULL);
800 			MemoryPopup->AddItem(memoryItem);
801 			memoryItem->UpdateSituation(committedMemory);
802 		}
803 	}
804 
805 	addtopbottom(MemoryPopup);
806 
807 	// CPU Load section
808 	TeamBarMenu* CPUPopup = new TeamBarMenu(B_TRANSLATE("Threads and CPU "
809 	"usage"), infos, systemInfo.used_teams);
810 	for (m = 0; m < systemInfo.used_teams; m++) {
811 		if (infos[m].team_info.team >= 0) {
812 			ThreadBarMenu* TeamPopup = new ThreadBarMenu(infos[m].team_name,
813 				infos[m].team_info.team, infos[m].team_info.thread_count);
814 			BMessage* kill_team = new BMessage('KlTm');
815 			kill_team->AddInt32("team", infos[m].team_info.team);
816 			TeamBarMenuItem* item = new TeamBarMenuItem(TeamPopup, kill_team,
817 				infos[m].team_info.team, infos[m].team_icon, false);
818 			item->SetTarget(gPCView);
819 			CPUPopup->AddItem(item);
820 		}
821 	}
822 
823 	addtopbottom(CPUPopup);
824 	addtopbottom(new BSeparatorItem());
825 
826 	// CPU on/off section
827 	if (gCPUcount > 1) {
828 		for (unsigned int i = 0; i < gCPUcount; i++) {
829 			char item_name[32];
830 			sprintf (item_name, B_TRANSLATE("Processor %d"), i + 1);
831 			BMessage* m = new BMessage ('CPU ');
832 			m->AddInt32 ("cpu", i);
833 			item = new IconMenuItem (gPCView->fProcessorIcon, item_name, m);
834 			if (_kern_cpu_enabled(i))
835 				item->SetMarked (true);
836 			item->SetTarget(gPCView);
837 			addtopbottom(item);
838 		}
839 		addtopbottom (new BSeparatorItem ());
840 	}
841 
842 	// Scheduler modes
843 	int32 currentMode = get_scheduler_mode();
844 	BMessage* msg = new BMessage('Schd');
845 	item = new BMenuItem(B_TRANSLATE("Power saving"), msg);
846 	if ((uint32)currentMode == SCHEDULER_MODE_POWER_SAVING)
847 		item->SetMarked(true);
848 	item->SetTarget(gPCView);
849 	addtopbottom(item);
850 	addtopbottom(new BSeparatorItem());
851 
852 	if (!be_roster->IsRunning(kTrackerSig)) {
853 		item = new IconMenuItem(gPCView->fTrackerIcon,
854 		B_TRANSLATE("Restart Tracker"), new BMessage('Trac'));
855 		item->SetTarget(gPCView);
856 		addtopbottom(item);
857 	}
858 	if (!be_roster->IsRunning(kDeskbarSig)) {
859 		item = new IconMenuItem(gPCView->fDeskbarIcon,
860 		B_TRANSLATE("Restart Deskbar"), new BMessage('Dbar'));
861 		item->SetTarget(gPCView);
862 		addtopbottom(item);
863 	}
864 
865 	item = new IconMenuItem(gPCView->fTerminalIcon,
866 	B_TRANSLATE("New Terminal"), new BMessage('Term'));
867 	item->SetTarget(gPCView);
868 	addtopbottom(item);
869 
870 	addtopbottom(new BSeparatorItem());
871 
872 	bool showLiveInDeskbarItem = gInDeskbar;
873 	if (!showLiveInDeskbarItem) {
874 		int32 cookie = 0;
875 		image_info info;
876 		while (get_next_image_info(B_CURRENT_TEAM, &cookie, &info) == B_OK) {
877 			if (info.type == B_APP_IMAGE) {
878 				// only show the Live in Deskbar item if a) we're running in
879 				// deskbar itself, or b) we're running in PC's team.
880 				if (strstr(info.name, "ProcessController") != NULL) {
881 					showLiveInDeskbarItem = true;
882 					break;
883 				}
884 			}
885 		}
886 	}
887 
888 	if (showLiveInDeskbarItem && be_roster->IsRunning(kDeskbarSig)) {
889 		item = new BMenuItem(B_TRANSLATE("Live in the Deskbar"),
890 			new BMessage('AlDb'));
891 		BDeskbar deskbar;
892 		item->SetMarked(gInDeskbar || deskbar.HasItem(kDeskbarItemName));
893 		item->SetTarget(gPCView);
894 		addtopbottom(item);
895 		addtopbottom(new BSeparatorItem ());
896 	}
897 
898 
899 	item = new IconMenuItem(gPCView->fProcessControllerIcon,
900 	B_TRANSLATE("About ProcessController" B_UTF8_ELLIPSIS),
901 		new BMessage(B_ABOUT_REQUESTED));
902 	item->SetTarget(gPCView);
903 	addtopbottom(item);
904 
905 	param->where.x -= 5;
906 	param->where.y -= 8;
907 	popup->Go(param->where, true, true, param->clickToOpenRect);
908 
909 	delete popup;
910 	for (m = 0; m < systemInfo.used_teams; m++) {
911 		if (infos[m].team_info.team >= 0) {
912 			delete[] infos[m].thread_info;
913 			delete infos[m].team_icon;
914 		}
915 	}
916 	delete[] infos;
917 	delete param;
918 	atomic_add (&gPopupFlag, -1);
919 	gPopupThreadID = 0;
920 
921 	return B_OK;
922 }
923 
924 
925 status_t
926 thread_quit_application(void *arg)
927 {
928 	BMessenger messenger(NULL, (addr_t)arg);
929 	messenger.SendMessage(B_QUIT_REQUESTED);
930 	return B_OK;
931 }
932 
933 
934 status_t
935 thread_debug_thread(void *arg)
936 {
937 	Tdebug_thead_param*	param = (Tdebug_thead_param*) arg;
938 	debug_thread(param->thread);
939 	delete param;
940 	return B_OK;
941 }
942