xref: /haiku/src/apps/processcontroller/ProcessController.cpp (revision 5629675a326ecf2ff3fd23f154beb525c171048d)
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 			int32 mode;
475 			if (message->FindInt32 ("mode", &mode) == B_OK)
476 				set_scheduler_mode(mode);
477 			break;
478 		}
479 
480 		case B_ABOUT_REQUESTED:
481 			AboutRequested();
482 			break;
483 
484 		default:
485 			BView::MessageReceived(message);
486 	}
487 }
488 
489 
490 void
491 ProcessController::AboutRequested()
492 {
493 	BAboutWindow* window = new BAboutWindow(
494 		B_TRANSLATE_SYSTEM_NAME("ProcessController"), kSignature);
495 
496 	const char* extraCopyrights[] = {
497 		"2004 beunited.org",
498 		"1997-2001 Georges-Edouard Berenger",
499 		NULL
500 	};
501 
502 	const char* authors[] = {
503 		"Georges-Edouard Berenger",
504 		NULL
505 	};
506 
507 	window->AddCopyright(2007, "Haiku, Inc.", extraCopyrights);
508 	window->AddAuthors(authors);
509 
510 	window->Show();
511 }
512 
513 
514 void
515 ProcessController::DefaultColors()
516 {
517 	swap_color.red = 203;
518 	swap_color.green = 0;
519 	swap_color.blue = 0;
520 	swap_color.alpha = 255;
521 	bool set = false;
522 
523 	if (!set) {
524 		active_color = kKernelBlue;
525 		active_color = tint_color (active_color, B_LIGHTEN_2_TINT);
526 		idle_color = active_color;
527 		idle_color.green /= 3;
528 		idle_color.red /= 3;
529 		idle_color.blue /= 3;
530 		frame_color = kBlack;
531 		mix_colors (memory_color, active_color, swap_color, 0.2);
532 	}
533 }
534 
535 
536 void
537 ProcessController::AttachedToWindow()
538 {
539 	BView::AttachedToWindow();
540 	if (Parent())
541 		SetViewColor(B_TRANSPARENT_COLOR);
542 	else
543 		SetViewColor(kBlack);
544 
545 	Preferences tPreferences(kPreferencesFileName, NULL, false);
546 	DefaultColors();
547 
548 	system_info info;
549 	get_system_info(&info);
550 	gCPUcount = info.cpu_count;
551 	Update();
552 
553 	gIdleColor = kIdleGreen;
554 	gIdleColorSelected = tint_color(gIdleColor, B_HIGHLIGHT_BACKGROUND_TINT);
555 	gKernelColor = kKernelBlue;
556 	gKernelColorSelected = tint_color(gKernelColor, B_HIGHLIGHT_BACKGROUND_TINT);
557 	gUserColor = tint_color(gKernelColor, B_LIGHTEN_2_TINT);
558 	gUserColorSelected = tint_color(gUserColor, B_HIGHLIGHT_BACKGROUND_TINT);
559 	gFrameColor = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
560 		B_HIGHLIGHT_BACKGROUND_TINT);
561 	gFrameColorSelected = tint_color(gFrameColor, B_HIGHLIGHT_BACKGROUND_TINT);
562 	gMenuBackColor = ui_color(B_MENU_BACKGROUND_COLOR);
563 	gMenuBackColorSelected = ui_color(B_MENU_SELECTION_BACKGROUND_COLOR);
564 	gWhiteSelected = tint_color(kWhite, B_HIGHLIGHT_BACKGROUND_TINT);
565 
566 	BMessenger messenger(this);
567 	BMessage message('Puls');
568 	fMessageRunner = new BMessageRunner(messenger, &message, 250000, -1);
569 }
570 
571 
572 
573 void
574 ProcessController::MouseDown(BPoint where)
575 {
576 	if (atomic_add(&gPopupFlag, 1) > 0) {
577 		atomic_add(&gPopupFlag, -1);
578 		return;
579 	}
580 
581 	Tpopup_param* param = new Tpopup_param;
582 	ConvertToScreen(&where);
583 	param->where = where;
584 	param->clickToOpenRect = Frame ();
585 	ConvertToScreen (&param->clickToOpenRect);
586 	param->top = where.y < BScreen(this->Window()).Frame().bottom-50;
587 
588 	gPopupThreadID = spawn_thread(thread_popup, "Popup holder thread",
589 		B_URGENT_DISPLAY_PRIORITY, param);
590 	resume_thread(gPopupThreadID);
591 }
592 
593 
594 void
595 ProcessController::Draw(BRect)
596 {
597 	SetDrawingMode(B_OP_COPY);
598 	DoDraw(true);
599 }
600 
601 
602 void
603 ProcessController::DoDraw(bool force)
604 {
605 	BRect bounds(Bounds());
606 
607 	float h = floorf(bounds.Height ()) - 2;
608 	float top = 1, left = 1;
609 	float bottom = top + h;
610 	float barWidth;
611 	float barGap;
612 	float memWidth;
613 	if (gCPUcount <= 12 && bounds.Width() == 15) {
614 		// Use fixed sizes for small icon sizes
615 		barWidth = layout[gCPUcount].cpu_width;
616 		barGap = layout[gCPUcount].cpu_inter;
617 		memWidth = layout[gCPUcount].mem_width;
618 	} else {
619 		memWidth = floorf((bounds.Height() + 1) / 8);
620 		barGap = ((bounds.Width() + 1) / gCPUcount) > 3 ? 1 : 0;
621 		barWidth = floorf((bounds.Width() - 1 - memWidth - barGap * gCPUcount)
622 			/ gCPUcount);
623 	}
624 	// interspace
625 	float right = left + gCPUcount * (barWidth + barGap) - barGap;
626 	float leftMem = bounds.Width() - memWidth;
627 		// right of CPU frame...
628 	if (force && Parent()) {
629 		SetHighColor(Parent()->ViewColor());
630 		FillRect(BRect(right + 1, top - 1, leftMem, bottom + 1));
631 	}
632 
633 	if (force) {
634 		SetHighColor(frame_color);
635 		StrokeRect(BRect(left - 1, top - 1, right, bottom + 1));
636 		if (gCPUcount > 1 && barGap == 1) {
637 			for (unsigned int x = 1; x < gCPUcount; x++)
638 				StrokeLine(BPoint(left + x * barWidth + x - 1, top),
639 					BPoint(left + x * barWidth + x - 1, bottom));
640 		}
641 	}
642 
643 	if (force)
644 		StrokeRect(BRect(leftMem - 1, top - 1, leftMem + memWidth, bottom + 1));
645 
646 	for (unsigned int x = 0; x < gCPUcount; x++) {
647 		right = left + barWidth - 1;
648 		float rem = fCPUTimes[x] * (h + 1);
649 		float barHeight = floorf (rem);
650 		rem -= barHeight;
651 		float limit = bottom - barHeight;	// horizontal line
652 		float previousLimit = bottom - fLastBarHeight[x];
653 		float idleTop = top;
654 
655 		if (!force && previousLimit > top)
656 			idleTop = previousLimit - 1;
657 		if (limit > idleTop) {
658 			SetHighColor(idle_color);
659 			FillRect(BRect(left, idleTop, right, limit - 1));
660 		}
661 		if (barHeight <= h) {
662 			rgb_color fraction_color;
663 			mix_colors(fraction_color, idle_color, active_color, rem);
664 			SetHighColor(fraction_color);
665 			StrokeLine(BPoint(left, bottom - barHeight), BPoint(right,
666 				bottom - barHeight));
667 		}
668 		float active_bottom = bottom;
669 		if (!force && previousLimit < bottom)
670 			active_bottom = previousLimit + 1;
671 		if (limit < active_bottom) {
672 			SetHighColor(active_color);
673 			FillRect(BRect(left, limit + 1, right, active_bottom));
674 		}
675 		left += barWidth + barGap;
676 		fLastBarHeight[x] = barHeight;
677 	}
678 
679 	float rightMem = bounds.Width() - 1;
680 	float rem = fMemoryUsage * (h + 1);
681 	float barHeight = floorf(rem);
682 	rem -= barHeight;
683 
684 	rgb_color used_memory_color;
685 	float sq = fMemoryUsage * fMemoryUsage;
686 	sq *= sq;
687 	sq *= sq;
688 	mix_colors(used_memory_color, memory_color, swap_color, sq);
689 
690 	float limit = bottom - barHeight;	// horizontal line
691 	float previousLimit = bottom - fLastMemoryHeight;
692 	float free_top = top;
693 	if (!force && previousLimit > top)
694 		free_top = previousLimit - 1;
695 	if (limit > free_top) {
696 		SetHighColor (idle_color);
697 		FillRect(BRect(leftMem, free_top, rightMem, limit - 1));
698 	}
699 	if (barHeight <= h) {
700 		rgb_color fraction_color;
701 		mix_colors(fraction_color, idle_color, used_memory_color, rem);
702 		SetHighColor(fraction_color);
703 		StrokeLine(BPoint(leftMem, bottom - barHeight), BPoint(rightMem,
704 			bottom - barHeight));
705 	}
706 	float usedBottom = bottom;
707 //	if (!force && previousLimit < bottom)
708 //		usedBottom = previousLimit + 1;
709 	if (limit < usedBottom) {
710 		SetHighColor(used_memory_color);
711 		FillRect(BRect(leftMem, limit + 1, rightMem, usedBottom));
712 	}
713 	fLastMemoryHeight = barHeight;
714 }
715 
716 
717 void
718 ProcessController::Update()
719 {
720 	system_info info;
721 	get_system_info(&info);
722 	bigtime_t now = system_time();
723 
724 	cpu_info* cpuInfos = new cpu_info[gCPUcount];
725 	get_cpu_info(0, gCPUcount, cpuInfos);
726 
727 	fMemoryUsage = float(info.used_pages) / float(info.max_pages);
728 	// Calculate work done since last call to Update() for each CPU
729 	for (unsigned int x = 0; x < gCPUcount; x++) {
730 		bigtime_t load = cpuInfos[x].active_time - fPrevActive[x];
731 		bigtime_t passed = now - fPrevTime;
732 		float cpuTime = float(load) / float(passed);
733 
734 		fPrevActive[x] = cpuInfos[x].active_time;
735 		if (load > passed)
736 			fPrevActive[x] -= load - passed; // save overload for next period...
737 		if (cpuTime < 0)
738 			cpuTime = 0;
739 		if (cpuTime > 1)
740 			cpuTime = 1;
741 		fCPUTimes[x] = cpuTime;
742 	}
743 	fPrevTime = now;
744 
745 	delete[] cpuInfos;
746 }
747 
748 
749 //	#pragma mark -
750 
751 
752 status_t
753 thread_popup(void *arg)
754 {
755 	Tpopup_param* param = (Tpopup_param*) arg;
756 	int32 mcookie, hcookie;
757 	unsigned long m;
758 	long h;
759 	BMenuItem* item;
760 	bool top = param->top;
761 
762 	system_info systemInfo;
763 	get_system_info(&systemInfo);
764 	info_pack* infos = new info_pack[systemInfo.used_teams];
765 	// TODO: this doesn't necessarily get all teams
766 	for (m = 0, mcookie = 0; m < systemInfo.used_teams; m++) {
767 		infos[m].team_icon = NULL;
768 		infos[m].team_name[0] = 0;
769 		infos[m].thread_info = NULL;
770 		if (get_next_team_info(&mcookie, &infos[m].team_info) == B_OK) {
771 			infos[m].thread_info = new thread_info[infos[m].team_info.thread_count];
772 			for (h = 0, hcookie = 0; h < infos[m].team_info.thread_count; h++) {
773 				if (get_next_thread_info(infos[m].team_info.team, &hcookie,
774 						&infos[m].thread_info[h]) != B_OK)
775 					infos[m].thread_info[h].thread = -1;
776 			}
777 			get_team_name_and_icon(infos[m], true);
778 		} else {
779 			systemInfo.used_teams = m;
780 			infos[m].team_info.team = -1;
781 		}
782 	}
783 
784 	BPopUpMenu* popup = new BPopUpMenu("Global Popup", false, false);
785 	popup->SetFont(be_plain_font);
786 
787 	// Quit section
788 	BMenu* QuitPopup = new QuitMenu(B_TRANSLATE("Quit an application"),
789 	infos, systemInfo.used_teams);
790 	QuitPopup->SetFont(be_plain_font);
791 	popup->AddItem(QuitPopup);
792 
793 	// Memory Usage section
794 	MemoryBarMenu* MemoryPopup = new MemoryBarMenu(B_TRANSLATE("Memory usage"),
795 	infos, systemInfo);
796 	int64 committedMemory = (int64)systemInfo.used_pages * B_PAGE_SIZE / 1024;
797 	for (m = 0; m < systemInfo.used_teams; m++) {
798 		if (infos[m].team_info.team >= 0) {
799 			MemoryBarMenuItem* memoryItem =
800 				new MemoryBarMenuItem(infos[m].team_name,
801 					infos[m].team_info.team, infos[m].team_icon, false, NULL);
802 			MemoryPopup->AddItem(memoryItem);
803 			memoryItem->UpdateSituation(committedMemory);
804 		}
805 	}
806 
807 	addtopbottom(MemoryPopup);
808 
809 	// CPU Load section
810 	TeamBarMenu* CPUPopup = new TeamBarMenu(B_TRANSLATE("Threads and CPU "
811 	"usage"), infos, systemInfo.used_teams);
812 	for (m = 0; m < systemInfo.used_teams; m++) {
813 		if (infos[m].team_info.team >= 0) {
814 			ThreadBarMenu* TeamPopup = new ThreadBarMenu(infos[m].team_name,
815 				infos[m].team_info.team, infos[m].team_info.thread_count);
816 			BMessage* kill_team = new BMessage('KlTm');
817 			kill_team->AddInt32("team", infos[m].team_info.team);
818 			TeamBarMenuItem* item = new TeamBarMenuItem(TeamPopup, kill_team,
819 				infos[m].team_info.team, infos[m].team_icon, false);
820 			item->SetTarget(gPCView);
821 			CPUPopup->AddItem(item);
822 		}
823 	}
824 
825 	addtopbottom(CPUPopup);
826 	addtopbottom(new BSeparatorItem());
827 
828 	// CPU on/off section
829 	if (gCPUcount > 1) {
830 		for (unsigned int i = 0; i < gCPUcount; i++) {
831 			char item_name[32];
832 			sprintf (item_name, B_TRANSLATE("Processor %d"), i + 1);
833 			BMessage* m = new BMessage ('CPU ');
834 			m->AddInt32 ("cpu", i);
835 			item = new IconMenuItem (gPCView->fProcessorIcon, item_name, m);
836 			if (_kern_cpu_enabled(i))
837 				item->SetMarked (true);
838 			item->SetTarget(gPCView);
839 			addtopbottom(item);
840 		}
841 		addtopbottom (new BSeparatorItem ());
842 	}
843 
844 	// Scheduler modes
845 	static const char* schedulerModes[] = { B_TRANSLATE_MARK("Low latency"),
846 		B_TRANSLATE_MARK("Power saving") };
847 	unsigned int modesCount = sizeof(schedulerModes) / sizeof(const char*);
848 	int32 currentMode = get_scheduler_mode();
849 	for (unsigned int i = 0; i < modesCount; i++) {
850 		BMessage* m = new BMessage('Schd');
851 		m->AddInt32("mode", i);
852 		item = new BMenuItem(B_TRANSLATE(schedulerModes[i]), m);
853 		if ((uint32)currentMode == i)
854 			item->SetMarked(true);
855 		item->SetTarget(gPCView);
856 		addtopbottom(item);
857 	}
858 	addtopbottom(new BSeparatorItem());
859 
860 	if (!be_roster->IsRunning(kTrackerSig)) {
861 		item = new IconMenuItem(gPCView->fTrackerIcon,
862 		B_TRANSLATE("Restart Tracker"), new BMessage('Trac'));
863 		item->SetTarget(gPCView);
864 		addtopbottom(item);
865 	}
866 	if (!be_roster->IsRunning(kDeskbarSig)) {
867 		item = new IconMenuItem(gPCView->fDeskbarIcon,
868 		B_TRANSLATE("Restart Deskbar"), new BMessage('Dbar'));
869 		item->SetTarget(gPCView);
870 		addtopbottom(item);
871 	}
872 
873 	item = new IconMenuItem(gPCView->fTerminalIcon,
874 	B_TRANSLATE("New Terminal"), new BMessage('Term'));
875 	item->SetTarget(gPCView);
876 	addtopbottom(item);
877 
878 	addtopbottom(new BSeparatorItem());
879 
880 	bool showLiveInDeskbarItem = gInDeskbar;
881 	if (!showLiveInDeskbarItem) {
882 		int32 cookie = 0;
883 		image_info info;
884 		while (get_next_image_info(B_CURRENT_TEAM, &cookie, &info) == B_OK) {
885 			if (info.type == B_APP_IMAGE) {
886 				// only show the Live in Deskbar item if a) we're running in
887 				// deskbar itself, or b) we're running in PC's team.
888 				if (strstr(info.name, "ProcessController") != NULL) {
889 					showLiveInDeskbarItem = true;
890 					break;
891 				}
892 			}
893 		}
894 	}
895 
896 	if (showLiveInDeskbarItem && be_roster->IsRunning(kDeskbarSig)) {
897 		item = new BMenuItem(B_TRANSLATE("Live in the Deskbar"),
898 			new BMessage('AlDb'));
899 		BDeskbar deskbar;
900 		item->SetMarked(gInDeskbar || deskbar.HasItem(kDeskbarItemName));
901 		item->SetTarget(gPCView);
902 		addtopbottom(item);
903 		addtopbottom(new BSeparatorItem ());
904 	}
905 
906 
907 	item = new IconMenuItem(gPCView->fProcessControllerIcon,
908 	B_TRANSLATE("About ProcessController" B_UTF8_ELLIPSIS),
909 		new BMessage(B_ABOUT_REQUESTED));
910 	item->SetTarget(gPCView);
911 	addtopbottom(item);
912 
913 	param->where.x -= 5;
914 	param->where.y -= 8;
915 	popup->Go(param->where, true, true, param->clickToOpenRect);
916 
917 	delete popup;
918 	for (m = 0; m < systemInfo.used_teams; m++) {
919 		if (infos[m].team_info.team >= 0) {
920 			delete[] infos[m].thread_info;
921 			delete infos[m].team_icon;
922 		}
923 	}
924 	delete[] infos;
925 	delete param;
926 	atomic_add (&gPopupFlag, -1);
927 	gPopupThreadID = 0;
928 
929 	return B_OK;
930 }
931 
932 
933 status_t
934 thread_quit_application(void *arg)
935 {
936 	BMessenger messenger(NULL, (addr_t)arg);
937 	messenger.SendMessage(B_QUIT_REQUESTED);
938 	return B_OK;
939 }
940 
941 
942 status_t
943 thread_debug_thread(void *arg)
944 {
945 	Tdebug_thead_param*	param = (Tdebug_thead_param*) arg;
946 	debug_thread(param->thread);
947 	delete param;
948 	return B_OK;
949 }
950