xref: /haiku/src/apps/processcontroller/ProcessController.cpp (revision 541ff51a6ef4c47f8ab105ba6ff895cdbba83aca)
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("Terminal") == B_OK) {
372 				launch(kTerminalSig, terminalPath.Path());
373 			}
374 			break;
375 		}
376 
377 		case 'AlDb':
378 		{
379 			if (!be_roster->IsRunning(kDeskbarSig)) {
380 				BPath deskbarPath;
381 				if (find_directory(B_SYSTEM_DIRECTORY, &deskbarPath) == B_OK
382 					&& deskbarPath.Append("Deskbar") == B_OK) {
383 					launch(kDeskbarSig, deskbarPath.Path());
384 				}
385 			}
386 			BDeskbar deskbar;
387 			if (gInDeskbar || deskbar.HasItem (kDeskbarItemName))
388 				deskbar.RemoveItem (kDeskbarItemName);
389 			else
390 				move_to_deskbar(deskbar);
391 			break;
392 		}
393 
394 		case 'CPU ':
395 		{
396 			int32 cpu;
397 			if (message->FindInt32 ("cpu", &cpu) == B_OK) {
398 				bool last = true;
399 				for (int p = 0; p < gCPUcount; p++) {
400 					if (p != cpu && _kern_cpu_enabled(p)) {
401 						last = false;
402 						break;
403 					}
404 				}
405 				if (last) {
406 					alert = new BAlert(B_TRANSLATE("Info"),
407 						B_TRANSLATE("This is the last active processor...\n"
408 						"You can't turn it off!"),
409 						B_TRANSLATE("That's no Fun!"), NULL, NULL,
410 						B_WIDTH_AS_USUAL, B_WARNING_ALERT);
411 					alert->SetShortcut(0, B_ESCAPE);
412 					alert->Go();
413 				} else
414 					_kern_set_cpu_enabled(cpu, !_kern_cpu_enabled(cpu));
415 			}
416 			break;
417 		}
418 
419 		case B_ABOUT_REQUESTED:
420 			AboutRequested();
421 			break;
422 
423 		default:
424 			BView::MessageReceived(message);
425 	}
426 }
427 
428 
429 void
430 ProcessController::AboutRequested()
431 {
432 	const char* authors[] = {
433 		"Georges-Edouard Berenger",
434 		NULL
435 	};
436 
437 	BAboutWindow about(B_TRANSLATE_SYSTEM_NAME("ProcessController"), 2007, authors,
438 		"Copyright 1997-2001\n"
439 		"Georges-Edouard Berenger.");
440 	about.Show();
441 }
442 
443 
444 void
445 ProcessController::DefaultColors()
446 {
447 	swap_color.red = 203;
448 	swap_color.green = 0;
449 	swap_color.blue = 0;
450 	swap_color.alpha = 255;
451 	bool set = false;
452 
453 	if (!set) {
454 		active_color = kKernelBlue;
455 		active_color = tint_color (active_color, B_LIGHTEN_2_TINT);
456 		idle_color = active_color;
457 		idle_color.green /= 3;
458 		idle_color.red /= 3;
459 		idle_color.blue /= 3;
460 		frame_color = kBlack;
461 		mix_colors (memory_color, active_color, swap_color, 0.2);
462 	}
463 }
464 
465 
466 void
467 ProcessController::AttachedToWindow()
468 {
469 	BView::AttachedToWindow();
470 	if (Parent())
471 		SetViewColor(B_TRANSPARENT_COLOR);
472 	else
473 		SetViewColor(kBlack);
474 
475 	Preferences tPreferences(kPreferencesFileName, NULL, false);
476 	DefaultColors();
477 
478 	system_info info;
479 	get_system_info(&info);
480 	gCPUcount = info.cpu_count;
481 	Update();
482 
483 	gIdleColor = kIdleGreen;
484 	gIdleColorSelected = tint_color(gIdleColor, B_HIGHLIGHT_BACKGROUND_TINT);
485 	gKernelColor = kKernelBlue;
486 	gKernelColorSelected = tint_color(gKernelColor, B_HIGHLIGHT_BACKGROUND_TINT);
487 	gUserColor = tint_color(gKernelColor, B_LIGHTEN_2_TINT);
488 	gUserColorSelected = tint_color(gUserColor, B_HIGHLIGHT_BACKGROUND_TINT);
489 	gFrameColor = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
490 		B_HIGHLIGHT_BACKGROUND_TINT);
491 	gFrameColorSelected = tint_color(gFrameColor, B_HIGHLIGHT_BACKGROUND_TINT);
492 	gMenuBackColor = ui_color(B_MENU_BACKGROUND_COLOR);
493 	gMenuBackColorSelected = ui_color(B_MENU_SELECTION_BACKGROUND_COLOR);
494 	gWhiteSelected = tint_color(kWhite, B_HIGHLIGHT_BACKGROUND_TINT);
495 
496 	BMessenger messenger(this);
497 	BMessage message('Puls');
498 	fMessageRunner = new BMessageRunner(messenger, &message, 250000, -1);
499 }
500 
501 
502 
503 void
504 ProcessController::MouseDown(BPoint where)
505 {
506 	if (atomic_add(&gPopupFlag, 1) > 0) {
507 		atomic_add(&gPopupFlag, -1);
508 		return;
509 	}
510 
511 	Tpopup_param* param = new Tpopup_param;
512 	ConvertToScreen(&where);
513 	param->where = where;
514 	param->clickToOpenRect = Frame ();
515 	ConvertToScreen (&param->clickToOpenRect);
516 	param->top = where.y < BScreen(this->Window()).Frame().bottom-50;
517 
518 	gPopupThreadID = spawn_thread(thread_popup, "Popup holder thread",
519 		B_URGENT_DISPLAY_PRIORITY, param);
520 	resume_thread(gPopupThreadID);
521 }
522 
523 
524 void
525 ProcessController::Draw(BRect)
526 {
527 	SetDrawingMode(B_OP_COPY);
528 	DoDraw(true);
529 }
530 
531 
532 void
533 ProcessController::DoDraw(bool force)
534 {
535 	BRect bounds(Bounds());
536 
537 	float h = floorf(bounds.Height ()) - 2;
538 	float top = 1, left = 1;
539 	float bottom = top + h;
540 	float barWidth = layout[gCPUcount].cpu_width;
541 	// interspace
542 	float right = left + gCPUcount * (barWidth + layout[gCPUcount].cpu_inter)
543 		- layout[gCPUcount].cpu_inter; // right of CPU frame...
544 	if (force && Parent()) {
545 		SetHighColor(Parent()->ViewColor ());
546 		FillRect(BRect(right + 1, top - 1, right + 2, bottom + 1));
547 	}
548 
549 	if (force) {
550 		SetHighColor(frame_color);
551 		StrokeRect(BRect(left - 1, top - 1, right, bottom + 1));
552 		if (gCPUcount == 2) {
553 			StrokeLine(BPoint(left + barWidth, top), BPoint(left + barWidth,
554 				bottom));
555 		}
556 	}
557 	float leftMem = bounds.Width() - layout[gCPUcount].mem_width;
558 	if (force)
559 		StrokeRect(BRect(leftMem - 1, top - 1,
560 			leftMem + layout[gCPUcount].mem_width, bottom + 1));
561 
562 	for (int x = 0; x < gCPUcount; x++) {
563 		right = left + barWidth - 1;
564 		float rem = fCPUTimes[x] * (h + 1);
565 		float barHeight = floorf (rem);
566 		rem -= barHeight;
567 		float limit = bottom - barHeight;	// horizontal line
568 		float previousLimit = bottom - fLastBarHeight[x];
569 		float idleTop = top;
570 
571 		if (!force && previousLimit > top)
572 			idleTop = previousLimit - 1;
573 		if (limit > idleTop) {
574 			SetHighColor(idle_color);
575 			FillRect(BRect(left, idleTop, right, limit - 1));
576 		}
577 		if (barHeight <= h) {
578 			rgb_color fraction_color;
579 			mix_colors(fraction_color, idle_color, active_color, rem);
580 			SetHighColor(fraction_color);
581 			StrokeLine(BPoint(left, bottom - barHeight), BPoint(right,
582 				bottom - barHeight));
583 		}
584 		float active_bottom = bottom;
585 		if (!force && previousLimit < bottom)
586 			active_bottom = previousLimit + 1;
587 		if (limit < active_bottom) {
588 			SetHighColor(active_color);
589 			FillRect(BRect(left, limit + 1, right, active_bottom));
590 		}
591 		left += layout[gCPUcount].cpu_width + layout[gCPUcount].cpu_inter;
592 		fLastBarHeight[x] = barHeight;
593 	}
594 
595 	float rightMem = bounds.Width () - 1;
596 	float rem = fMemoryUsage * (h + 1);
597 	float barHeight = floorf (rem);
598 	rem -= barHeight;
599 
600 	rgb_color used_memory_color;
601 	float sq = fMemoryUsage * fMemoryUsage;
602 	sq *= sq;
603 	sq *= sq;
604 	mix_colors(used_memory_color, memory_color, swap_color, sq);
605 
606 	float limit = bottom - barHeight;	// horizontal line
607 	float previousLimit = bottom - fLastMemoryHeight;
608 	float free_top = top;
609 	if (!force && previousLimit > top)
610 		free_top = previousLimit - 1;
611 	if (limit > free_top) {
612 		SetHighColor (idle_color);
613 		FillRect(BRect(leftMem, free_top, rightMem, limit - 1));
614 	}
615 	if (barHeight <= h) {
616 		rgb_color fraction_color;
617 		mix_colors(fraction_color, idle_color, used_memory_color, rem);
618 		SetHighColor(fraction_color);
619 		StrokeLine(BPoint(leftMem, bottom - barHeight), BPoint(rightMem,
620 			bottom - barHeight));
621 	}
622 	float usedBottom = bottom;
623 //	if (!force && previousLimit < bottom)
624 //		usedBottom = previousLimit + 1;
625 	if (limit < usedBottom) {
626 		SetHighColor(used_memory_color);
627 		FillRect(BRect(leftMem, limit + 1, rightMem, usedBottom));
628 	}
629 	fLastMemoryHeight = barHeight;
630 }
631 
632 
633 void
634 ProcessController::Update()
635 {
636 	system_info info;
637 	get_system_info(&info);
638 	bigtime_t now = system_time();
639 
640 	fMemoryUsage = float(info.used_pages) / float(info.max_pages);
641 	// Calculate work done since last call to Update() for each CPU
642 	for (int x = 0; x < gCPUcount; x++) {
643 		bigtime_t load = info.cpu_infos[x].active_time - fPrevActive[x];
644 		bigtime_t passed = now - fPrevTime;
645 		float cpuTime = float(load) / float(passed);
646 
647 		fPrevActive[x] = info.cpu_infos[x].active_time;
648 		if (load > passed)
649 			fPrevActive[x] -= load - passed; // save overload for next period...
650 		if (cpuTime < 0)
651 			cpuTime = 0;
652 		if (cpuTime > 1)
653 			cpuTime = 1;
654 		fCPUTimes[x] = cpuTime;
655 	}
656 	fPrevTime = now;
657 }
658 
659 
660 //	#pragma mark -
661 
662 
663 long
664 thread_popup(void *arg)
665 {
666 	Tpopup_param* param = (Tpopup_param*) arg;
667 	int32 mcookie, hcookie;
668 	long m, h;
669 	BMenuItem* item;
670 	bool top = param->top;
671 
672 	system_info systemInfo;
673 	get_system_info(&systemInfo);
674 	info_pack* infos = new info_pack[systemInfo.used_teams];
675 	// TODO: this doesn't necessarily get all teams
676 	for (m = 0, mcookie = 0; m < systemInfo.used_teams; m++) {
677 		infos[m].team_icon = NULL;
678 		infos[m].team_name[0] = 0;
679 		infos[m].thread_info = NULL;
680 		if (get_next_team_info(&mcookie, &infos[m].team_info) == B_OK) {
681 			infos[m].thread_info = new thread_info[infos[m].team_info.thread_count];
682 			for (h = 0, hcookie = 0; h < infos[m].team_info.thread_count; h++) {
683 				if (get_next_thread_info(infos[m].team_info.team, &hcookie,
684 						&infos[m].thread_info[h]) != B_OK)
685 					infos[m].thread_info[h].thread = -1;
686 			}
687 			get_team_name_and_icon(infos[m], true);
688 		} else {
689 			systemInfo.used_teams = m;
690 			infos[m].team_info.team = -1;
691 		}
692 	}
693 
694 	BPopUpMenu* popup = new BPopUpMenu("Global Popup", false, false);
695 	popup->SetFont(be_plain_font);
696 
697 	// Quit section
698 	BMenu* QuitPopup = new QuitMenu(B_TRANSLATE("Quit an application"),
699 	infos, systemInfo.used_teams);
700 	QuitPopup->SetFont(be_plain_font);
701 	popup->AddItem(QuitPopup);
702 
703 	// Memory Usage section
704 	MemoryBarMenu* MemoryPopup = new MemoryBarMenu(B_TRANSLATE("Memory usage"),
705 	infos, systemInfo);
706 	int commitedMemory = int(systemInfo.used_pages * B_PAGE_SIZE / 1024);
707 	for (m = 0; m < systemInfo.used_teams; m++) {
708 		if (infos[m].team_info.team >= 0) {
709 			MemoryBarMenuItem* memoryItem =
710 				new MemoryBarMenuItem(infos[m].team_name,
711 					infos[m].team_info.team, infos[m].team_icon, false, NULL);
712 			MemoryPopup->AddItem(memoryItem);
713 			memoryItem->UpdateSituation(commitedMemory);
714 		}
715 	}
716 
717 	addtopbottom(MemoryPopup);
718 
719 	// CPU Load section
720 	TeamBarMenu* CPUPopup = new TeamBarMenu(B_TRANSLATE("Threads and CPU "
721 	"usage"), infos, systemInfo.used_teams);
722 	for (m = 0; m < systemInfo.used_teams; m++) {
723 		if (infos[m].team_info.team >= 0) {
724 			ThreadBarMenu* TeamPopup = new ThreadBarMenu(infos[m].team_name,
725 				infos[m].team_info.team, infos[m].team_info.thread_count);
726 			BMessage* kill_team = new BMessage('KlTm');
727 			kill_team->AddInt32("team", infos[m].team_info.team);
728 			TeamBarMenuItem* item = new TeamBarMenuItem(TeamPopup, kill_team,
729 				infos[m].team_info.team, infos[m].team_icon, false);
730 			item->SetTarget(gPCView);
731 			CPUPopup->AddItem(item);
732 		}
733 	}
734 
735 	addtopbottom(CPUPopup);
736 	addtopbottom(new BSeparatorItem());
737 
738 	// CPU on/off section
739 	if (gCPUcount > 1) {
740 		for (int i = 0; i < gCPUcount; i++) {
741 			char item_name[32];
742 			sprintf (item_name, B_TRANSLATE("Processor %d"), i + 1);
743 			BMessage* m = new BMessage ('CPU ');
744 			m->AddInt32 ("cpu", i);
745 			item = new IconMenuItem (gPCView->fProcessorIcon, item_name, m);
746 			if (_kern_cpu_enabled(i))
747 				item->SetMarked (true);
748 			item->SetTarget(gPCView);
749 			addtopbottom(item);
750 		}
751 		addtopbottom (new BSeparatorItem ());
752 	}
753 
754 	if (!be_roster->IsRunning(kTrackerSig)) {
755 		item = new IconMenuItem(gPCView->fTrackerIcon,
756 		B_TRANSLATE("Restart Tracker"), new BMessage('Trac'));
757 		item->SetTarget(gPCView);
758 		addtopbottom(item);
759 	}
760 	if (!be_roster->IsRunning(kDeskbarSig)) {
761 		item = new IconMenuItem(gPCView->fDeskbarIcon,
762 		B_TRANSLATE("Restart Deskbar"), new BMessage('Dbar'));
763 		item->SetTarget(gPCView);
764 		addtopbottom(item);
765 	}
766 
767 	item = new IconMenuItem(gPCView->fTerminalIcon,
768 	B_TRANSLATE("New Terminal"), new BMessage('Term'));
769 	item->SetTarget(gPCView);
770 	addtopbottom(item);
771 
772 	addtopbottom(new BSeparatorItem());
773 
774 	if (be_roster->IsRunning(kDeskbarSig)) {
775 		item = new BMenuItem(B_TRANSLATE("Live in the Deskbar"),
776 		new BMessage('AlDb'));
777 		BDeskbar deskbar;
778 		item->SetMarked(gInDeskbar || deskbar.HasItem(kDeskbarItemName));
779 		item->SetTarget(gPCView);
780 		addtopbottom(item);
781 		addtopbottom(new BSeparatorItem ());
782 	}
783 
784 	item = new IconMenuItem(gPCView->fProcessControllerIcon,
785 	B_TRANSLATE("About ProcessController"B_UTF8_ELLIPSIS),
786 		new BMessage(B_ABOUT_REQUESTED));
787 	item->SetTarget(gPCView);
788 	addtopbottom(item);
789 
790 	param->where.x -= 5;
791 	param->where.y -= 8;
792 	popup->Go(param->where, true, true, param->clickToOpenRect);
793 
794 	delete popup;
795 	for (m = 0; m < systemInfo.used_teams; m++) {
796 		if (infos[m].team_info.team >= 0) {
797 			delete[] infos[m].thread_info;
798 			delete infos[m].team_icon;
799 		}
800 	}
801 	delete[] infos;
802 	delete param;
803 	atomic_add (&gPopupFlag, -1);
804 	gPopupThreadID = 0;
805 
806 	return B_OK;
807 }
808 
809 
810 long
811 thread_quit_application(void *arg)
812 {
813 	BMessenger messenger(NULL, (team_id)arg);
814 	messenger.SendMessage(B_QUIT_REQUESTED);
815 	return B_OK;
816 }
817 
818 
819 long
820 thread_debug_thread(void *arg)
821 {
822 	Tdebug_thead_param*	param = (Tdebug_thead_param*) arg;
823 #ifdef __HAIKU__
824 	debug_thread(param->thread);
825 #else	// !__HAIKU__
826 	thread_info	thinfo;
827 	get_thread_info(param->thread, &thinfo);
828 	char text[4096];
829 	sprintf(text, "db %d", int(param->thread));
830 	system(text);
831 	if (param->sem >= 0 && thinfo.state == B_THREAD_WAITING && param->sem
832 			== thinfo.sem) {
833 		snooze(1000000);
834 		get_thread_info(param->thread, &thinfo);
835 		if (thinfo.state == B_THREAD_WAITING
836 			&& param->sem == thinfo.sem
837 			&& param->totalTime == thinfo.user_time + thinfo.kernel_time) {
838 			// the thread has been waiting for this semaphore since the before
839 			// the alert, not doing anything... Let's push it out of there!
840 			sem_info sinfo;
841 			thread_info thinfo;
842 			info_pack infos;
843 
844 			if (get_sem_info(param->sem, &sinfo) == B_OK
845 				&& get_thread_info(param->thread, &thinfo) == B_OK
846 				&& get_team_info(thinfo.team, &infos.team_info) == B_OK) {
847 				sprintf (text, "This thread is waiting for the "
848 					"semaphore called \"%s\". As long as it waits for this "
849 					"semaphore, you won't be able to debug that thread.\n",
850 						sinfo.name);
851 				if (sinfo.team == thinfo.team)
852 					strcat(text, "This semaphore belongs to the "
853 						"thread's team.\n\nShould I release this semaphore?\n");
854 				else {
855 					get_team_name_and_icon(infos);
856 					char moreText[1024];
857 					sprintf(moreText, "\nWARNING! This semaphore "
858 						"belongs to the team \"%s\"!\n\nShould I release this "
859 						"semaphore anyway?\n",
860 						infos.team_name);
861 					strcat(text, moreText);
862 				}
863 
864 				BAlert* alert = new BAlert("", text, "Cancel", "Release",
865 						NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
866 				alert->SetShortcut(0, B_ESCAPE);
867 				if (alert->Go()) {
868 					get_thread_info (param->thread, &thinfo);
869 					if (thinfo.state == B_THREAD_WAITING && param->sem
870 							== thinfo.sem
871 						&& param->totalTime == thinfo.user_time
872 							+ thinfo.kernel_time)
873 						release_sem(param->sem);
874 					else {
875 						alert = new BAlert("", "The semaphore wasn't released, "
876 							"because it wasn't necessary anymore!",
877 							"OK", NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
878 						alert->Go();
879 					}
880 				}
881 			}
882 		}
883 	}
884 #endif	// !__HAIKU__
885 	delete param;
886 	return B_OK;
887 }
888