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