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