⛏️ index : haiku.git

/*
 * Copyright 2000, Georges-Edouard Berenger. All rights reserved.
 * Copyright 2006-2018, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 */


#include "ProcessController.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <AboutWindow.h>
#include <Alert.h>
#include <Bitmap.h>
#include <Catalog.h>
#include <debugger.h>
#include <Deskbar.h>
#include <Directory.h>
#include <Dragger.h>
#include <File.h>
#include <FindDirectory.h>
#include <MessageRunner.h>
#include <Path.h>
#include <PopUpMenu.h>
#include <Roster.h>
#include <Screen.h>
#include <TextView.h>

#include <scheduler.h>
#include <syscalls.h>

#include "AutoIcon.h"
#include "Colors.h"
#include "IconMenuItem.h"
#include "MemoryBarMenu.h"
#include "MemoryBarMenuItem.h"
#include "PCWorld.h"
#include "Preferences.h"
#include "QuitMenu.h"
#include "TeamBarMenu.h"
#include "TeamBarMenuItem.h"
#include "ThreadBarMenu.h"
#include "Utilities.h"


#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "ProcessController"


const char* kDeskbarItemName = "ProcessController";
const char* kClassName = "ProcessController";

const char* kFrameColorPref = "deskbar_frame_color";
const char* kIdleColorPref = "deskbar_idle_color";
const char* kActiveColorPref = "deskbar_active_color";

static const char* const kDebuggerSignature
	= "application/x-vnd.Haiku-Debugger";

const rgb_color kKernelBlue = {20, 20, 231,	255};
const rgb_color kIdleGreen = {110, 190,110,	255};

ProcessController* gPCView;
uint32 gCPUcount;
rgb_color gUserColor;
rgb_color gUserColorSelected;
rgb_color gIdleColor;
rgb_color gIdleColorSelected;
rgb_color gKernelColor;
rgb_color gKernelColorSelected;
rgb_color gFrameColor;
rgb_color gFrameColorSelected;
rgb_color gMenuBackColorSelected;
rgb_color gMenuBackColor;
rgb_color gWhiteSelected;
ThreadBarMenu* gCurrentThreadBarMenu;
bool gInDeskbar = false;

#define addtopbottom(x) if (top) popup->AddItem(x); else popup->AddItem(x, 0)

status_t thread_popup(void *arg);

int32			gPopupFlag = 0;
thread_id		gPopupThreadID = 0;

typedef struct {
	BPoint		where;
	BRect		clickToOpenRect;
	bool		top;
} Tpopup_param;

#define DEBUG_THREADS 1

status_t thread_quit_application(void *arg);
status_t thread_debug_thread(void *arg);

typedef struct {
	thread_id	thread;
	sem_id		sem;
	time_t		totalTime;
} Tdebug_thead_param;

// Bar layout depending on number of CPUs
// This is used only in case the replicant width is 16

typedef struct {
	float	cpu_width;
	float	cpu_inter;
	float	mem_width;
} layoutT;

layoutT layout[] = {
	{ 1, 1, 1 },
	{ 5, 1, 5 },	// 1
	{ 3, 1, 4 },	// 2
	{ 2, 1, 3 },
	{ 2, 0, 3 },	// 4
};


extern "C" _EXPORT BView* instantiate_deskbar_item(float maxWidth,
	float maxHeight);

extern "C" _EXPORT BView*
instantiate_deskbar_item(float maxWidth, float maxHeight)
{
	gInDeskbar = true;

	system_info info;
	get_system_info(&info);
	int width = 4;
	if (info.cpu_count > 4)
		width = info.cpu_count;
	if (info.cpu_count <= 16)
		width *= 2;

	// For the memory bar
	width += 8;

	// Damn, you got a lot of CPU
	if (width > maxWidth)
		width = (int)maxWidth;

	return new ProcessController(width - 1, maxHeight - 1);
}


//	#pragma mark -


