diff options
Diffstat (limited to 'Meter.c')
-rw-r--r-- | Meter.c | 408 |
1 files changed, 220 insertions, 188 deletions
@@ -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 */ |