1/*
2 * Copyright 2000, Georges-Edouard Berenger. All rights reserved.
3 * Copyright 2006-2018, Haiku, Inc. All rights reserved.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include "ProcessController.h"
9
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13
14#include <AboutWindow.h>
15#include <Alert.h>
16#include <Bitmap.h>
17#include <Catalog.h>
18#include <debugger.h>
19#include <Deskbar.h>
20#include <Directory.h>
21#include <Dragger.h>
22#include <File.h>
23#include <FindDirectory.h>
24#include <MessageRunner.h>
25#include <Path.h>
26#include <PopUpMenu.h>
27#include <Roster.h>
28#include <Screen.h>
29#include <TextView.h>
30
31#include <scheduler.h>
32#include <syscalls.h>
33
34#include "AutoIcon.h"
35#include "Colors.h"
36#include "IconMenuItem.h"
37#include "MemoryBarMenu.h"
38#include "MemoryBarMenuItem.h"
39#include "PCWorld.h"
40#include "Preferences.h"
41#include "QuitMenu.h"
42#include "TeamBarMenu.h"
43#include "TeamBarMenuItem.h"
44#include "ThreadBarMenu.h"
45#include "Utilities.h"
46
47
48#undef B_TRANSLATION_CONTEXT
49#define B_TRANSLATION_CONTEXT "ProcessController"
50
51
52const char* kDeskbarItemName = "ProcessController";
53const char* kClassName = "ProcessController";
54
55const char* kFrameColorPref = "deskbar_frame_color";
56const char* kIdleColorPref = "deskbar_idle_color";
57const char* kActiveColorPref = "deskbar_active_color";
58
59static const char* const kDebuggerSignature
60	= "application/x-vnd.Haiku-Debugger";
61
62const rgb_color kKernelBlue = {20, 20, 231,	255};
63const rgb_color kIdleGreen = {110, 190,110,	255};
64
65ProcessController* gPCView;
66uint32 gCPUcount;
67rgb_color gUserColor;
68rgb_color gUserColorSelected;
69rgb_color gIdleColor;
70rgb_color gIdleColorSelected;
71rgb_color gKernelColor;
72rgb_color gKernelColorSelected;
73rgb_color gFrameColor;
74rgb_color gFrameColorSelected;
75rgb_color gMenuBackColorSelected;
76rgb_color gMenuBackColor;
77rgb_color gWhiteSelected;
78ThreadBarMenu* gCurrentThreadBarMenu;
79bool gInDeskbar = false;
80
81#define addtopbottom(x) if (top) popup->AddItem(x); else popup->AddItem(x, 0)
82
83status_t thread_popup(void *arg);
84
85int32			gPopupFlag = 0;
86thread_id		gPopupThreadID = 0;
87
88typedef struct {
89	BPoint		where;
90	BRect		clickToOpenRect;
91	bool		top;
92} Tpopup_param;
93
94#define DEBUG_THREADS 1
95
96status_t thread_quit_application(void *arg);
97status_t thread_debug_thread(void *arg);
98
99typedef struct {
100	thread_id	thread;
101	sem_id		sem;
102	time_t		totalTime;
103} Tdebug_thead_param;
104
105// Bar layout depending on number of CPUs
106// This is used only in case the replicant width is 16
107
108typedef struct {
109	float	cpu_width;
110	float	cpu_inter;
111	float	mem_width;
112} layoutT;
113
114layoutT layout[] = {
115	{ 1, 1, 1 },
116	{ 5, 1, 5 },	// 1
117	{ 3, 1, 4 },	// 2
118	{ 2, 1, 3 },
119	{ 2, 0, 3 },	// 4
120};
121
122
123extern "C" _EXPORT BView* instantiate_deskbar_item(float maxWidth,
124	float maxHeight);
125
126extern "C" _EXPORT BView*
127instantiate_deskbar_item(float maxWidth, float maxHeight)
128{
129	gInDeskbar = true;
130
131	system_info info;
132	get_system_info(&info);
133	int width = 4;
134	if (info.cpu_count > 4)
135		width = info.cpu_count;
136	if (info.cpu_count <= 16)
137		width *= 2;
138
139	// For the memory bar
140	width += 8;
141
142	// Damn, you got a lot of CPU
143	if (width > maxWidth)
144		width = (int)maxWidth;
145
146	return new ProcessController(width - 1, maxHeight - 1);
147}
148
149
150//	#pragma mark -
151
152
153ProcessController::ProcessController(BRect frame, bool temp)
154	: BView(frame, kDeskbarItemName, B_FOLLOW_TOP_BOTTOM,
155		B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
156	fProcessControllerIcon(kSignature),
157	fProcessorIcon(k_cpu_mini),
158	fTrackerIcon(kTrackerSig),
159	fDeskbarIcon(kDeskbarSig),
160	fTerminalIcon(kTerminalSig),
161	kCPUCount(sysconf(_SC_NPROCESSORS_CONF)),
162	fTemp(temp),
163	fLastBarHeight(new float[kCPUCount]),
164	fCPUTimes(new double[kCPUCount]),
165	fPrevActive(new bigtime_t[kCPUCount])
166{
167	if (!temp) {
168		Init();
169
170		frame.OffsetTo(B_ORIGIN);
171		frame.top = frame.bottom - 7;
172		frame.left = frame.right - 7;
173		BDragger* dragger = new BDragger(frame, this, B_FOLLOW_BOTTOM);
174		AddChild(dragger);
175	}
176}
177
178ProcessController::ProcessController(BMessage *data)
179	: BView(data),
180	fProcessControllerIcon(kSignature),
181	fProcessorIcon(k_cpu_mini),
182	fTrackerIcon(kTrackerSig),
183	fDeskbarIcon(kDeskbarSig),
184	fTerminalIcon(kTerminalSig),
185	kCPUCount(sysconf(_SC_NPROCESSORS_CONF)),
186	fTemp(false),
187	fLastBarHeight(new float[kCPUCount]),
188	fCPUTimes(new double[kCPUCount]),
189	fPrevActive(new bigtime_t[kCPUCount])
190{
191	Init();
192}
193
194
195ProcessController::ProcessController(float width, float height)
196	:
197	BView(BRect (0, 0, width, height), kDeskbarItemName, B_FOLLOW_NONE,
198		B_WILL_DRAW),
199	fProcessControllerIcon(kSignature),
200	fProcessorIcon(k_cpu_mini),
201	fTrackerIcon(kTrackerSig),
202	fDeskbarIcon(kDeskbarSig),
203	fTerminalIcon(kTerminalSig),
204	kCPUCount(sysconf(_SC_NPROCESSORS_CONF)),
205	fTemp(false),
206	fLastBarHeight(new float[kCPUCount]),
207	fCPUTimes(new double[kCPUCount]),
208	fPrevActive(new bigtime_t[kCPUCount])
209{
210	Init();
211}
212
213
214ProcessController::~ProcessController()
215{
216	if (!fTemp) {
217		if (gPopupThreadID) {
218			status_t return_value;
219			wait_for_thread (gPopupThreadID, &return_value);
220		}
221	}
222
223	delete fMessageRunner;
224	gPCView = NULL;
225
226	delete[] fPrevActive;
227	delete[] fCPUTimes;
228	delete[] fLastBarHeight;
229}
230
231
232void
233ProcessController::Init()
234{
235	memset(fLastBarHeight, 0, sizeof(float) * kCPUCount);
236	memset(fCPUTimes, 0, sizeof(double) * kCPUCount);
237	memset(fPrevActive, 0, sizeof(bigtime_t) * kCPUCount);
238
239	gPCView = this;
240	fMessageRunner = NULL;
241	fLastMemoryHeight = 0;
242	fPrevTime = 0;
243}
244
245
246void
247ProcessController::_HandleDebugRequest(team_id team, thread_id thread)
248{
249	char paramString[16];
250	char idString[16];
251	strlcpy(paramString, thread > 0 ? "--thread" : "--team",
252		sizeof(paramString));
253	snprintf(idString, sizeof(idString), "%" B_PRId32,
254		thread > 0 ? thread : team);
255
256	const char* argv[] = {paramString, idString, NULL};
257	status_t error = be_roster->Launch(kDebuggerSignature, 2, argv);
258	if (error != B_OK) {
259		// TODO: notify user
260	}
261}
262
263
264ProcessController*
265ProcessController::Instantiate(BMessage *data)
266{
267	if (!validate_instantiation(data, kClassName))
268		return NULL;
269
270	return new ProcessController(data);
271}
272
273
274status_t
275ProcessController::Archive(BMessage *data, bool deep) const
276{
277	BView::Archive(data, deep);
278	data->AddString("add_on", kSignature);
279	data->AddString("class", kClassName);
280	return B_OK;
281}
282
283
284void
285ProcessController::MessageReceived(BMessage *message)
286{
287	team_id team;
288	thread_id thread;
289	BAlert *alert;
290	char	question[1000];
291	switch (message->what) {
292		case 'Puls':
293			Update ();
294			DoDraw (false);
295			break;
296
297		case 'QtTm':
298			if (message->FindInt32("team", &team) == B_OK) {
299				resume_thread(spawn_thread(thread_quit_application,
300					B_TRANSLATE("Quit application"), B_NORMAL_PRIORITY,
301					(void*)(addr_t)team));
302			}
303			break;
304
305		case 'KlTm':
306			if (message->FindInt32("team", &team) == B_OK) {
307				info_pack infos;
308				if (get_team_info(team, &infos.team_info) == B_OK) {
309					get_team_name_and_icon(infos);
310					snprintf(question, sizeof(question),
311					B_TRANSLATE("What do you want to do with the team \"%s\"?"),
312					infos.team_name);
313					alert = new BAlert(B_TRANSLATE("Please confirm"), question,
314					B_TRANSLATE("Cancel"), B_TRANSLATE("Debug this team!"),
315					B_TRANSLATE("Kill this team!"), B_WIDTH_AS_USUAL,
316					B_STOP_ALERT);
317					alert->SetShortcut(0, B_ESCAPE);
318					int result = alert->Go();
319					switch (result) {
320						case 1:
321							_HandleDebugRequest(team, -1);
322							break;
323						case 2:
324							kill_team(team);
325							break;
326						default:
327							break;
328					}
329				} else {
330					alert = new BAlert(B_TRANSLATE("Info"),
331						B_TRANSLATE("This team is already gone" B_UTF8_ELLIPSIS),
332						B_TRANSLATE("Ok!"), NULL, NULL, B_WIDTH_AS_USUAL,
333						B_STOP_ALERT);
334					alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
335					alert->Go();
336				}
337			}
338			break;
339
340		case 'KlTh':
341			if (message->FindInt32("thread", &thread) == B_OK) {
342				thread_info	thinfo;
343				if (get_thread_info(thread, &thinfo) == B_OK) {
344					#if DEBUG_THREADS
345					snprintf(question, sizeof(question),
346						B_TRANSLATE("What do you want to do "
347						"with the thread \"%s\"?"), thinfo.name);
348					alert = new BAlert(B_TRANSLATE("Please confirm"), question,
349						B_TRANSLATE("Cancel"), B_TRANSLATE("Debug this thread!"),
350						B_TRANSLATE("Kill this thread!"), B_WIDTH_AS_USUAL,
351						B_STOP_ALERT);
352					alert->SetShortcut(0, B_ESCAPE);
353
354					#define KILL 2
355					#else
356					snprintf(question, sizeof(question),
357						B_TRANSLATE("Are you sure you want "
358						"to kill the thread \"%s\"?"), thinfo.name);
359					alert = new BAlert(B_TRANSLATE("Please confirm"), question,
360						B_TRANSLATE("Cancel"), B_TRANSLATE("Kill this thread!"),
361						NULL, B_WIDTH_AS_USUAL,	B_STOP_ALERT);
362					alert->SetShortcut(0, B_ESCAPE);
363
364					#define KILL 1
365					#endif
366					alert->SetShortcut(0, B_ESCAPE);
367					int r = alert->Go();
368					if (r == KILL)
369						kill_thread(thread);
370					#if DEBUG_THREADS
371					else if (r == 1)
372						_HandleDebugRequest(thinfo.team, thinfo.thread);
373					#endif
374				} else {
375					alert = new BAlert(B_TRANSLATE("Info"),
376						B_TRANSLATE("This thread is already gone" B_UTF8_ELLIPSIS),
377						B_TRANSLATE("Ok!"),	NULL, NULL,
378						B_WIDTH_AS_USUAL, B_STOP_ALERT);
379					alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
380					alert->Go();
381				}
382			}
383			break;
384
385		case 'PrTh':
386			if (message->FindInt32("thread", &thread) == B_OK) {
387				int32 new_priority;
388				if (message->FindInt32("priority", &new_priority) == B_OK)
389					set_thread_priority(thread, new_priority);
390			}
391			break;
392
393		case 'Trac':
394		{
395			BPath trackerPath;
396			if (find_directory(B_SYSTEM_DIRECTORY, &trackerPath) == B_OK
397				&& trackerPath.Append("Tracker") == B_OK) {
398				launch(kTrackerSig, trackerPath.Path());
399			}
400			break;
401		}
402
403		case 'Dbar':
404		{
405			BPath deskbarPath;
406			if (find_directory(B_SYSTEM_DIRECTORY, &deskbarPath) == B_OK
407				&& deskbarPath.Append("Deskbar") == B_OK) {
408				launch(kDeskbarSig, deskbarPath.Path());
409			}
410			break;
411		}
412
413		case 'Term':
414		{
415			BPath terminalPath;
416			if (find_directory(B_SYSTEM_APPS_DIRECTORY, &terminalPath) == B_OK
417				&& terminalPath.Append("Terminal") == B_OK) {
418				launch(kTerminalSig, terminalPath.Path());
419			}
420			break;
421		}
422
423		case 'AlDb':
424		{
425			if (!be_roster->IsRunning(kDeskbarSig)) {
426				BPath deskbarPath;
427				if (find_directory(B_SYSTEM_DIRECTORY, &deskbarPath) == B_OK
428					&& deskbarPath.Append("Deskbar") == B_OK) {
429					launch(kDeskbarSig, deskbarPath.Path());
430				}
431			}
432			BDeskbar deskbar;
433			if (gInDeskbar || deskbar.HasItem (kDeskbarItemName))
434				deskbar.RemoveItem (kDeskbarItemName);
435			else
436				move_to_deskbar(deskbar);
437			break;
438		}
439
440		case 'CPU ':
441		{
442			uint32 cpu;
443			if (message->FindInt32("cpu", (int32*)&cpu) == B_OK) {
444				bool last = true;
445				for (unsigned int p = 0; p < gCPUcount; p++) {
446					if (p != cpu && _kern_cpu_enabled(p)) {
447						last = false;
448						break;
449					}
450				}
451				if (last) {
452					alert = new BAlert(B_TRANSLATE("Info"),
453						B_TRANSLATE("This is the last active processor"
454						B_UTF8_ELLIPSIS "\n"
455						"You can't turn it off!"),
456						B_TRANSLATE("That's no Fun!"), NULL, NULL,
457						B_WIDTH_AS_USUAL, B_WARNING_ALERT);
458					alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
459					alert->Go();
460				} else
461					_kern_set_cpu_enabled(cpu, !_kern_cpu_enabled(cpu));
462			}
463			break;
464		}
465
466		case 'Schd':
467		{
468			BMenuItem* source;
469			if (message->FindPointer("source", (void**)&source) != B_OK)
470				break;
471			if (!source->IsMarked())
472				set_scheduler_mode(SCHEDULER_MODE_POWER_SAVING);
473			else
474				set_scheduler_mode(SCHEDULER_MODE_LOW_LATENCY);
475			break;
476		}
477
478		case B_ABOUT_REQUESTED:
479			AboutRequested();
480			break;
481
482		default:
483			BView::MessageReceived(message);
484	}
485}
486
487
488void
489ProcessController::AboutRequested()
490{
491	BAboutWindow* window = new BAboutWindow(
492		B_TRANSLATE_SYSTEM_NAME("ProcessController"), kSignature);
493
494	const char* extraCopyrights[] = {
495		"2004 beunited.org",
496		"1997-2001 Georges-Edouard Berenger",
497		NULL
498	};
499
500	const char* authors[] = {
501		"Georges-Edouard Berenger",
502		NULL
503	};
504
505	window->AddCopyright(2007, "Haiku, Inc.", extraCopyrights);
506	window->AddAuthors(authors);
507
508	window->Show();
509}
510
511
512void
513ProcessController::DefaultColors()
514{
515	swap_color.red = 203;
516	swap_color.green = 0;
517	swap_color.blue = 0;
518	swap_color.alpha = 255;
519	bool set = false;
520
521	if (!set) {
522		active_color = kKernelBlue;
523		active_color = tint_color (active_color, B_LIGHTEN_2_TINT);
524		idle_color = active_color;
525		idle_color.green /= 3;
526		idle_color.red /= 3;
527		idle_color.blue /= 3;
528		frame_color = kBlack;
529		mix_colors (memory_color, active_color, swap_color, 0.2);
530	}
531}
532
533
534void
535ProcessController::AttachedToWindow()
536{
537	BView::AttachedToWindow();
538	if (Parent())
539		SetViewColor(B_TRANSPARENT_COLOR);
540	else
541		SetViewColor(kBlack);
542
543	Preferences tPreferences(kPreferencesFileName, NULL, false);
544	DefaultColors();
545
546	system_info info;
547	get_system_info(&info);
548	gCPUcount = info.cpu_count;
549	Update();
550
551	gIdleColor = kIdleGreen;
552	gIdleColorSelected = tint_color(gIdleColor, B_HIGHLIGHT_BACKGROUND_TINT);
553	gKernelColor = kKernelBlue;
554	gKernelColorSelected = tint_color(gKernelColor, B_HIGHLIGHT_BACKGROUND_TINT);
555	gUserColor = tint_color(gKernelColor, B_LIGHTEN_2_TINT);
556	gUserColorSelected = tint_color(gUserColor, B_HIGHLIGHT_BACKGROUND_TINT);
557	gFrameColor = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
558		B_HIGHLIGHT_BACKGROUND_TINT);
559	gFrameColorSelected = tint_color(gFrameColor, B_HIGHLIGHT_BACKGROUND_TINT);
560	gMenuBackColor = ui_color(B_MENU_BACKGROUND_COLOR);
561	gMenuBackColorSelected = ui_color(B_MENU_SELECTION_BACKGROUND_COLOR);
562	gWhiteSelected = tint_color(kWhite, B_HIGHLIGHT_BACKGROUND_TINT);
563
564	BMessenger messenger(this);
565	BMessage message('Puls');
566	fMessageRunner = new BMessageRunner(messenger, &message, 250000, -1);
567}
568
569
570
571void
572ProcessController::MouseDown(BPoint where)
573{
574	if (atomic_add(&gPopupFlag, 1) > 0) {
575		atomic_add(&gPopupFlag, -1);
576		return;
577	}
578
579	Tpopup_param* param = new Tpopup_param;
580	ConvertToScreen(&where);
581	param->where = where;
582	param->clickToOpenRect = Frame ();
583	ConvertToScreen (&param->clickToOpenRect);
584	param->top = where.y < BScreen(this->Window()).Frame().bottom-50;
585
586	gPopupThreadID = spawn_thread(thread_popup, "Popup holder thread",
587		B_URGENT_DISPLAY_PRIORITY, param);
588	resume_thread(gPopupThreadID);
589}
590
591
592void
593ProcessController::Draw(BRect)
594{
595	SetDrawingMode(B_OP_COPY);
596	DoDraw(true);
597}
598
599
600void
601ProcessController::DoDraw(bool force)
602{
603	BRect bounds(Bounds());
604
605	float h = floorf(bounds.Height ()) - 2;
606	float top = 1, left = 1;
607	float bottom = top + h;
608	float barWidth;
609	float barGap;
610	float memWidth;
611	if (gCPUcount <= 4 && bounds.Width() == 15) {
612		// Use fixed sizes for small CPU counts
613		barWidth = layout[gCPUcount].cpu_width;
614		barGap = layout[gCPUcount].cpu_inter;
615		memWidth = layout[gCPUcount].mem_width;
616	} else {
617		memWidth = floorf((bounds.Height() + 1) / 8);
618		barGap = ((bounds.Width() + 1) / gCPUcount) > 3 ? 1 : 0;
619		barWidth = floorf((bounds.Width() - 1 - memWidth - barGap * gCPUcount)
620			/ gCPUcount);
621	}
622	// interspace
623	float right = left + gCPUcount * (barWidth + barGap) - barGap;
624	float leftMem = bounds.Width() - memWidth;
625		// right of CPU frame...
626	if (force && Parent()) {
627		SetHighColor(Parent()->ViewColor());
628		FillRect(BRect(right + 1, top - 1, leftMem, bottom + 1));
629	}
630
631	if (force) {
632		SetHighColor(frame_color);
633		StrokeRect(BRect(left - 1, top - 1, right, bottom + 1));
634		if (gCPUcount > 1 && barGap == 1) {
635			for (unsigned int x = 1; x < gCPUcount; x++)
636				StrokeLine(BPoint(left + x * barWidth + x - 1, top),
637					BPoint(left + x * barWidth + x - 1, bottom));
638		}
639	}
640
641	if (force)
642		StrokeRect(BRect(leftMem - 1, top - 1, leftMem + memWidth, bottom + 1));
643
644	for (unsigned int x = 0; x < gCPUcount; x++) {
645		right = left + barWidth - 1;
646		float rem = fCPUTimes[x] * (h + 1);
647		float barHeight = floorf (rem);
648		rem -= barHeight;
649		float limit = bottom - barHeight;	// horizontal line
650		float previousLimit = bottom - fLastBarHeight[x];
651		float idleTop = top;
652
653		if (!force && previousLimit > top)
654			idleTop = previousLimit - 1;
655		if (limit > idleTop) {
656			SetHighColor(idle_color);
657			FillRect(BRect(left, idleTop, right, limit - 1));
658		}
659		if (barHeight <= h) {
660			rgb_color fraction_color;
661			mix_colors(fraction_color, idle_color, active_color, rem);
662			SetHighColor(fraction_color);
663			StrokeLine(BPoint(left, bottom - barHeight), BPoint(right,
664				bottom - barHeight));
665		}
666		float active_bottom = bottom;
667		if (!force && previousLimit < bottom)
668			active_bottom = previousLimit + 1;
669		if (limit < active_bottom) {
670			SetHighColor(active_color);
671			FillRect(BRect(left, limit + 1, right, active_bottom));
672		}
673		left += barWidth + barGap;
674		fLastBarHeight[x] = barHeight;
675	}
676
677	float rightMem = bounds.Width() - 1;
678	float rem = fMemoryUsage * (h + 1);
679	float barHeight = floorf(rem);
680	rem -= barHeight;
681
682	rgb_color used_memory_color;
683	float sq = fMemoryUsage * fMemoryUsage;
684	sq *= sq;
685	sq *= sq;
686	mix_colors(used_memory_color, memory_color, swap_color, sq);
687
688	float limit = bottom - barHeight;	// horizontal line
689	float previousLimit = bottom - fLastMemoryHeight;
690	float free_top = top;
691	if (!force && previousLimit > top)
692		free_top = previousLimit - 1;
693	if (limit > free_top) {
694		SetHighColor (idle_color);
695		FillRect(BRect(leftMem, free_top, rightMem, limit - 1));
696	}
697	if (barHeight <= h) {
698		rgb_color fraction_color;
699		mix_colors(fraction_color, idle_color, used_memory_color, rem);
700		SetHighColor(fraction_color);
701		StrokeLine(BPoint(leftMem, bottom - barHeight), BPoint(rightMem,
702			bottom - barHeight));
703	}
704	float usedBottom = bottom;
705//	if (!force && previousLimit < bottom)
706//		usedBottom = previousLimit + 1;
707	if (limit < usedBottom) {
708		SetHighColor(used_memory_color);
709		FillRect(BRect(leftMem, limit + 1, rightMem, usedBottom));
710	}
711	fLastMemoryHeight = barHeight;
712}
713
714
715void
716ProcessController::Update()
717{
718	system_info info;
719	get_system_info(&info);
720	bigtime_t now = system_time();
721
722	cpu_info* cpuInfos = new cpu_info[gCPUcount];
723	get_cpu_info(0, gCPUcount, cpuInfos);
724
725	fMemoryUsage = float(info.used_pages) / float(info.max_pages);
726	// Calculate work done since last call to Update() for each CPU
727	for (unsigned int x = 0; x < gCPUcount; x++) {
728		bigtime_t load = cpuInfos[x].active_time - fPrevActive[x];
729		bigtime_t passed = now - fPrevTime;
730		float cpuTime = float(load) / float(passed);
731
732		fPrevActive[x] = cpuInfos[x].active_time;
733		if (load > passed)
734			fPrevActive[x] -= load - passed; // save overload for next period...
735		if (cpuTime < 0)
736			cpuTime = 0;
737		if (cpuTime > 1)
738			cpuTime = 1;
739		fCPUTimes[x] = cpuTime;
740	}
741	fPrevTime = now;
742
743	delete[] cpuInfos;
744}
745
746
747//	#pragma mark -
748
749
750status_t
751thread_popup(void *arg)
752{
753	Tpopup_param* param = (Tpopup_param*) arg;
754	int32 mcookie, hcookie;
755	unsigned long m;
756	long h;
757	BMenuItem* item;
758	bool top = param->top;
759
760	system_info systemInfo;
761	get_system_info(&systemInfo);
762	info_pack* infos = new info_pack[systemInfo.used_teams];
763	// TODO: this doesn't necessarily get all teams
764	for (m = 0, mcookie = 0; m < systemInfo.used_teams; m++) {
765		infos[m].team_icon = NULL;
766		infos[m].team_name[0] = 0;
767		infos[m].thread_info = NULL;
768		if (get_next_team_info(&mcookie, &infos[m].team_info) == B_OK) {
769			infos[m].thread_info = new thread_info[infos[m].team_info.thread_count];
770			for (h = 0, hcookie = 0; h < infos[m].team_info.thread_count; h++) {
771				if (get_next_thread_info(infos[m].team_info.team, &hcookie,
772						&infos[m].thread_info[h]) != B_OK)
773					infos[m].thread_info[h].thread = -1;
774			}
775			get_team_name_and_icon(infos[m], true);
776		} else {
777			systemInfo.used_teams = m;
778			infos[m].team_info.team = -1;
779		}
780	}
781
782	BPopUpMenu* popup = new BPopUpMenu("Global Popup", false, false);
783	popup->SetFont(be_plain_font);
784
785	// Quit section
786	BMenu* QuitPopup = new QuitMenu(B_TRANSLATE("Quit an application"),
787	infos, systemInfo.used_teams);
788	QuitPopup->SetFont(be_plain_font);
789	popup->AddItem(QuitPopup);
790
791	// Memory Usage section
792	MemoryBarMenu* MemoryPopup = new MemoryBarMenu(B_TRANSLATE("Memory usage"),
793	infos, systemInfo);
794	int64 committedMemory = (int64)systemInfo.used_pages * B_PAGE_SIZE / 1024;
795	for (m = 0; m < systemInfo.used_teams; m++) {
796		if (infos[m].team_info.team >= 0) {
797			MemoryBarMenuItem* memoryItem =
798				new MemoryBarMenuItem(infos[m].team_name,
799					infos[m].team_info.team, infos[m].team_icon, false, NULL);
800			MemoryPopup->AddItem(memoryItem);
801			memoryItem->UpdateSituation(committedMemory);
802		}
803	}
804
805	addtopbottom(MemoryPopup);
806
807	// CPU Load section
808	TeamBarMenu* CPUPopup = new TeamBarMenu(B_TRANSLATE("Threads and CPU "
809	"usage"), infos, systemInfo.used_teams);
810	for (m = 0; m < systemInfo.used_teams; m++) {
811		if (infos[m].team_info.team >= 0) {
812			ThreadBarMenu* TeamPopup = new ThreadBarMenu(infos[m].team_name,
813				infos[m].team_info.team, infos[m].team_info.thread_count);
814			BMessage* kill_team = new BMessage('KlTm');
815			kill_team->AddInt32("team", infos[m].team_info.team);
816			TeamBarMenuItem* item = new TeamBarMenuItem(TeamPopup, kill_team,
817				infos[m].team_info.team, infos[m].team_icon, false);
818			item->SetTarget(gPCView);
819			CPUPopup->AddItem(item);
820		}
821	}
822
823	addtopbottom(CPUPopup);
824	addtopbottom(new BSeparatorItem());
825
826	// CPU on/off section
827	if (gCPUcount > 1) {
828		for (unsigned int i = 0; i < gCPUcount; i++) {
829			char item_name[32];
830			sprintf (item_name, B_TRANSLATE("Processor %d"), i + 1);
831			BMessage* m = new BMessage ('CPU ');
832			m->AddInt32 ("cpu", i);
833			item = new IconMenuItem (gPCView->fProcessorIcon, item_name, m);
834			if (_kern_cpu_enabled(i))
835				item->SetMarked (true);
836			item->SetTarget(gPCView);
837			addtopbottom(item);
838		}
839		addtopbottom (new BSeparatorItem ());
840	}
841
842	// Scheduler modes
843	int32 currentMode = get_scheduler_mode();
844	BMessage* msg = new BMessage('Schd');
845	item = new BMenuItem(B_TRANSLATE("Power saving"), msg);
846	if ((uint32)currentMode == SCHEDULER_MODE_POWER_SAVING)
847		item->SetMarked(true);
848	item->SetTarget(gPCView);
849	addtopbottom(item);
850	addtopbottom(new BSeparatorItem());
851
852	if (!be_roster->IsRunning(kTrackerSig)) {
853		item = new IconMenuItem(gPCView->fTrackerIcon,
854		B_TRANSLATE("Restart Tracker"), new BMessage('Trac'));
855		item->SetTarget(gPCView);
856		addtopbottom(item);
857	}
858	if (!be_roster->IsRunning(kDeskbarSig)) {
859		item = new IconMenuItem(gPCView->fDeskbarIcon,
860		B_TRANSLATE("Restart Deskbar"), new BMessage('Dbar'));
861		item->SetTarget(gPCView);
862		addtopbottom(item);
863	}
864
865	item = new IconMenuItem(gPCView->fTerminalIcon,
866	B_TRANSLATE("New Terminal"), new BMessage('Term'));
867	item->SetTarget(gPCView);
868	addtopbottom(item);
869
870	addtopbottom(new BSeparatorItem());
871
872	bool showLiveInDeskbarItem = gInDeskbar;
873	if (!showLiveInDeskbarItem) {
874		int32 cookie = 0;
875		image_info info;
876		while (get_next_image_info(B_CURRENT_TEAM, &cookie, &info) == B_OK) {
877			if (info.type == B_APP_IMAGE) {
878				// only show the Live in Deskbar item if a) we're running in
879				// deskbar itself, or b) we're running in PC's team.
880				if (strstr(info.name, "ProcessController") != NULL) {
881					showLiveInDeskbarItem = true;
882					break;
883				}
884			}
885		}
886	}
887
888	if (showLiveInDeskbarItem && be_roster->IsRunning(kDeskbarSig)) {
889		item = new BMenuItem(B_TRANSLATE("Live in the Deskbar"),
890			new BMessage('AlDb'));
891		BDeskbar deskbar;
892		item->SetMarked(gInDeskbar || deskbar.HasItem(kDeskbarItemName));
893		item->SetTarget(gPCView);
894		addtopbottom(item);
895		addtopbottom(new BSeparatorItem ());
896	}
897
898
899	item = new IconMenuItem(gPCView->fProcessControllerIcon,
900	B_TRANSLATE("About ProcessController" B_UTF8_ELLIPSIS),
901		new BMessage(B_ABOUT_REQUESTED));
902	item->SetTarget(gPCView);
903	addtopbottom(item);
904
905	param->where.x -= 5;
906	param->where.y -= 8;
907	popup->Go(param->where, true, true, param->clickToOpenRect);
908
909	delete popup;
910	for (m = 0; m < systemInfo.used_teams; m++) {
911		if (infos[m].team_info.team >= 0) {
912			delete[] infos[m].thread_info;
913			delete infos[m].team_icon;
914		}
915	}
916	delete[] infos;
917	delete param;
918	atomic_add (&gPopupFlag, -1);
919	gPopupThreadID = 0;
920
921	return B_OK;
922}
923
924
925status_t
926thread_quit_application(void *arg)
927{
928	BMessenger messenger(NULL, (addr_t)arg);
929	messenger.SendMessage(B_QUIT_REQUESTED);
930	return B_OK;
931}
932
933
934status_t
935thread_debug_thread(void *arg)
936{
937	Tdebug_thead_param*	param = (Tdebug_thead_param*) arg;
938	debug_thread(param->thread);
939	delete param;
940	return B_OK;
941}
942