xref: /haiku/src/apps/processcontroller/ProcessController.cpp (revision c38a5f90997cef0d0d574585f01d664707b03c05)
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*
instantiate_deskbar_item(float maxWidth,float maxHeight)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 
ProcessController(BRect frame,bool temp)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 
ProcessController(BMessage * data)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 
ProcessController(float width,float height)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 
~ProcessController()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
Init()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
_HandleDebugRequest(team_id team,thread_id thread)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*
Instantiate(BMessage * data)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
Archive(BMessage * data,bool deep) const275 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
MessageReceived(BMessage * message)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 			Preferences preferences(kPreferencesFileName);
476 			preferences.SaveInt32(get_scheduler_mode(), "scheduler_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
AboutRequested()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
DefaultColors()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
AttachedToWindow()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
MouseDown(BPoint where)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
Draw(BRect)595 ProcessController::Draw(BRect)
596 {
597 	SetDrawingMode(B_OP_COPY);
598 	DoDraw(true);
599 }
600 
601 
602 void
DoDraw(bool force)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 <= 4 && bounds.Width() == 15) {
614 		// Use fixed sizes for small CPU counts
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
Update()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
thread_popup(void * arg)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 	int32 currentMode = get_scheduler_mode();
846 	Preferences preferences(kPreferencesFileName, NULL, false);
847 	int32 savedMode;
848 	if (preferences.ReadInt32(savedMode, "scheduler_mode") && currentMode != savedMode) {
849 		set_scheduler_mode(savedMode);
850 		currentMode = get_scheduler_mode();
851 	}
852 	BMessage* msg = new BMessage('Schd');
853 	item = new BMenuItem(B_TRANSLATE("Power saving"), msg);
854 	if ((uint32)currentMode == SCHEDULER_MODE_POWER_SAVING)
855 		item->SetMarked(true);
856 	item->SetTarget(gPCView);
857 	addtopbottom(item);
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
thread_quit_application(void * arg)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
thread_debug_thread(void * arg)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