ProcessController::ProcessController(BRect frame, bool temp)
	: BView(frame, kDeskbarItemName, B_FOLLOW_TOP_BOTTOM,
		B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
	fProcessControllerIcon(kSignature),
	fProcessorIcon(k_cpu_mini),
	fTrackerIcon(kTrackerSig),
	fDeskbarIcon(kDeskbarSig),
	fTerminalIcon(kTerminalSig),
	kCPUCount(sysconf(_SC_NPROCESSORS_CONF)),
	fTemp(temp),
	fLastBarHeight(new float[kCPUCount]),
	fCPUTimes(new double[kCPUCount]),
	fPrevActive(new bigtime_t[kCPUCount])
{
	if (!temp) {
		Init();

		frame.OffsetTo(B_ORIGIN);
		frame.top = frame.bottom - 7;
		frame.left = frame.right - 7;
		BDragger* dragger = new BDragger(frame, this, B_FOLLOW_BOTTOM);
		AddChild(dragger);
	}
}

ProcessController::ProcessController(BMessage *data)
	: BView(data),
	fProcessControllerIcon(kSignature),
	fProcessorIcon(k_cpu_mini),
	fTrackerIcon(kTrackerSig),
	fDeskbarIcon(kDeskbarSig),
	fTerminalIcon(kTerminalSig),
	kCPUCount(sysconf(_SC_NPROCESSORS_CONF)),
	fTemp(false),
	fLastBarHeight(new float[kCPUCount]),
	fCPUTimes(new double[kCPUCount]),
	fPrevActive(new bigtime_t[kCPUCount])
{
	Init();
}


ProcessController::ProcessController(float width, float height)
	:
	BView(BRect (0, 0, width, height), kDeskbarItemName, B_FOLLOW_NONE,
		B_WILL_DRAW),
	fProcessControllerIcon(kSignature),
	fProcessorIcon(k_cpu_mini),
	fTrackerIcon(kTrackerSig),
	fDeskbarIcon(kDeskbarSig),
	fTerminalIcon(kTerminalSig),
	kCPUCount(sysconf(_SC_NPROCESSORS_CONF)),
	fTemp(false),
	fLastBarHeight(new float[kCPUCount]),
	fCPUTimes(new double[kCPUCount]),
	fPrevActive(new bigtime_t[kCPUCount])
{
	Init();
}


ProcessController::~ProcessController()
{
	if (!fTemp) {
		if (gPopupThreadID) {
			status_t return_value;
			wait_for_thread (gPopupThreadID, &return_value);
		}
	}

	delete fMessageRunner;
	gPCView = NULL;

	delete[] fPrevActive;
	delete[] fCPUTimes;
	delete[] fLastBarHeight;
}


void
ProcessController::Init()
{
	memset(fLastBarHeight, 0, sizeof(float) * kCPUCount);
	memset(fCPUTimes, 0, sizeof(double) * kCPUCount);
	memset(fPrevActive, 0, sizeof(bigtime_t) * kCPUCount);

	gPCView = this;
	fMessageRunner = NULL;
	fLastMemoryHeight = 0;
	fPrevTime = 0;
}


void
ProcessController::_HandleDebugRequest(team_id team, thread_id thread)
{
	char paramString[16];
	char idString[16];
	strlcpy(paramString, thread > 0 ? "--thread" : "--team",
		sizeof(paramString));
	snprintf(idString, sizeof(idString), "%" B_PRId32,
		thread > 0 ? thread : team);

	const char* argv[] = {paramString, idString, NULL};
	status_t error = be_roster->Launch(kDebuggerSignature, 2, argv);
	if (error != B_OK) {
		// TODO: notify user
	}
}


ProcessController*
ProcessController::Instantiate(BMessage *data)
{
	if (!validate_instantiation(data, kClassName))
		return NULL;

	return new ProcessController(data);
}


status_t
ProcessController::Archive(BMessage *data, bool deep) const
{
	BView::Archive(data, deep);
	data->AddString("add_on", kSignature);
	data->AddString("class", kClassName);
	return B_OK;
}


void
ProcessController::MessageReceived(BMessage *message)
{
	team_id team;
	thread_id thread;
	BAlert *alert;
	BString question;

	switch (message->what) {
		case 'Puls':
			Update ();
			DoDraw (false);
			break;

		case 'QtTm':
			if (message->FindInt32("team", &team) == B_OK) {
				resume_thread(spawn_thread(thread_quit_application,
					B_TRANSLATE("Quit application"), B_NORMAL_PRIORITY,
					(void*)(addr_t)team));
			}
			break;

		case 'KlTm':
			if (message->FindInt32("team", &team) == B_OK) {
				info_pack infos;
				if (get_team_info(team, &infos.team_info) == B_OK) {
					get_team_name_and_icon(infos);
					question.SetToFormat(
						B_TRANSLATE("What do you want to do with the team \"%s\"?"),
						infos.team_name);
					alert = new BAlert(B_TRANSLATE("Please confirm"), question,
						B_TRANSLATE("Cancel"), B_TRANSLATE("Debug this team!"),
						B_TRANSLATE("Kill this team!"), B_WIDTH_AS_USUAL,
						B_STOP_ALERT);
					alert->SetShortcut(0, B_ESCAPE);
					int result = alert->Go();
					switch (result) {
						case 1:
							_HandleDebugRequest(team, -1);
							break;
						case 2:
							kill_team(team);
							break;
						default:
							break;
					}
				} else {
					alert = new BAlert(B_TRANSLATE("Info"),
						B_TRANSLATE("This team is already gone" B_UTF8_ELLIPSIS),
						B_TRANSLATE("Ok!"), NULL, NULL, B_WIDTH_AS_USUAL,
						B_STOP_ALERT);
					alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
					alert->Go();
				}
			}
			break;

		case 'KlTh':
			if (message->FindInt32("thread", &thread) == B_OK) {
				thread_info	thinfo;
				if (get_thread_info(thread, &thinfo) == B_OK) {
					#if DEBUG_THREADS
					question.SetToFormat(B_TRANSLATE("What do you want to do "
						"with the thread \"%s\"?"), thinfo.name);
					alert = new BAlert(B_TRANSLATE("Please confirm"), question,
						B_TRANSLATE("Cancel"), B_TRANSLATE("Debug this thread!"),
						B_TRANSLATE("Kill this thread!"), B_WIDTH_AS_USUAL,
						B_STOP_ALERT);
					alert->SetShortcut(0, B_ESCAPE);

					#define KILL 2
					#else
					question.SetToFormat(
						B_TRANSLATE("Are you sure you want "
						"to kill the thread \"%s\"?"), thinfo.name);
					alert = new BAlert(B_TRANSLATE("Please confirm"), question,
						B_TRANSLATE("Cancel"), B_TRANSLATE("Kill this thread!"),
						NULL, B_WIDTH_AS_USUAL,	B_STOP_ALERT);
					alert->SetShortcut(0, B_ESCAPE);

					#define KILL 1
					#endif
					alert->SetShortcut(0, B_ESCAPE);
					int r = alert->Go();
					if (r == KILL)
						kill_thread(thread);
					#if DEBUG_THREADS
					else if (r == 1)
						_HandleDebugRequest(thinfo.team, thinfo.thread);
					#endif
				} else {
					alert = new BAlert(B_TRANSLATE("Info"),
						B_TRANSLATE("This thread is already gone" B_UTF8_ELLIPSIS),
						B_TRANSLATE("Ok!"),	NULL, NULL,
						B_WIDTH_AS_USUAL, B_STOP_ALERT);
					alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
					alert->Go();
				}
			}
			break;

		case 'PrTh':
			if (message->FindInt32("thread", &thread) == B_OK) {
				int32 new_priority;
				if (message->FindInt32("priority", &new_priority) == B_OK)
					set_thread_priority(thread, new_priority);
			}
			break;

		case 'Trac':
		{
			BPath trackerPath;
			if (find_directory(B_SYSTEM_DIRECTORY, &trackerPath) == B_OK
				&& trackerPath.Append("Tracker") == B_OK) {
				launch(kTrackerSig, trackerPath.Path());
			}
			break;
		}

		case 'Dbar':
		{
			BPath deskbarPath;
			if (find_directory(B_SYSTEM_DIRECTORY, &deskbarPath) == B_OK
				&& deskbarPath.Append("Deskbar") == B_OK) {
				launch(kDeskbarSig, deskbarPath.Path());
			}
			break;
		}

		case 'Term':
		{
			BPath terminalPath;
			if (find_directory(B_SYSTEM_APPS_DIRECTORY, &terminalPath) == B_OK
				&& terminalPath.Append("Terminal") == B_OK) {
				launch(kTerminalSig, terminalPath.Path());
			}
			break;
		}

		case 'AlDb':
		{
			if (!be_roster->IsRunning(kDeskbarSig)) {
				BPath deskbarPath;
				if (find_directory(B_SYSTEM_DIRECTORY, &deskbarPath) == B_OK
					&& deskbarPath.Append("Deskbar") == B_OK) {
					launch(kDeskbarSig, deskbarPath.Path());
				}
			}
			BDeskbar deskbar;
			if (gInDeskbar || deskbar.HasItem (kDeskbarItemName))
				deskbar.RemoveItem (kDeskbarItemName);
			else
				move_to_deskbar(deskbar);
			break;
		}

		case 'CPU ':
		{
			uint32 cpu;
			if (message->FindInt32("cpu", (int32*)&cpu) == B_OK) {
				bool last = true;
				for (unsigned int p = 0; p < gCPUcount; p++) {
					if (p != cpu && _kern_cpu_enabled(p)) {
						last = false;
						break;
					}
				}
				if (last) {
					alert = new BAlert(B_TRANSLATE("Info"),
						B_TRANSLATE("This is the last active processor"
						B_UTF8_ELLIPSIS "\n"
						"You can't turn it off!"),
						B_TRANSLATE("That's no Fun!"), NULL, NULL,
						B_WIDTH_AS_USUAL, B_WARNING_ALERT);
					alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
					alert->Go();
				} else
					_kern_set_cpu_enabled(cpu, !_kern_cpu_enabled(cpu));
			}
			break;
		}

		case 'Schd':
		{
			BMenuItem* source;
			if (message->FindPointer("source", (void**)&source) != B_OK)
				break;
			if (!source->IsMarked())
				set_scheduler_mode(SCHEDULER_MODE_POWER_SAVING);
			else
				set_scheduler_mode(SCHEDULER_MODE_LOW_LATENCY);
			Preferences preferences(kPreferencesFileName);
			preferences.SaveInt32(get_scheduler_mode(), "scheduler_mode");
			break;
		}

		case B_ABOUT_REQUESTED:
			AboutRequested();
			break;

		default:
			BView::MessageReceived(message);
	}
}


void
ProcessController::AboutRequested()
{
	BAboutWindow* window = new BAboutWindow(
		B_TRANSLATE_SYSTEM_NAME("ProcessController"), kSignature);

	const char* extraCopyrights[] = {
		"2004 beunited.org",
		"1997-2001 Georges-Edouard Berenger",
		NULL
	};

	const char* authors[] = {
		"Georges-Edouard Berenger",
		NULL
	};

	window->AddCopyright(2007, "Haiku, Inc.", extraCopyrights);
	window->AddAuthors(authors);

	window->Show();
}


void
ProcessController::DefaultColors()
{
	swap_color.red = 203;
	swap_color.green = 0;
	swap_color.blue = 0;
	swap_color.alpha = 255;
	bool set = false;

	if (!set) {
		active_color = kKernelBlue;
		active_color = tint_color (active_color, B_LIGHTEN_2_TINT);
		idle_color = active_color;
		idle_color.green /= 3;
		idle_color.red /= 3;
		idle_color.blue /= 3;
		frame_color = kBlack;
		mix_colors (memory_color, active_color, swap_color, 0.2);
	}
}


void
ProcessController::AttachedToWindow()
{
	BView::AttachedToWindow();
	if (Parent())
		SetViewColor(B_TRANSPARENT_COLOR);
	else
		SetViewColor(kBlack);

	Preferences tPreferences(kPreferencesFileName, NULL, false);
	DefaultColors();

	system_info info;
	get_system_info(&info);
	gCPUcount = info.cpu_count;
	Update();

	gIdleColor = kIdleGreen;
	gIdleColorSelected = tint_color(gIdleColor, B_HIGHLIGHT_BACKGROUND_TINT);
	gKernelColor = kKernelBlue;
	gKernelColorSelected = tint_color(gKernelColor, B_HIGHLIGHT_BACKGROUND_TINT);
	gUserColor = tint_color(gKernelColor, B_LIGHTEN_2_TINT);
	gUserColorSelected = tint_color(gUserColor, B_HIGHLIGHT_BACKGROUND_TINT);
	gFrameColor = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
		B_HIGHLIGHT_BACKGROUND_TINT);
	gFrameColorSelected = tint_color(gFrameColor, B_HIGHLIGHT_BACKGROUND_TINT);
	gMenuBackColor = ui_color(B_MENU_BACKGROUND_COLOR);
	gMenuBackColorSelected = ui_color(B_MENU_SELECTION_BACKGROUND_COLOR);
	gWhiteSelected = tint_color(kWhite, B_HIGHLIGHT_BACKGROUND_TINT);

	BMessenger messenger(this);
	BMessage message('Puls');
	fMessageRunner = new BMessageRunner(messenger, &message, 250000, -1);
}



