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