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