void
ProcessController::MouseDown(BPoint where)
{
	if (atomic_add(&gPopupFlag, 1) > 0) {
		atomic_add(&gPopupFlag, -1);
		return;
	}

	Tpopup_param* param = new Tpopup_param;
	ConvertToScreen(&where);
	param->where = where;
	param->clickToOpenRect = Frame ();
	ConvertToScreen (&param->clickToOpenRect);
	param->top = where.y < BScreen(this->Window()).Frame().bottom-50;

	gPopupThreadID = spawn_thread(thread_popup, "Popup holder thread",
		B_URGENT_DISPLAY_PRIORITY, param);
	resume_thread(gPopupThreadID);
}


void
ProcessController::Draw(BRect)
{
	SetDrawingMode(B_OP_COPY);
	DoDraw(true);
}


void
ProcessController::DoDraw(bool force)
{
	BRect bounds(Bounds());

	float h = floorf(bounds.Height ()) - 2;
	float top = 1, left = 1;
	float bottom = top + h;
	float barWidth;
	float barGap;
	float memWidth;
	if (gCPUcount <= 4 && bounds.Width() == 15) {
		// Use fixed sizes for small CPU counts
		barWidth = layout[gCPUcount].cpu_width;
		barGap = layout[gCPUcount].cpu_inter;
		memWidth = layout[gCPUcount].mem_width;
	} else {
		memWidth = floorf((bounds.Height() + 1) / 8);
		barGap = ((bounds.Width() + 1) / gCPUcount) > 3 ? 1 : 0;
		barWidth = floorf((bounds.Width() - 1 - memWidth - barGap * gCPUcount)
			/ gCPUcount);
	}
	// interspace
	float right = left + gCPUcount * (barWidth + barGap) - barGap;
	float leftMem = bounds.Width() - memWidth;
		// right of CPU frame...
	if (force && Parent()) {
		SetHighColor(Parent()->ViewColor());
		FillRect(BRect(right + 1, top - 1, leftMem, bottom + 1));
	}

	if (force) {
		SetHighColor(frame_color);
		StrokeRect(BRect(left - 1, top - 1, right, bottom + 1));
		if (gCPUcount > 1 && barGap == 1) {
			for (unsigned int x = 1; x < gCPUcount; x++)
				StrokeLine(BPoint(left + x * barWidth + x - 1, top),
					BPoint(left + x * barWidth + x - 1, bottom));
		}
	}

	if (force)
		StrokeRect(BRect(leftMem - 1, top - 1, leftMem + memWidth, bottom + 1));

	for (unsigned int x = 0; x < gCPUcount; x++) {
		right = left + barWidth - 1;
		float rem = fCPUTimes[x] * (h + 1);
		float barHeight = floorf (rem);
		rem -= barHeight;
		float limit = bottom - barHeight;	// horizontal line
		float previousLimit = bottom - fLastBarHeight[x];
		float idleTop = top;

		if (!force && previousLimit > top)
			idleTop = previousLimit - 1;
		if (limit > idleTop) {
			SetHighColor(idle_color);
			FillRect(BRect(left, idleTop, right, limit - 1));
		}
		if (barHeight <= h) {
			rgb_color fraction_color;
			mix_colors(fraction_color, idle_color, active_color, rem);
			SetHighColor(fraction_color);
			StrokeLine(BPoint(left, bottom - barHeight), BPoint(right,
				bottom - barHeight));
		}
		float active_bottom = bottom;
		if (!force && previousLimit < bottom)
			active_bottom = previousLimit + 1;
		if (limit < active_bottom) {
			SetHighColor(active_color);
			FillRect(BRect(left, limit + 1, right, active_bottom));
		}
		left += barWidth + barGap;
		fLastBarHeight[x] = barHeight;
	}

	float rightMem = bounds.Width() - 1;
	float rem = fMemoryUsage * (h + 1);
	float barHeight = floorf(rem);
	rem -= barHeight;

	rgb_color used_memory_color;
	float sq = fMemoryUsage * fMemoryUsage;
	sq *= sq;
	sq *= sq;
	mix_colors(used_memory_color, memory_color, swap_color, sq);

	float limit = bottom - barHeight;	// horizontal line
	float previousLimit = bottom - fLastMemoryHeight;
	float free_top = top;
	if (!force && previousLimit > top)
		free_top = previousLimit - 1;
	if (limit > free_top) {
		SetHighColor (idle_color);
		FillRect(BRect(leftMem, free_top, rightMem, limit - 1));
	}
	if (barHeight <= h) {
		rgb_color fraction_color;
		mix_colors(fraction_color, idle_color, used_memory_color, rem);
		SetHighColor(fraction_color);
		StrokeLine(BPoint(leftMem, bottom - barHeight), BPoint(rightMem,
			bottom - barHeight));
	}
	float usedBottom = bottom;
//	if (!force && previousLimit < bottom)
//		usedBottom = previousLimit + 1;
	if (limit < usedBottom) {
		SetHighColor(used_memory_color);
		FillRect(BRect(leftMem, limit + 1, rightMem, usedBottom));
	}
	fLastMemoryHeight = barHeight;
}


