root/src/apps/processcontroller/ProcessController.cpp
/*
 * 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;
        return new ProcessController(ProcessController::ComposeSize(maxWidth, maxHeight));
}


//      #pragma mark -


BSize
ProcessController::ComposeSize(float maxWidth, float maxHeight)
{
        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;

        // Scale, and be at least as wide as tall
        if (maxHeight > 16)
                width *= (int)(maxHeight / 16);
        if (width < maxHeight)
                width = (int)maxHeight;

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

        return BSize(width - 1, maxHeight - 1);
}


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(BSize size)
        :
        BView(BRect(0, 0, size.Width(), size.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 ());
        }

        BString text(B_TRANSLATE("About %appname%" B_UTF8_ELLIPSIS));
        text.ReplaceFirst("%appname%", B_TRANSLATE_SYSTEM_NAME("ProcessController"));
        item = new IconMenuItem(gPCView->fProcessControllerIcon, text, 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;
}