From 3a4a214197712d83f79b1f035937ada3d486dbea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Sun, 13 Apr 2008 06:02:45 +0000 Subject: [PATCH] Some more work on the ActivityMonitor: * Added legend. * Added network receiving/sending DataSource. * Now uses the new layout engine. * Added support for per CPU DataSources, and added a per CPU CPU usage DataSource. * The DataSources can now be turned on and off via popup menu. * You can now add more than one view to the window. * The DataSources of the ActivityViews are now remembered. * Now uses an offscreen bitmap to reduce flicker (it should now no longer flicker with accelerated graphics). git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24955 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/activitymonitor/ActivityView.cpp | 343 +++++++++++++++++--- src/apps/activitymonitor/ActivityView.h | 20 +- src/apps/activitymonitor/ActivityWindow.cpp | 100 +++++- src/apps/activitymonitor/ActivityWindow.h | 6 +- src/apps/activitymonitor/DataSource.cpp | 319 +++++++++++++++++- src/apps/activitymonitor/DataSource.h | 73 ++++- src/apps/activitymonitor/Jamfile | 4 +- src/apps/activitymonitor/SystemInfo.cpp | 85 ++++- src/apps/activitymonitor/SystemInfo.h | 9 + 9 files changed, 877 insertions(+), 82 deletions(-) diff --git a/src/apps/activitymonitor/ActivityView.cpp b/src/apps/activitymonitor/ActivityView.cpp index 9be7486270..140f539baf 100644 --- a/src/apps/activitymonitor/ActivityView.cpp +++ b/src/apps/activitymonitor/ActivityView.cpp @@ -32,6 +32,7 @@ const bigtime_t kInitialRefreshInterval = 500000LL; const uint32 kMsgRefresh = 'refr'; const uint32 kMsgToggleDataSource = 'tgds'; +const uint32 kMsgToggleLegend = 'tglg'; extern const char* kSignature; @@ -123,12 +124,25 @@ DataHistory::SetRefreshInterval(bigtime_t interval) ActivityView::ActivityView(BRect frame, const char* name, - const BMessage& settings, uint32 resizingMode) + const BMessage* settings, uint32 resizingMode) : BView(frame, name, resizingMode, - B_WILL_DRAW | B_SUBPIXEL_PRECISE | B_FULL_UPDATE_ON_RESIZE - | B_FRAME_EVENTS) + B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS) { - _Init(&settings); + _Init(settings); + + BRect rect(Bounds()); + rect.top = rect.bottom - 7; + rect.left = rect.right - 7; + BDragger* dragger = new BDragger(rect, this, + B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); + AddChild(dragger); +} + + +ActivityView::ActivityView(const char* name, const BMessage* settings) + : BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS) +{ + _Init(settings); BRect rect(Bounds()); rect.top = rect.bottom - 7; @@ -148,6 +162,7 @@ ActivityView::ActivityView(BMessage* archive) ActivityView::~ActivityView() { + delete fOffscreen; } @@ -155,17 +170,29 @@ void ActivityView::_Init(const BMessage* settings) { fBackgroundColor = (rgb_color){255, 255, 240}; - SetLowColor(fBackgroundColor); + fOffscreen = NULL; + SetViewColor(B_TRANSPARENT_COLOR); + SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); fRefreshInterval = kInitialRefreshInterval; fDrawInterval = kInitialRefreshInterval * 2; fLastRefresh = 0; fDrawResolution = 1; - AddDataSource(new UsedMemoryDataSource()); - AddDataSource(new CachedMemoryDataSource()); - AddDataSource(new ThreadsDataSource()); - AddDataSource(new CpuUsageDataSource()); + if (settings == NULL + || settings->FindBool("show legend", &fShowLegend) != B_OK) + fShowLegend = true; + + if (settings == NULL) { + AddDataSource(new UsedMemoryDataSource()); + AddDataSource(new CachedMemoryDataSource()); + return; + } + + const char* name; + for (int32 i = 0; settings->FindString("source", i, &name) == B_OK; i++) { + AddDataSource(DataSource::FindSource(name)); + } } @@ -203,16 +230,30 @@ ActivityView::Instantiate(BMessage* archive) status_t ActivityView::SaveState(BMessage& state) const { - return B_ERROR; + status_t status = state.AddBool("show legend", fShowLegend); + if (status != B_OK) + return status; + + for (int32 i = 0; i < fSources.CountItems(); i++) { + DataSource* source = fSources.ItemAt(i); + + if (!source->PerCPU() || source->CPU() == 0) + status = state.AddString("source", source->Name()); + if (status != B_OK) + return status; + + // TODO: save and restore color as well + } + return B_OK; } DataSource* -ActivityView::FindDataSource(const char* name) +ActivityView::FindDataSource(const DataSource* search) { for (int32 i = fSources.CountItems(); i-- > 0;) { DataSource* source = fSources.ItemAt(i); - if (!strcmp(source->Label(), name)) + if (!strcmp(source->Name(), search->Name())) return source; } @@ -221,21 +262,50 @@ ActivityView::FindDataSource(const char* name) status_t -ActivityView::AddDataSource(DataSource* source) +ActivityView::AddDataSource(const DataSource* source) { - DataHistory* values = new(std::nothrow) DataHistory(10 * 60000000LL, - fRefreshInterval); - if (values == NULL) - return B_NO_MEMORY; - if (!fValues.AddItem(values)) { - delete values; - return B_NO_MEMORY; + if (source == NULL) + return B_BAD_VALUE; + + int32 insert = DataSource::IndexOf(source); + for (int32 i = 0; i < fSources.CountItems() && i < insert; i++) { + DataSource* before = fSources.ItemAt(i); + if (DataSource::IndexOf(before) > insert) { + insert = i; + break; + } + } + if (insert > fSources.CountItems()) + insert = fSources.CountItems(); + + uint32 count = 1; + if (source->PerCPU()) { + SystemInfo info; + count = info.CPUCount(); } - if (!fSources.AddItem(source)) { - fValues.RemoveItem(values); - delete values; - return B_NO_MEMORY; + for (uint32 i = 0; i < count; i++) { + DataHistory* values = new(std::nothrow) DataHistory(10 * 60000000LL, + fRefreshInterval); + if (values == NULL) + return B_NO_MEMORY; + + if (!fValues.AddItem(values, insert + i)) { + delete values; + return B_NO_MEMORY; + } + + DataSource* copy; + if (source->PerCPU()) + copy = source->CopyForCPU(i); + else + copy = source->Copy(); + + if (!fSources.AddItem(copy, insert + i)) { + fValues.RemoveItem(values); + delete values; + return B_NO_MEMORY; + } } return B_OK; @@ -243,16 +313,22 @@ ActivityView::AddDataSource(DataSource* source) status_t -ActivityView::RemoveDataSource(DataSource* source) +ActivityView::RemoveDataSource(const DataSource* remove) { - int32 index = fSources.IndexOf(source); - if (index < 0) - return B_ENTRY_NOT_FOUND; + while (true) { + DataSource* source = FindDataSource(remove); + if (source == NULL) + return B_OK; - fSources.RemoveItemAt(index); - delete source; - DataHistory* values = fValues.RemoveItemAt(index); - delete values; + int32 index = fSources.IndexOf(source); + if (index < 0) + return B_ENTRY_NOT_FOUND; + + fSources.RemoveItemAt(index); + delete source; + DataHistory* values = fValues.RemoveItemAt(index); + delete values; + } return B_OK; } @@ -271,6 +347,8 @@ ActivityView::AttachedToWindow() { BMessage refresh(kMsgRefresh); fRunner = new BMessageRunner(this, &refresh, fRefreshInterval); + + FrameResized(Bounds().Width(), Bounds().Height()); } @@ -281,9 +359,47 @@ ActivityView::DetachedFromWindow() } +BSize +ActivityView::MinSize() +{ + BSize size(32, 32); + if (fShowLegend) + size.height = _LegendFrame().Height(); + + return size; +} + + void ActivityView::FrameResized(float /*width*/, float /*height*/) { + _UpdateOffscreenBitmap(); +} + + +void +ActivityView::_UpdateOffscreenBitmap() +{ + BRect frame = _HistoryFrame(); + if (fOffscreen != NULL && frame == fOffscreen->Bounds()) + return; + + delete fOffscreen; + + // create offscreen bitmap + + fOffscreen = new(std::nothrow) BBitmap(frame, B_BITMAP_ACCEPTS_VIEWS, + B_RGB32); + if (fOffscreen == NULL || fOffscreen->InitCheck() != B_OK) { + delete fOffscreen; + fOffscreen = NULL; + return; + } + + BView* view = new BView(frame, NULL, B_FOLLOW_NONE, B_SUBPIXEL_PRECISE); + view->SetViewColor(fBackgroundColor); + view->SetLowColor(view->ViewColor()); + fOffscreen->AddChild(view); } @@ -301,21 +417,30 @@ ActivityView::MouseDown(BPoint where) BPopUpMenu *menu = new BPopUpMenu(B_EMPTY_STRING, false, false); menu->SetFont(be_plain_font); + + SystemInfo info; BMenuItem* item; for (int32 i = 0; i < DataSource::CountSources(); i++) { const DataSource* source = DataSource::SourceAt(i); + if (source->MultiCPUOnly() && info.CPUCount() == 1) + continue; + BMessage* message = new BMessage(kMsgToggleDataSource); message->AddInt32("index", i); - item = new BMenuItem(source->Label(), message); - if (FindDataSource(source->Label())) + item = new BMenuItem(source->Name(), message); + if (FindDataSource(source)) item->SetMarked(true); menu->AddItem(item); } + menu->AddSeparatorItem(); + menu->AddItem(new BMenuItem(fShowLegend ? "Hide Legend" : "Show Legend", + new BMessage(kMsgToggleLegend))); + menu->SetTargetForItems(this); ConvertToScreen(&where); @@ -353,9 +478,9 @@ ActivityView::MessageReceived(BMessage* message) if (baseSource == NULL) break; - DataSource* source = FindDataSource(baseSource->Label()); + DataSource* source = FindDataSource(baseSource); if (source == NULL) - AddDataSource(baseSource->Copy()); + AddDataSource(baseSource); else RemoveDataSource(source); @@ -363,6 +488,11 @@ ActivityView::MessageReceived(BMessage* message) break; } + case kMsgToggleLegend: + fShowLegend = !fShowLegend; + Invalidate(); + break; + case B_MOUSE_WHEEL_CHANGED: { float deltaY = 0.0f; @@ -391,6 +521,56 @@ ActivityView::MessageReceived(BMessage* message) } +BRect +ActivityView::_HistoryFrame() const +{ + if (!fShowLegend) + return Bounds(); + + BRect frame = Bounds(); + BRect legendFrame = _LegendFrame(); + + frame.bottom = legendFrame.top - 1; + + return frame; +} + + +BRect +ActivityView::_LegendFrame() const +{ + BRect frame = Bounds(); + font_height fontHeight; + GetFontHeight(&fontHeight); + + int32 rows = (fSources.CountItems() + 1) / 2; + frame.top = frame.bottom - rows * (4 + ceilf(fontHeight.ascent) + + ceilf(fontHeight.descent) + ceilf(fontHeight.leading)); + + return frame; +} + + +BRect +ActivityView::_LegendFrameAt(BRect frame, int32 index) const +{ + int32 column = index & 1; + int32 row = index / 2; + if (column == 0) + frame.right = frame.left + floorf(frame.Width() / 2) - 5; + else + frame.left = frame.right - floorf(frame.Width() / 2) + 5; + + int32 rows = (fSources.CountItems() + 1) / 2; + float height = floorf((frame.Height() - 5) / rows); + + frame.top = frame.top + 5 + row * height; + frame.bottom = frame.top + height - 1; + + return frame; +} + + float ActivityView::_PositionForValue(DataSource* source, DataHistory* values, int64 value) @@ -408,14 +588,25 @@ ActivityView::_PositionForValue(DataSource* source, DataHistory* values, if (value < min) value = min; - float height = Bounds().Height(); + float height = _HistoryFrame().Height(); return height - (value - min) * height / (max - min); } void -ActivityView::Draw(BRect /*updateRect*/) +ActivityView::_DrawHistory() { + _UpdateOffscreenBitmap(); + + BView* view = this; + if (fOffscreen != NULL) { + fOffscreen->Lock(); + view = fOffscreen->ChildAt(0); + } + + BRect frame = _HistoryFrame(); + view->FillRect(frame, B_SOLID_LOW); + uint32 step = 2; uint32 resolution = fDrawResolution; if (fDrawResolution > 1) { @@ -423,12 +614,19 @@ ActivityView::Draw(BRect /*updateRect*/) resolution--; } - uint32 width = Bounds().IntegerWidth() - 10; + uint32 width = frame.IntegerWidth() - 10; uint32 steps = width / step; bigtime_t timeStep = fRefreshInterval * resolution; bigtime_t now = system_time(); - SetPenSize(2); + view->SetPenSize(1); + + view->SetHighColor(tint_color(view->ViewColor(), B_DARKEN_2_TINT)); + view->StrokeRect(frame); + view->StrokeLine(BPoint(frame.left, frame.top + frame.Height() / 2), + BPoint(frame.right, frame.top + frame.Height() / 2)); + + view->SetPenSize(2); for (uint32 i = fSources.CountItems(); i-- > 0;) { DataSource* source = fSources.ItemAt(i); @@ -436,8 +634,8 @@ ActivityView::Draw(BRect /*updateRect*/) bigtime_t time = now - steps * timeStep; // for now steps pixels per second - BeginLineArray(steps); - SetHighColor(source->Color()); + view->BeginLineArray(steps); + view->SetHighColor(source->Color()); float lastY = FLT_MIN; uint32 lastX = 0; @@ -462,17 +660,72 @@ ActivityView::Draw(BRect /*updateRect*/) } float y = _PositionForValue(source, values, value); - if (lastY != FLT_MIN) - AddLine(BPoint(lastX, lastY), BPoint(x, y), source->Color()); + if (lastY != FLT_MIN) { + view->AddLine(BPoint(lastX, lastY), BPoint(x, y), + source->Color()); + } lastX = x; lastY = y; } - EndLineArray(); + view->EndLineArray(); } // TODO: add marks when an app started or quit + view->Sync(); + if (fOffscreen != NULL) { + fOffscreen->Unlock(); + DrawBitmap(fOffscreen); + } +} + + +void +ActivityView::Draw(BRect /*updateRect*/) +{ + _DrawHistory(); + + if (!fShowLegend) + return; + + // draw legend + + BRect legendFrame = _LegendFrame(); + FillRect(legendFrame, B_SOLID_LOW); + + font_height fontHeight; + GetFontHeight(&fontHeight); + + for (int32 i = 0; i < fSources.CountItems(); i++) { + DataSource* source = fSources.ItemAt(i); + DataHistory* values = fValues.ItemAt(i); + BRect frame = _LegendFrameAt(legendFrame, i); + + // draw color box + BRect colorBox = frame.InsetByCopy(2, 2); + colorBox.right = colorBox.left + colorBox.Height(); + SetHighColor(tint_color(source->Color(), B_DARKEN_1_TINT)); + StrokeRect(colorBox); + SetHighColor(source->Color()); + colorBox.InsetBy(1, 1); + FillRect(colorBox); + + // show current value and label + float y = frame.top + ceilf(fontHeight.ascent); + int64 value = values->ValueAt(values->End()); + BString text; + source->Print(text, value); + float width = StringWidth(text.String()); + + BString label = source->Label(); + TruncateString(&label, B_TRUNCATE_MIDDLE, + frame.right - colorBox.right - 12 - width); + + SetHighColor(ui_color(B_CONTROL_TEXT_COLOR)); + DrawString(label.String(), BPoint(6 + colorBox.right, y)); + DrawString(text.String(), BPoint(frame.right - width, y)); + } } diff --git a/src/apps/activitymonitor/ActivityView.h b/src/apps/activitymonitor/ActivityView.h index 0130b6b2ea..cb0bcb62eb 100644 --- a/src/apps/activitymonitor/ActivityView.h +++ b/src/apps/activitymonitor/ActivityView.h @@ -11,6 +11,7 @@ #include "CircularBuffer.h" +class BBitmap; class BMessageRunner; class DataSource; struct data_item; @@ -43,7 +44,9 @@ private: class ActivityView : public BView { public: ActivityView(BRect frame, const char* name, - const BMessage& settings, uint32 resizingMode); + const BMessage* settings, uint32 resizingMode); + ActivityView(const char* name, + const BMessage* settings); ActivityView(BMessage* archive); virtual ~ActivityView(); @@ -52,15 +55,17 @@ public: status_t SaveState(BMessage& state) const; - DataSource* FindDataSource(const char* name); - status_t AddDataSource(DataSource* source); - status_t RemoveDataSource(DataSource* source); + DataSource* FindDataSource(const DataSource* source); + status_t AddDataSource(const DataSource* source); + status_t RemoveDataSource(const DataSource* source); void RemoveAllDataSources(); protected: virtual void AttachedToWindow(); virtual void DetachedFromWindow(); + virtual BSize MinSize(); + virtual void FrameResized(float width, float height); virtual void MouseDown(BPoint where); virtual void MouseMoved(BPoint where, uint32 transit, @@ -73,10 +78,16 @@ protected: private: void _Init(const BMessage* settings); void _Refresh(); + void _UpdateOffscreenBitmap(); + BRect _HistoryFrame() const; + BRect _LegendFrame() const; + BRect _LegendFrameAt(BRect frame, int32 index) const; float _PositionForValue(DataSource* source, DataHistory* values, int64 value); + void _DrawHistory(); rgb_color fBackgroundColor; + BBitmap* fOffscreen; BObjectList fSources; BObjectList fValues; BMessageRunner* fRunner; @@ -84,6 +95,7 @@ private: bigtime_t fLastRefresh; bigtime_t fDrawInterval; int32 fDrawResolution; + bool fShowLegend; }; #endif // ACTIVITY_VIEW_H diff --git a/src/apps/activitymonitor/ActivityWindow.cpp b/src/apps/activitymonitor/ActivityWindow.cpp index 27890cf0d7..7c12e4c8d6 100644 --- a/src/apps/activitymonitor/ActivityWindow.cpp +++ b/src/apps/activitymonitor/ActivityWindow.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,10 @@ #include "ActivityView.h" +const uint32 kMsgAddView = 'advw'; +const uint32 kMsgRemoveView = 'rmvw'; + + ActivityWindow::ActivityWindow() : BWindow(BRect(100, 100, 500, 250), "ActivityMonitor", B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS | B_QUIT_ON_WINDOW_CLOSE) @@ -32,24 +37,34 @@ ActivityWindow::ActivityWindow() if (settings.FindRect("window frame", &frame) == B_OK) { MoveTo(frame.LeftTop()); ResizeTo(frame.Width(), frame.Height()); - frame.OffsetTo(B_ORIGIN); - } else - frame = Bounds(); + } + + BGroupLayout* layout = new BGroupLayout(B_VERTICAL); + SetLayout(layout); // create GUI - BMenuBar* menuBar = new BMenuBar(Bounds(), "menu"); - AddChild(menuBar); + BMenuBar* menuBar = new BMenuBar("menu"); + layout->AddView(menuBar); - frame.top = menuBar->Frame().bottom; + fLayout = new BGroupLayout(B_VERTICAL); + float inset = ceilf(be_plain_font->Size() * 0.7); + fLayout->SetInsets(inset, inset, inset, inset); + fLayout->SetSpacing(inset); - BView* top = new BView(frame, NULL, B_FOLLOW_ALL, B_WILL_DRAW); + BView* top = new BView("top", 0, fLayout); top->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); - AddChild(top); + layout->AddView(top); - fActivityView = new ActivityView(top->Bounds().InsetByCopy(10, 10), - "ActivityMonitor", settings, B_FOLLOW_ALL); - top->AddChild(fActivityView); + BMessage viewState; + int32 count = 0; + for (int32 i = 0; settings.FindMessage("activity view", i, &viewState) + == B_OK; i++) { + fLayout->AddView(new ActivityView("ActivityMonitor", &viewState)); + count++; + } + if (count == 0) + fLayout->AddView(new ActivityView("ActivityMonitor", NULL)); // add menu @@ -57,6 +72,11 @@ ActivityWindow::ActivityWindow() BMenu* menu = new BMenu("File"); BMenuItem* item; + menu->AddItem(new BMenuItem("Add View", new BMessage(kMsgAddView))); + menu->AddItem(fRemoveItem = new BMenuItem("Remove View", + new BMessage(kMsgRemoveView))); + menu->AddSeparatorItem(); + menu->AddItem(item = new BMenuItem("About ActivityMonitor" B_UTF8_ELLIPSIS, new BMessage(B_ABOUT_REQUESTED))); menu->AddSeparatorItem(); @@ -65,6 +85,8 @@ ActivityWindow::ActivityWindow() menu->SetTargetForItems(this); item->SetTarget(be_app); menuBar->AddItem(menu); + + _UpdateRemoveItem(); } @@ -109,6 +131,26 @@ ActivityWindow::_SaveSettings() BMessage settings('actm'); status = settings.AddRect("window frame", Frame()); + if (status != B_OK) + return status; + + BView* top = GetLayout()->View(); + int32 count = top->CountChildren(); + for (int32 i = 0; i < count; i++) { + ActivityView* view = dynamic_cast(top->ChildAt(i)); + if (view == NULL) + continue; + + BMessage* viewState = new BMessage; + status = view->SaveState(*viewState); + if (status == B_OK) + status = settings.AddMessage("activity view", viewState); + if (status != B_OK) { + delete viewState; + break; + } + } + if (status == B_OK) status = settings.Flatten(&file); @@ -116,6 +158,16 @@ ActivityWindow::_SaveSettings() } +void +ActivityWindow::_UpdateRemoveItem() +{ + BView* view = fLayout->View(); + int32 count = view->CountChildren(); + + fRemoveItem->SetEnabled(count >= 2); +} + + void ActivityWindow::_MessageDropped(BMessage* message) { @@ -140,6 +192,32 @@ ActivityWindow::MessageReceived(BMessage* message) _MessageDropped(message); break; + case kMsgAddView: + { + BView* view = fLayout->View()->ChildAt(0); + fLayout->AddView(new ActivityView("ActivityMonitor", NULL)); + if (view != NULL) + ResizeBy(0, view->Bounds().Height() + fLayout->Spacing()); + _UpdateRemoveItem(); + break; + } + + case kMsgRemoveView: + { + BView* view = fLayout->View(); + int32 count = view->CountChildren(); + if (count == 1) + return; + + BView* last = view->ChildAt(count - 1); + fLayout->RemoveView(last); + ResizeBy(0, -last->Bounds().Height() - fLayout->Spacing()); + delete last; + + _UpdateRemoveItem(); + break; + } + default: BWindow::MessageReceived(message); break; diff --git a/src/apps/activitymonitor/ActivityWindow.h b/src/apps/activitymonitor/ActivityWindow.h index 65e795169b..bdfa6442ff 100644 --- a/src/apps/activitymonitor/ActivityWindow.h +++ b/src/apps/activitymonitor/ActivityWindow.h @@ -9,6 +9,8 @@ #include class BFile; +class BGroupLayout; +class BMenuItem; class ActivityView; @@ -25,9 +27,11 @@ private: status_t _LoadSettings(BMessage& settings); status_t _SaveSettings(); + void _UpdateRemoveItem(); void _MessageDropped(BMessage *message); - ActivityView* fActivityView; + BGroupLayout* fLayout; + BMenuItem* fRemoveItem; }; #endif // ACTIVITY_WINDOW_H diff --git a/src/apps/activitymonitor/DataSource.cpp b/src/apps/activitymonitor/DataSource.cpp index 9038c12050..b50fee5d52 100644 --- a/src/apps/activitymonitor/DataSource.cpp +++ b/src/apps/activitymonitor/DataSource.cpp @@ -18,7 +18,10 @@ const DataSource* kSources[] = { new UsedMemoryDataSource(), new CachedMemoryDataSource(), new ThreadsDataSource(), - new CpuUsageDataSource(), + new CPUUsageDataSource(), + new CPUCombinedUsageDataSource(), + new NetworkUsageDataSource(true), + new NetworkUsageDataSource(false) }; const size_t kSourcesCount = sizeof(kSources) / sizeof(kSources[0]); @@ -65,6 +68,13 @@ DataSource::Copy() const } +DataSource* +DataSource::CopyForCPU(int32 cpu) const +{ + return Copy(); +} + + int64 DataSource::Minimum() const { @@ -123,6 +133,13 @@ DataSource::Print(BString& text, int64 value) const } +const char* +DataSource::Name() const +{ + return Label(); +} + + const char* DataSource::Label() const { @@ -151,6 +168,27 @@ DataSource::AdaptiveScale() const } +int32 +DataSource::CPU() const +{ + return 0; +} + + +bool +DataSource::PerCPU() const +{ + return false; +} + + +bool +DataSource::MultiCPUOnly() const +{ + return false; +} + + /*static*/ int32 DataSource::CountSources() { @@ -168,6 +206,33 @@ DataSource::SourceAt(int32 index) } +/*static*/ const DataSource* +DataSource::FindSource(const char* name) +{ + for (uint32 i = 0; i < kSourcesCount; i++) { + const DataSource* source = kSources[i]; + if (!strcmp(source->Name(), name)) + return source; + } + + return NULL; +} + + +/*static*/ int32 +DataSource::IndexOf(const DataSource* source) +{ + const char* name = source->Name(); + + for (uint32 i = 0; i < kSourcesCount; i++) { + if (!strcmp(kSources[i]->Name(), name)) + return i; + } + + return -1; +} + + // #pragma mark - @@ -189,7 +254,7 @@ void MemoryDataSource::Print(BString& text, int64 value) const { char buffer[32]; - snprintf(buffer, sizeof(buffer), "%.1g MB", value / 1048576.0); + snprintf(buffer, sizeof(buffer), "%.1f MB", value / 1048576.0); text = buffer; } @@ -232,7 +297,7 @@ UsedMemoryDataSource::NextValue(SystemInfo& info) const char* UsedMemoryDataSource::Label() const { - return "Available Memory"; + return "Used Memory"; } @@ -321,7 +386,134 @@ ThreadsDataSource::AdaptiveScale() const // #pragma mark - -CpuUsageDataSource::CpuUsageDataSource() +CPUUsageDataSource::CPUUsageDataSource(int32 cpu) + : + fPreviousActive(0), + fPreviousTime(0) +{ + fMinimum = 0; + fMaximum = 1000; + + _SetCPU(cpu); +} + + +CPUUsageDataSource::CPUUsageDataSource(const CPUUsageDataSource& other) + : DataSource(other) +{ + fPreviousActive = other.fPreviousActive; + fPreviousTime = other.fPreviousTime; + fCPU = other.fCPU; + fLabel = other.fLabel; +} + + +CPUUsageDataSource::~CPUUsageDataSource() +{ +} + + +DataSource* +CPUUsageDataSource::Copy() const +{ + return new CPUUsageDataSource(*this); +} + + +DataSource* +CPUUsageDataSource::CopyForCPU(int32 cpu) const +{ + CPUUsageDataSource* copy = new CPUUsageDataSource(*this); + copy->_SetCPU(cpu); + + return copy; +} + + +void +CPUUsageDataSource::Print(BString& text, int64 value) const +{ + char buffer[32]; + snprintf(buffer, sizeof(buffer), "%.1f%%", value / 10.0); + + text = buffer; +} + + +int64 +CPUUsageDataSource::NextValue(SystemInfo& info) +{ + bigtime_t active = info.Info().cpu_infos[fCPU].active_time; + + int64 percent = int64(1000.0 * (active - fPreviousActive) + / (info.Time() - fPreviousTime)); + if (percent < 0) + percent = 0; + if (percent > 1000) + percent = 1000; + + fPreviousActive = active; + fPreviousTime = info.Time(); + + return percent; +} + + +const char* +CPUUsageDataSource::Label() const +{ + return fLabel.String(); +} + + +const char* +CPUUsageDataSource::Name() const +{ + return "CPU Usage"; +} + + +int32 +CPUUsageDataSource::CPU() const +{ + return fCPU; +} + + +bool +CPUUsageDataSource::PerCPU() const +{ + return true; +} + + +void +CPUUsageDataSource::_SetCPU(int32 cpu) +{ + fCPU = cpu; + fLabel = "CPU"; + if (SystemInfo().CPUCount() > 1) + fLabel << " " << cpu; + + fLabel << " Usage"; + + const rgb_color kColors[] = { + // TODO: find some better defaults... + {200, 0, 200}, + {0, 200, 200}, + {80, 80, 80}, + {230, 150, 50}, + }; + const uint32 kNumColors = sizeof(kColors) / sizeof(kColors[0]); + + fColor = kColors[cpu % kNumColors]; +} + + +// #pragma mark - + + +CPUCombinedUsageDataSource::CPUCombinedUsageDataSource() : fPreviousActive(0), fPreviousTime(0) @@ -333,37 +525,39 @@ CpuUsageDataSource::CpuUsageDataSource() } -CpuUsageDataSource::CpuUsageDataSource(const CpuUsageDataSource& other) +CPUCombinedUsageDataSource::CPUCombinedUsageDataSource( + const CPUCombinedUsageDataSource& other) + : DataSource(other) { fPreviousActive = other.fPreviousActive; fPreviousTime = other.fPreviousTime; } -CpuUsageDataSource::~CpuUsageDataSource() +CPUCombinedUsageDataSource::~CPUCombinedUsageDataSource() { } DataSource* -CpuUsageDataSource::Copy() const +CPUCombinedUsageDataSource::Copy() const { - return new CpuUsageDataSource(*this); + return new CPUCombinedUsageDataSource(*this); } void -CpuUsageDataSource::Print(BString& text, int64 value) const +CPUCombinedUsageDataSource::Print(BString& text, int64 value) const { char buffer[32]; - snprintf(buffer, sizeof(buffer), "%.1g%%", value / 10.0); + snprintf(buffer, sizeof(buffer), "%.1f%%", value / 10.0); text = buffer; } int64 -CpuUsageDataSource::NextValue(SystemInfo& info) +CPUCombinedUsageDataSource::NextValue(SystemInfo& info) { int32 running = 0; bigtime_t active = 0; @@ -389,7 +583,108 @@ CpuUsageDataSource::NextValue(SystemInfo& info) const char* -CpuUsageDataSource::Label() const +CPUCombinedUsageDataSource::Label() const { return "CPU Usage"; } + + +const char* +CPUCombinedUsageDataSource::Name() const +{ + return "CPU Usage (combined)"; +} + + +bool +CPUCombinedUsageDataSource::MultiCPUOnly() const +{ + return true; +} + + +// #pragma mark - + + +NetworkUsageDataSource::NetworkUsageDataSource(bool in) + : + fIn(in), + fPreviousBytes(0), + fPreviousTime(0) +{ + SystemInfo info; + NextValue(info); + + fMinimum = 0; + fMaximum = 1000000000LL; + + fColor = fIn ? (rgb_color){200, 150, 0} : (rgb_color){200, 220, 0}; +} + + +NetworkUsageDataSource::NetworkUsageDataSource( + const NetworkUsageDataSource& other) + : DataSource(other) +{ + fIn = other.fIn; + fPreviousBytes = other.fPreviousBytes; + fPreviousTime = other.fPreviousTime; +} + + +NetworkUsageDataSource::~NetworkUsageDataSource() +{ +} + + +DataSource* +NetworkUsageDataSource::Copy() const +{ + return new NetworkUsageDataSource(*this); +} + + +void +NetworkUsageDataSource::Print(BString& text, int64 value) const +{ + char buffer[32]; + snprintf(buffer, sizeof(buffer), "%.1f KB/s", value / 1024.0); + + text = buffer; +} + + +int64 +NetworkUsageDataSource::NextValue(SystemInfo& info) +{ + uint64 transferred = fIn ? info.NetworkReceived() : info.NetworkSent(); + + int64 bytesPerSecond = uint64((transferred - fPreviousBytes) + / ((info.Time() - fPreviousTime) / 1000000.0)); + + fPreviousBytes = transferred; + fPreviousTime = info.Time(); + + return bytesPerSecond; +} + + +const char* +NetworkUsageDataSource::Label() const +{ + return fIn ? "Receiving" : "Sending"; +} + + +const char* +NetworkUsageDataSource::Name() const +{ + return fIn ? "Network Receive" : "Network Send"; +} + + +bool +NetworkUsageDataSource::AdaptiveScale() const +{ + return true; +} diff --git a/src/apps/activitymonitor/DataSource.h b/src/apps/activitymonitor/DataSource.h index a123d7d66d..4d8ffa5329 100644 --- a/src/apps/activitymonitor/DataSource.h +++ b/src/apps/activitymonitor/DataSource.h @@ -7,8 +7,8 @@ #include +#include -class BString; class SystemInfo; @@ -20,6 +20,7 @@ public: virtual ~DataSource(); virtual DataSource* Copy() const; + virtual DataSource* CopyForCPU(int32 cpu) const; int64 Minimum() const; int64 Maximum() const; @@ -32,13 +33,19 @@ public: virtual int64 NextValue(SystemInfo& info); virtual void Print(BString& text, int64 value) const; + virtual const char* Name() const; virtual const char* Label() const; virtual const char* Unit() const; virtual rgb_color Color() const; virtual bool AdaptiveScale() const; + virtual int32 CPU() const; + virtual bool PerCPU() const; + virtual bool MultiCPUOnly() const; static int32 CountSources(); static const DataSource* SourceAt(int32 index); + static const DataSource* FindSource(const char* name); + static int32 IndexOf(const DataSource* source); protected: int64 fMinimum; @@ -95,21 +102,77 @@ public: }; -class CpuUsageDataSource : public DataSource { +class CPUUsageDataSource : public DataSource { public: - CpuUsageDataSource(); - CpuUsageDataSource(const CpuUsageDataSource& other); - virtual ~CpuUsageDataSource(); + CPUUsageDataSource(int32 cpu = 0); + CPUUsageDataSource(const CPUUsageDataSource& other); + virtual ~CPUUsageDataSource(); + + virtual DataSource* Copy() const; + virtual DataSource* CopyForCPU(int32 cpu) const; + + virtual void Print(BString& text, int64 value) const; + virtual int64 NextValue(SystemInfo& info); + + virtual const char* Name() const; + virtual const char* Label() const; + + virtual int32 CPU() const; + virtual bool PerCPU() const; + +private: + void _SetCPU(int32 cpu); + + bigtime_t fPreviousActive; + bigtime_t fPreviousTime; + int32 fCPU; + BString fLabel; +}; + + +class CPUCombinedUsageDataSource : public DataSource { +public: + CPUCombinedUsageDataSource(); + CPUCombinedUsageDataSource( + const CPUCombinedUsageDataSource& other); + virtual ~CPUCombinedUsageDataSource(); virtual DataSource* Copy() const; virtual void Print(BString& text, int64 value) const; virtual int64 NextValue(SystemInfo& info); + + virtual const char* Name() const; virtual const char* Label() const; + virtual bool MultiCPUOnly() const; + private: bigtime_t fPreviousActive; bigtime_t fPreviousTime; }; + +class NetworkUsageDataSource : public DataSource { +public: + NetworkUsageDataSource(bool in); + NetworkUsageDataSource( + const NetworkUsageDataSource& other); + virtual ~NetworkUsageDataSource(); + + virtual DataSource* Copy() const; + + virtual void Print(BString& text, int64 value) const; + virtual int64 NextValue(SystemInfo& info); + + virtual const char* Name() const; + virtual const char* Label() const; + virtual bool AdaptiveScale() const; + +private: + bool fIn; + uint64 fPreviousBytes; + bigtime_t fPreviousTime; +}; + #endif // DATA_SOURCE_H diff --git a/src/apps/activitymonitor/Jamfile b/src/apps/activitymonitor/Jamfile index 2a18e39a4b..1607a5b4a0 100644 --- a/src/apps/activitymonitor/Jamfile +++ b/src/apps/activitymonitor/Jamfile @@ -1,7 +1,5 @@ SubDir HAIKU_TOP src apps activitymonitor ; -SetSubDirSupportedPlatformsBeOSCompatible ; - UsePrivateHeaders shared ; Application ActivityMonitor : @@ -11,6 +9,6 @@ Application ActivityMonitor : DataSource.cpp SystemInfo.cpp - : be tracker $(TARGET_LIBSTDC++) + : be tracker $(TARGET_LIBSTDC++) libnetwork.so : ActivityMonitor.rdef ; diff --git a/src/apps/activitymonitor/SystemInfo.cpp b/src/apps/activitymonitor/SystemInfo.cpp index f07c0992df..557b889f83 100644 --- a/src/apps/activitymonitor/SystemInfo.cpp +++ b/src/apps/activitymonitor/SystemInfo.cpp @@ -6,10 +6,17 @@ #include "SystemInfo.h" +#include +#include +#include +#include +#include + SystemInfo::SystemInfo() : - fTime(system_time()) + fTime(system_time()), + fRetrievedNetwork(false) { get_system_info(&fSystemInfo); } @@ -67,3 +74,79 @@ SystemInfo::MaxTeams() const { return fSystemInfo.max_teams; } + + +void +SystemInfo::_RetrieveNetwork() +{ + if (fRetrievedNetwork) + return; + + fBytesReceived = 0; + fBytesSent = 0; + fRetrievedNetwork = true; + + // we need a socket to talk to the networking stack + int socket = ::socket(AF_INET, SOCK_DGRAM, 0); + if (socket < 0) + return; + + // get a list of all interfaces + + ifconf config; + config.ifc_len = sizeof(config.ifc_value); + if (ioctl(socket, SIOCGIFCOUNT, &config, sizeof(struct ifconf)) < 0 + || config.ifc_value == 0) { + close(socket); + return; + } + + uint32 count = (uint32)config.ifc_value; + + void *buffer = malloc(count * sizeof(struct ifreq)); + if (buffer == NULL) { + close(socket); + return; + } + + config.ifc_len = count * sizeof(struct ifreq); + config.ifc_buf = buffer; + if (ioctl(socket, SIOCGIFCONF, &config, sizeof(struct ifconf)) < 0) { + close(socket); + return; + } + + ifreq *interface = (ifreq *)buffer; + + for (uint32 i = 0; i < count; i++) { + ifreq request; + strlcpy(request.ifr_name, interface->ifr_name, IF_NAMESIZE); + + if (ioctl(socket, SIOCGIFSTATS, &request, sizeof(struct ifreq)) == 0) { + fBytesReceived += request.ifr_stats.receive.bytes; + fBytesSent += request.ifr_stats.send.bytes; + } + + interface = (ifreq *)((addr_t)interface + IF_NAMESIZE + + interface->ifr_addr.sa_len); + } + + free(buffer); + close(socket); +} + + +uint64 +SystemInfo::NetworkReceived() +{ + _RetrieveNetwork(); + return fBytesReceived; +} + + +uint64 +SystemInfo::NetworkSent() +{ + _RetrieveNetwork(); + return fBytesSent; +} diff --git a/src/apps/activitymonitor/SystemInfo.h b/src/apps/activitymonitor/SystemInfo.h index e88976cf53..58021ba241 100644 --- a/src/apps/activitymonitor/SystemInfo.h +++ b/src/apps/activitymonitor/SystemInfo.h @@ -25,11 +25,20 @@ public: uint32 MaxTeams() const; bigtime_t Time() const { return fTime; } + uint32 CPUCount() const { return fSystemInfo.cpu_count; } const system_info& Info() const { return fSystemInfo; } + uint64 NetworkReceived(); + uint64 NetworkSent(); + private: + void _RetrieveNetwork(); + system_info fSystemInfo; bigtime_t fTime; + bool fRetrievedNetwork; + uint64 fBytesReceived; + uint64 fBytesSent; }; #endif // SYSTEM_INFO_H