void
ProcessController::Update()
{
	system_info info;
	get_system_info(&info);
	bigtime_t now = system_time();

	cpu_info* cpuInfos = new cpu_info[gCPUcount];
	get_cpu_info(0, gCPUcount, cpuInfos);

	fMemoryUsage = float(info.used_pages) / float(info.max_pages);
	// Calculate work done since last call to Update() for each CPU
	for (unsigned int x = 0; x < gCPUcount; x++) {
		bigtime_t load = cpuInfos[x].active_time - fPrevActive[x];
		bigtime_t passed = now - fPrevTime;
		float cpuTime = float(load) / float(passed);

		fPrevActive[x] = cpuInfos[x].active_time;
		if (load > passed)
			fPrevActive[x] -= load - passed; // save overload for next period...
		if (cpuTime < 0)
			cpuTime = 0;
		if (cpuTime > 1)
			cpuTime = 1;
		fCPUTimes[x] = cpuTime;
	}
	fPrevTime = now;

	delete[] cpuInfos;
}


//	#pragma mark -


status_t
thread_popup(void *arg)
{
	Tpopup_param* param = (Tpopup_param*) arg;
	int32 mcookie, hcookie;
	unsigned long m;
	long h;
	BMenuItem* item;
	bool top = param->top;

	system_info systemInfo;
	get_system_info(&systemInfo);
	info_pack* infos = new info_pack[systemInfo.used_teams];
	// TODO: this doesn't necessarily get all teams
	for (m = 0, mcookie = 0; m < systemInfo.used_teams; m++) {
		infos[m].team_icon = NULL;
		infos[m].team_name[0] = 0;
		infos[m].thread_info = NULL;
		if (get_next_team_info(&mcookie, &infos[m].team_info) == B_OK) {
			infos[m].thread_info = new thread_info[infos[m].team_info.thread_count];
			for (h = 0, hcookie = 0; h < infos[m].team_info.thread_count; h++) {
				if (get_next_thread_info(infos[m].team_info.team, &hcookie,
						&infos[m].thread_info[h]) != B_OK)
					infos[m].thread_info[h].thread = -1;
			}
			get_team_name_and_icon(infos[m], true);
		} else {
			systemInfo.used_teams = m;
			infos[m].team_info.team = -1;
		}
	}

	BPopUpMenu* popup = new BPopUpMenu("Global Popup", false, false);
	popup->SetFont(be_plain_font);

	// Quit section
	BMenu* QuitPopup = new QuitMenu(B_TRANSLATE("Quit an application"),
	infos, systemInfo.used_teams);
	QuitPopup->SetFont(be_plain_font);
	popup->AddItem(QuitPopup);

	// Memory Usage section
	MemoryBarMenu* MemoryPopup = new MemoryBarMenu(B_TRANSLATE("Memory usage"),
	infos, systemInfo);
	int64 committedMemory = (int64)systemInfo.used_pages * B_PAGE_SIZE / 1024;
	for (m = 0; m < systemInfo.used_teams; m++) {
		if (infos[m].team_info.team >= 0) {
			MemoryBarMenuItem* memoryItem =
				new MemoryBarMenuItem(infos[m].team_name,
					infos[m].team_info.team, infos[m].team_icon, false, NULL);
			MemoryPopup->AddItem(memoryItem);
			memoryItem->UpdateSituation(committedMemory);
		}
	}

	addtopbottom(MemoryPopup);

	// CPU Load section
	TeamBarMenu* CPUPopup = new TeamBarMenu(B_TRANSLATE("Threads and CPU "
	"usage"), infos, systemInfo.used_teams);
	for (m = 0; m < systemInfo.used_teams; m++) {
		if (infos[m].team_info.team >= 0) {
			ThreadBarMenu* TeamPopup = new ThreadBarMenu(infos[m].team_name,
				infos[m].team_info.team, infos[m].team_info.thread_count);
			BMessage* kill_team = new BMessage('KlTm');
			kill_team->AddInt32("team", infos[m].team_info.team);
			TeamBarMenuItem* item = new TeamBarMenuItem(TeamPopup, kill_team,
				infos[m].team_info.team, infos[m].team_icon, false);
			item->SetTarget(gPCView);
			CPUPopup->AddItem(item);
		}
	}

	addtopbottom(CPUPopup);
	addtopbottom(new BSeparatorItem());

	// CPU on/off section
	if (gCPUcount > 1) {
		for (unsigned int i = 0; i < gCPUcount; i++) {
			BString itemName;
			itemName.SetToFormat(B_TRANSLATE("Processor %d"), i + 1);
			BMessage* m = new BMessage('CPU ');
			m->AddInt32("cpu", i);
			item = new IconMenuItem (gPCView->fProcessorIcon, itemName, m);
			if (_kern_cpu_enabled(i))
				item->SetMarked(true);
			item->SetTarget(gPCView);
			addtopbottom(item);
		}
		addtopbottom(new BSeparatorItem());
	}

	// Scheduler modes
	int32 currentMode = get_scheduler_mode();
	Preferences preferences(kPreferencesFileName, NULL, false);
	int32 savedMode;
	if (preferences.ReadInt32(savedMode, "scheduler_mode") && currentMode != savedMode) {
		set_scheduler_mode(savedMode);
		currentMode = get_scheduler_mode();
	}
	BMessage* msg = new BMessage('Schd');
	item = new BMenuItem(B_TRANSLATE("Power saving"), msg);
	if ((uint32)currentMode == SCHEDULER_MODE_POWER_SAVING)
		item->SetMarked(true);
	item->SetTarget(gPCView);
	addtopbottom(item);
	addtopbottom(new BSeparatorItem());

	if (!be_roster->IsRunning(kTrackerSig)) {
		item = new IconMenuItem(gPCView->fTrackerIcon,
		B_TRANSLATE("Restart Tracker"), new BMessage('Trac'));
		item->SetTarget(gPCView);
		addtopbottom(item);
	}
	if (!be_roster->IsRunning(kDeskbarSig)) {
		item = new IconMenuItem(gPCView->fDeskbarIcon,
		B_TRANSLATE("Restart Deskbar"), new BMessage('Dbar'));
		item->SetTarget(gPCView);
		addtopbottom(item);
	}

	item = new IconMenuItem(gPCView->fTerminalIcon,
	B_TRANSLATE("New Terminal"), new BMessage('Term'));
	item->SetTarget(gPCView);
	addtopbottom(item);

	addtopbottom(new BSeparatorItem());

	bool showLiveInDeskbarItem = gInDeskbar;
	if (!showLiveInDeskbarItem) {
		int32 cookie = 0;
		image_info info;
		while (get_next_image_info(B_CURRENT_TEAM, &cookie, &info) == B_OK) {
			if (info.type == B_APP_IMAGE) {
				// only show the Live in Deskbar item if a) we're running in
				// deskbar itself, or b) we're running in PC's team.
				if (strstr(info.name, "ProcessController") != NULL) {
					showLiveInDeskbarItem = true;
					break;
				}
			}
		}
	}

	if (showLiveInDeskbarItem && be_roster->IsRunning(kDeskbarSig)) {
		item = new BMenuItem(B_TRANSLATE("Live in the Deskbar"),
			new BMessage('AlDb'));
		BDeskbar deskbar;
		item->SetMarked(gInDeskbar || deskbar.HasItem(kDeskbarItemName));
		item->SetTarget(gPCView);
		addtopbottom(item);
		addtopbottom(new BSeparatorItem ());
	}


	item = new IconMenuItem(gPCView->fProcessControllerIcon,
	B_TRANSLATE("About ProcessController" B_UTF8_ELLIPSIS),
		new BMessage(B_ABOUT_REQUESTED));
	item->SetTarget(gPCView);
	addtopbottom(item);

	param->where.x -= 5;
	param->where.y -= 8;
	popup->Go(param->where, true, true, param->clickToOpenRect);

	delete popup;
	for (m = 0; m < systemInfo.used_teams; m++) {
		if (infos[m].team_info.team >= 0) {
			delete[] infos[m].thread_info;
			delete infos[m].team_icon;
		}
	}
	delete[] infos;
	delete param;
	atomic_add (&gPopupFlag, -1);
	gPopupThreadID = 0;

	return B_OK;
}


status_t
thread_quit_application(void *arg)
{
	BMessenger messenger(NULL, (addr_t)arg);
	messenger.SendMessage(B_QUIT_REQUESTED);
	return B_OK;
}


status_t
thread_debug_thread(void *arg)
{
	Tdebug_thead_param*	param = (Tdebug_thead_param*) arg;
	debug_thread(param->thread);
	delete param;
	return B_OK;
}