summaryrefslogtreecommitdiffstats
path: root/Meter.c
diff options
context:
space:
mode:
Diffstat (limited to 'Meter.c')
-rw-r--r--Meter.c408
1 files changed, 220 insertions, 188 deletions
diff --git a/Meter.c b/Meter.c
index 3e5281c7..3dbdfcc6 100644
--- a/Meter.c
+++ b/Meter.c
@@ -1,7 +1,7 @@
/*
htop - Meter.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPLv2, see the COPYING file
+Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
@@ -13,88 +13,26 @@ in the source distribution for its full text.
#include <math.h>
#include <stdlib.h>
#include <string.h>
-#include <stdio.h>
#include "CRT.h"
#include "Macros.h"
#include "Object.h"
#include "ProvideCurses.h"
#include "RichString.h"
+#include "Row.h"
#include "Settings.h"
#include "XUtils.h"
#define GRAPH_HEIGHT 4 /* Unit: rows (lines) */
-const MeterClass Meter_class = {
- .super = {
- .extends = Class(Object)
- }
-};
-
-Meter* Meter_new(const struct ProcessList_* pl, unsigned int param, const MeterClass* type) {
- Meter* this = xCalloc(1, sizeof(Meter));
- Object_setClass(this, type);
- this->h = 1;
- this->param = param;
- this->pl = pl;
- this->curItems = type->maxItems;
- this->curAttributes = NULL;
- this->values = type->maxItems ? xCalloc(type->maxItems, sizeof(double)) : NULL;
- this->total = type->total;
- this->caption = xStrdup(type->caption);
- if (Meter_initFn(this)) {
- Meter_init(this);
- }
- Meter_setMode(this, type->defaultMode);
- return this;
-}
-
-int Meter_humanUnit(char* buffer, unsigned long int value, size_t size) {
- const char* prefix = "KMGTPEZY";
- unsigned long int powi = 1;
- unsigned int powj = 1, precision = 2;
-
- for (;;) {
- if (value / 1024 < powi)
- break;
-
- if (prefix[1] == '\0')
- break;
-
- powi *= 1024;
- ++prefix;
- }
+typedef struct MeterMode_ {
+ Meter_Draw draw;
+ const char* uiName;
+ int h;
+} MeterMode;
- if (*prefix == 'K')
- precision = 0;
-
- for (; precision > 0; precision--) {
- powj *= 10;
- if (value / powi < powj)
- break;
- }
-
- return snprintf(buffer, size, "%.*f%c", precision, (double) value / powi, *prefix);
-}
-
-void Meter_delete(Object* cast) {
- if (!cast)
- return;
-
- Meter* this = (Meter*) cast;
- if (Meter_doneFn(this)) {
- Meter_done(this);
- }
- free(this->drawData);
- free(this->caption);
- free(this->values);
- free(this);
-}
-
-void Meter_setCaption(Meter* this, const char* caption) {
- free_and_xStrdup(&this->caption, caption);
-}
+/* Meter drawing modes */
static inline void Meter_displayBuffer(const Meter* this, RichString* out) {
if (Object_displayFn(this)) {
@@ -104,58 +42,12 @@ static inline void Meter_displayBuffer(const Meter* this, RichString* out) {
}
}
-void Meter_setMode(Meter* this, int modeIndex) {
- if (modeIndex > 0 && modeIndex == this->mode) {
- return;
- }
-
- if (!modeIndex) {
- modeIndex = 1;
- }
-
- assert(modeIndex < LAST_METERMODE);
- if (Meter_defaultMode(this) == CUSTOM_METERMODE) {
- this->draw = Meter_drawFn(this);
- if (Meter_updateModeFn(this)) {
- Meter_updateMode(this, modeIndex);
- }
- } else {
- assert(modeIndex >= 1);
- free(this->drawData);
- this->drawData = NULL;
-
- const MeterMode* mode = Meter_modes[modeIndex];
- this->draw = mode->draw;
- this->h = mode->h;
- }
- this->mode = modeIndex;
-}
-
-ListItem* Meter_toListItem(const Meter* this, bool moving) {
- char mode[20];
- if (this->mode) {
- xSnprintf(mode, sizeof(mode), " [%s]", Meter_modes[this->mode]->uiName);
- } else {
- mode[0] = '\0';
- }
- char name[32];
- if (Meter_getUiNameFn(this))
- Meter_getUiName(this, name, sizeof(name));
- else
- xSnprintf(name, sizeof(name), "%s", Meter_uiName(this));
- char buffer[50];
- xSnprintf(buffer, sizeof(buffer), "%s%s", name, mode);
- ListItem* li = ListItem_new(buffer, 0);
- li->moving = moving;
- return li;
-}
-
/* ---------- TextMeterMode ---------- */
static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
const char* caption = Meter_getCaption(this);
attrset(CRT_colors[METER_TEXT]);
- mvaddnstr(y, x, caption, w - 1);
+ mvaddnstr(y, x, caption, w);
attrset(CRT_colors[RESET_COLOR]);
int captionLen = strlen(caption);
@@ -166,7 +58,7 @@ static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
RichString_begin(out);
Meter_displayBuffer(this, &out);
- RichString_printoffnVal(out, y, x, 0, w - 1);
+ RichString_printoffnVal(out, y, x, 0, w);
RichString_delete(&out);
}
@@ -175,29 +67,34 @@ static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
static const char BarMeterMode_characters[] = "|#*@$%&.";
static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
+ // Draw the caption
const char* caption = Meter_getCaption(this);
- w -= 2;
attrset(CRT_colors[METER_TEXT]);
int captionLen = 3;
mvaddnstr(y, x, caption, captionLen);
x += captionLen;
w -= captionLen;
+
+ // Draw the bar borders
attrset(CRT_colors[BAR_BORDER]);
mvaddch(y, x, '[');
+ w--;
mvaddch(y, x + MAXIMUM(w, 0), ']');
+ w--;
attrset(CRT_colors[RESET_COLOR]);
- w--;
x++;
- if (w < 1)
+ if (w < 1) {
return;
+ }
// The text in the bar is right aligned;
// Pad with maximal spaces and then calculate needed starting position offset
RichString_begin(bar);
RichString_appendChr(&bar, 0, ' ', w);
RichString_appendWide(&bar, 0, this->txtBuffer);
+
int startPos = RichString_sizeVal(bar) - w;
if (startPos > w) {
// Text is too large for bar
@@ -214,6 +111,7 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
// If still too large, print the start not the end
startPos = MINIMUM(startPos, w);
}
+
assert(startPos >= 0);
assert(startPos <= w);
assert(startPos + w <= RichString_sizeVal(bar));
@@ -224,8 +122,8 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
int offset = 0;
for (uint8_t i = 0; i < this->curItems; i++) {
double value = this->values[i];
- value = CLAMP(value, 0.0, this->total);
- if (value > 0) {
+ if (isPositive(value) && this->total > 0.0) {
+ value = MINIMUM(value, this->total);
blockSizes[i] = ceil((value / this->total) * w);
} else {
blockSizes[i] = 0;
@@ -236,6 +134,7 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
for (int j = offset; j < nextOffset; j++)
if (RichString_getCharVal(bar, startPos + j) == ' ') {
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
+ assert(i < strlen(BarMeterMode_characters));
RichString_setChar(&bar, startPos + j, BarMeterMode_characters[i]);
} else {
RichString_setChar(&bar, startPos + j, '|');
@@ -287,13 +186,46 @@ static const char* const GraphMeterMode_dotsAscii[] = {
};
static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
- const ProcessList* pl = this->pl;
+ const char* caption = Meter_getCaption(this);
+ attrset(CRT_colors[METER_TEXT]);
+ const int captionLen = 3;
+ mvaddnstr(y, x, caption, captionLen);
+ x += captionLen;
+ w -= captionLen;
+
+ GraphData* data = &this->drawData;
+ assert(data->nValues / 2 <= INT_MAX);
+ if (w > (int)(data->nValues / 2) && MAX_METER_GRAPHDATA_VALUES > data->nValues) {
+ size_t oldNValues = data->nValues;
+ data->nValues = MAXIMUM(oldNValues + oldNValues / 2, (size_t)w * 2);
+ data->nValues = MINIMUM(data->nValues, MAX_METER_GRAPHDATA_VALUES);
+ data->values = xReallocArray(data->values, data->nValues, sizeof(*data->values));
+ memmove(data->values + (data->nValues - oldNValues), data->values, oldNValues * sizeof(*data->values));
+ memset(data->values, 0, (data->nValues - oldNValues) * sizeof(*data->values));
+ }
+
+ const size_t nValues = data->nValues;
+ if (nValues < 1)
+ return;
+
+ const Machine* host = this->host;
+ if (!timercmp(&host->realtime, &(data->time), <)) {
+ int globalDelay = host->settings->delay;
+ struct timeval delay = { .tv_sec = globalDelay / 10, .tv_usec = (globalDelay % 10) * 100000L };
+ timeradd(&host->realtime, &delay, &(data->time));
+
+ memmove(&data->values[0], &data->values[1], (nValues - 1) * sizeof(*data->values));
+
+ data->values[nValues - 1] = sumPositiveValues(this->values, this->curItems);
+ }
+
+ if (w <= 0)
+ return;
- if (!this->drawData) {
- this->drawData = xCalloc(1, sizeof(GraphData));
+ if ((size_t)w > nValues / 2) {
+ x += w - nValues / 2;
+ w = nValues / 2;
}
- GraphData* data = this->drawData;
- const int nValues = METER_GRAPHDATA_SIZE;
const char* const* GraphMeterMode_dots;
int GraphMeterMode_pixPerRow;
@@ -308,38 +240,12 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
GraphMeterMode_pixPerRow = PIXPERROW_ASCII;
}
- const char* caption = Meter_getCaption(this);
- attrset(CRT_colors[METER_TEXT]);
- int captionLen = 3;
- mvaddnstr(y, x, caption, captionLen);
- x += captionLen;
- w -= captionLen;
-
- if (!timercmp(&pl->realtime, &(data->time), <)) {
- int globalDelay = this->pl->settings->delay;
- struct timeval delay = { .tv_sec = globalDelay / 10, .tv_usec = (globalDelay - ((globalDelay / 10) * 10)) * 100000 };
- timeradd(&pl->realtime, &delay, &(data->time));
-
- for (int i = 0; i < nValues - 1; i++)
- data->values[i] = data->values[i + 1];
-
- double value = 0.0;
- for (uint8_t i = 0; i < this->curItems; i++)
- value += this->values[i];
- data->values[nValues - 1] = value;
- }
-
- int i = nValues - (w * 2) + 2, k = 0;
- if (i < 0) {
- k = -i / 2;
- i = 0;
- }
- for (; i < nValues - 1; i += 2, k++) {
+ size_t i = nValues - (size_t)w * 2;
+ for (int col = 0; i < nValues - 1; i += 2, col++) {
int pix = GraphMeterMode_pixPerRow * GRAPH_HEIGHT;
- if (this->total < 1)
- this->total = 1;
- int v1 = CLAMP((int) lround(data->values[i] / this->total * pix), 1, pix);
- int v2 = CLAMP((int) lround(data->values[i + 1] / this->total * pix), 1, pix);
+ double total = MAXIMUM(this->total, 1);
+ int v1 = CLAMP((int) lround(data->values[i] / total * pix), 1, pix);
+ int v2 = CLAMP((int) lround(data->values[i + 1] / total * pix), 1, pix);
int colorIdx = GRAPH_1;
for (int line = 0; line < GRAPH_HEIGHT; line++) {
@@ -347,7 +253,7 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
int line2 = CLAMP(v2 - (GraphMeterMode_pixPerRow * (GRAPH_HEIGHT - 1 - line)), 0, GraphMeterMode_pixPerRow);
attrset(CRT_colors[colorIdx]);
- mvaddstr(y + line, x + k, GraphMeterMode_dots[line1 * (GraphMeterMode_pixPerRow + 1) + line2]);
+ mvaddstr(y + line, x + col, GraphMeterMode_dots[line1 * (GraphMeterMode_pixPerRow + 1) + line2]);
colorIdx = GRAPH_2;
}
}
@@ -367,7 +273,7 @@ static const char* const LEDMeterMode_digitsAscii[] = {
static const char* const LEDMeterMode_digitsUtf8[] = {
"┌──┐", " ┐ ", "╶──┐", "╶──┐", "╷ ╷", "┌──╴", "┌──╴", "╶──┐", "┌──┐", "┌──┐",
"│ │", " │ ", "┌──┘", " ──┤", "└──┤", "└──┐", "├──┐", " │", "├──┤", "└──┤",
- "└──┘", " ╵ ", "└──╴", "╶──┘", " ╵", "╶──┘", "└──┘", " ╵", "└──┘", " ──┘"
+ "└──┘", " ╵ ", "└──╴", "╶──┘", " ╵", "╶──┘", "└──┘", " ╵", "└──┘", "╶──┘"
};
#endif
@@ -379,7 +285,7 @@ static void LEDMeterMode_drawDigit(int x, int y, int n) {
mvaddstr(y + i, x, LEDMeterMode_digits[i * 10 + n]);
}
-static void LEDMeterMode_draw(Meter* this, int x, int y, ATTR_UNUSED int w) {
+static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
#ifdef HAVE_LIBNCURSESW
if (CRT_utf8)
LEDMeterMode_digits = LEDMeterMode_digitsUtf8;
@@ -403,9 +309,14 @@ static void LEDMeterMode_draw(Meter* this, int x, int y, ATTR_UNUSED int w) {
for (int i = 0; i < len; i++) {
int c = RichString_getCharVal(out, i);
if (c >= '0' && c <= '9') {
+ if (xx - x + 4 > w)
+ break;
+
LEDMeterMode_drawDigit(xx, y, c - '0');
xx += 4;
} else {
+ if (xx - x + 1 > w)
+ break;
#ifdef HAVE_LIBNCURSESW
const cchar_t wc = { .chars = { c, '\0' }, .attr = 0 }; /* use LED_COLOR from attrset() */
mvadd_wch(yText, xx, &wc);
@@ -419,38 +330,159 @@ static void LEDMeterMode_draw(Meter* this, int x, int y, ATTR_UNUSED int w) {
RichString_delete(&out);
}
-static MeterMode BarMeterMode = {
- .uiName = "Bar",
- .h = 1,
- .draw = BarMeterMode_draw,
+static const MeterMode Meter_modes[] = {
+ [0] = {
+ .uiName = NULL,
+ .h = 0,
+ .draw = NULL,
+ },
+ [BAR_METERMODE] = {
+ .uiName = "Bar",
+ .h = 1,
+ .draw = BarMeterMode_draw,
+ },
+ [TEXT_METERMODE] = {
+ .uiName = "Text",
+ .h = 1,
+ .draw = TextMeterMode_draw,
+ },
+ [GRAPH_METERMODE] = {
+ .uiName = "Graph",
+ .h = GRAPH_HEIGHT,
+ .draw = GraphMeterMode_draw,
+ },
+ [LED_METERMODE] = {
+ .uiName = "LED",
+ .h = 3,
+ .draw = LEDMeterMode_draw,
+ },
};
-static MeterMode TextMeterMode = {
- .uiName = "Text",
- .h = 1,
- .draw = TextMeterMode_draw,
-};
+/* Meter class and methods */
-static MeterMode GraphMeterMode = {
- .uiName = "Graph",
- .h = GRAPH_HEIGHT,
- .draw = GraphMeterMode_draw,
+const MeterClass Meter_class = {
+ .super = {
+ .extends = Class(Object)
+ }
};
-static MeterMode LEDMeterMode = {
- .uiName = "LED",
- .h = 3,
- .draw = LEDMeterMode_draw,
-};
+Meter* Meter_new(const Machine* host, unsigned int param, const MeterClass* type) {
+ Meter* this = xCalloc(1, sizeof(Meter));
+ Object_setClass(this, type);
+ this->h = 1;
+ this->param = param;
+ this->host = host;
+ this->curItems = type->maxItems;
+ this->curAttributes = NULL;
+ this->values = type->maxItems ? xCalloc(type->maxItems, sizeof(double)) : NULL;
+ this->total = type->total;
+ this->caption = xStrdup(type->caption);
+ if (Meter_initFn(this)) {
+ Meter_init(this);
+ }
+ Meter_setMode(this, type->defaultMode);
+ return this;
+}
-const MeterMode* const Meter_modes[] = {
- NULL,
- &BarMeterMode,
- &TextMeterMode,
- &GraphMeterMode,
- &LEDMeterMode,
- NULL
-};
+/* Converts 'value' in kibibytes into a human readable string.
+ Example output strings: "0K", "1023K", "98.7M" and "1.23G" */
+int Meter_humanUnit(char* buffer, double value, size_t size) {
+ size_t i = 0;
+
+ assert(value >= 0.0);
+ while (value >= ONE_K) {
+ if (i >= ARRAYSIZE(unitPrefixes) - 1) {
+ if (value > 9999.0) {
+ return xSnprintf(buffer, size, "inf");
+ }
+ break;
+ }
+
+ value /= ONE_K;
+ ++i;
+ }
+
+ int precision = 0;
+
+ if (i > 0) {
+ // Fraction digits for mebibytes and above
+ precision = value <= 99.9 ? (value <= 9.99 ? 2 : 1) : 0;
+
+ // Round up if 'value' is in range (99.9, 100) or (9.99, 10)
+ if (precision < 2) {
+ double limit = precision == 1 ? 10.0 : 100.0;
+ if (value < limit) {
+ value = limit;
+ }
+ }
+ }
+
+ return xSnprintf(buffer, size, "%.*f%c", precision, value, unitPrefixes[i]);
+}
+
+void Meter_delete(Object* cast) {
+ if (!cast)
+ return;
+
+ Meter* this = (Meter*) cast;
+ if (Meter_doneFn(this)) {
+ Meter_done(this);
+ }
+ free(this->drawData.values);
+ free(this->caption);
+ free(this->values);
+ free(this);
+}
+
+void Meter_setCaption(Meter* this, const char* caption) {
+ free_and_xStrdup(&this->caption, caption);
+}
+
+void Meter_setMode(Meter* this, MeterModeId modeIndex) {
+ if (modeIndex > 0 && modeIndex == this->mode) {
+ return;
+ }
+
+ if (modeIndex == 0) {
+ modeIndex = 1;
+ }
+
+ assert(modeIndex < LAST_METERMODE);
+ if (Meter_updateModeFn(this)) {
+ assert(Meter_drawFn(this));
+ this->draw = Meter_drawFn(this);
+ Meter_updateMode(this, modeIndex);
+ } else {
+ assert(modeIndex >= 1);
+ free(this->drawData.values);
+ this->drawData.values = NULL;
+ this->drawData.nValues = 0;
+
+ const MeterMode* mode = &Meter_modes[modeIndex];
+ this->draw = mode->draw;
+ this->h = mode->h;
+ }
+ this->mode = modeIndex;
+}
+
+ListItem* Meter_toListItem(const Meter* this, bool moving) {
+ char mode[20];
+ if (this->mode > 0) {
+ xSnprintf(mode, sizeof(mode), " [%s]", Meter_modes[this->mode].uiName);
+ } else {
+ mode[0] = '\0';
+ }
+ char name[32];
+ if (Meter_getUiNameFn(this))
+ Meter_getUiName(this, name, sizeof(name));
+ else
+ xSnprintf(name, sizeof(name), "%s", Meter_uiName(this));
+ char buffer[50];
+ xSnprintf(buffer, sizeof(buffer), "%s%s", name, mode);
+ ListItem* li = ListItem_new(buffer, 0);
+ li->moving = moving;
+ return li;
+}
/* Blank meter */