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