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