| /* |
| htop - Meter.c |
| (C) 2004,2005 Hisham H. Muhammad |
| Released under the GNU GPL, see the COPYING file |
| in the source distribution for its full text. |
| */ |
| |
| #include "Meter.h" |
| #include "Object.h" |
| #include "CRT.h" |
| #include "ListItem.h" |
| #include "String.h" |
| |
| #include <stdlib.h> |
| #include <curses.h> |
| #include <string.h> |
| #include <math.h> |
| |
| #include "debug.h" |
| #include <assert.h> |
| |
| #define METER_BARBUFFER_LEN 128 |
| #define METER_GRAPHBUFFER_LEN 128 |
| |
| /*{ |
| |
| typedef struct Meter_ Meter; |
| |
| typedef void(*Meter_SetValues)(Meter*); |
| typedef void(*Meter_Draw)(Meter*, int, int, int); |
| |
| typedef enum MeterMode_ { |
| UNSET, |
| BAR, |
| TEXT, |
| GRAPH, |
| LED, |
| LAST_METERMODE |
| } MeterMode; |
| |
| struct Meter_ { |
| Object super; |
| |
| int h; |
| int w; |
| Meter_Draw draw; |
| Meter_SetValues setValues; |
| int items; |
| int* attributes; |
| double* values; |
| double total; |
| char* caption; |
| char* name; |
| union { |
| RichString* rs; |
| char* c; |
| double* graph; |
| } displayBuffer; |
| MeterMode mode; |
| }; |
| |
| extern char* METER_CLASS; |
| |
| }*/ |
| |
| #ifndef MIN |
| #define MIN(a,b) ((a)<(b)?(a):(b)) |
| #endif |
| |
| /* private property */ |
| char* METER_CLASS = "Meter"; |
| |
| /* private */ |
| char* Meter_ledDigits[3][10] = { |
| { " __ "," "," __ "," __ "," "," __ "," __ "," __ "," __ "," __ "}, |
| { "| |"," |"," __|"," __|","|__|","|__ ","|__ "," |","|__|","|__|"}, |
| { "|__|"," |","|__ "," __|"," |"," __|","|__|"," |","|__|"," __|"}, |
| }; |
| |
| /* private property */ |
| char Meter_barCharacters[] = "|#*@$%&"; |
| |
| /* private property */ |
| static RichString Meter_stringBuffer; |
| |
| Meter* Meter_new(char* name, char* caption, int items) { |
| Meter* this = malloc(sizeof(Meter)); |
| Meter_init(this, name, caption, items); |
| return this; |
| } |
| |
| void Meter_init(Meter* this, char* name, char* caption, int items) { |
| ((Object*)this)->delete = Meter_delete; |
| ((Object*)this)->class = METER_CLASS; |
| this->items = items; |
| this->name = name; |
| this->caption = caption; |
| this->attributes = malloc(sizeof(int) * items); |
| this->values = malloc(sizeof(double) * items); |
| this->displayBuffer.c = NULL; |
| this->mode = UNSET; |
| this->h = 0; |
| } |
| |
| void Meter_delete(Object* cast) { |
| Meter* this = (Meter*) cast; |
| assert (this != NULL); |
| Meter_done(this); |
| free(this); |
| } |
| |
| /* private */ |
| void Meter_freeBuffer(Meter* this) { |
| switch (this->mode) { |
| case BAR: { |
| free(this->displayBuffer.c); |
| break; |
| } |
| case GRAPH: { |
| free(this->displayBuffer.graph); |
| break; |
| } |
| default: { |
| } |
| } |
| this->h = 0; |
| } |
| |
| void Meter_done(Meter* this) { |
| free(this->caption); |
| free(this->attributes); |
| free(this->values); |
| free(this->name); |
| Meter_freeBuffer(this); |
| } |
| |
| /* private */ |
| void Meter_drawBar(Meter* this, int x, int y, int w) { |
| |
| w -= 2; |
| attrset(CRT_colors[METER_TEXT]); |
| mvaddstr(y, x, this->caption); |
| int captionLen = strlen(this->caption); |
| x += captionLen; |
| w -= captionLen; |
| attrset(CRT_colors[BAR_BORDER]); |
| mvaddch(y, x, '['); |
| mvaddch(y, x + w, ']'); |
| |
| w--; |
| x++; |
| char bar[w]; |
| |
| this->setValues(this); |
| |
| int blockSizes[10]; |
| for (int i = 0; i < w; i++) |
| bar[i] = ' '; |
| |
| sprintf(bar + (w-strlen(this->displayBuffer.c)), "%s", this->displayBuffer.c); |
| |
| // First draw in the bar[] buffer... |
| double total = 0.0; |
| int offset = 0; |
| for (int i = 0; i < this->items; i++) { |
| this->values[i] = MAX(this->values[i], 0); |
| this->values[i] = MIN(this->values[i], this->total); |
| double value = this->values[i]; |
| if (value > 0) { |
| blockSizes[i] = ceil((value/this->total) * w); |
| } else { |
| blockSizes[i] = 0; |
| } |
| int nextOffset = offset + blockSizes[i]; |
| // (Control against invalid values) |
| nextOffset = MAX(nextOffset, 0); |
| nextOffset = MIN(nextOffset, w); |
| for (int j = offset; j < nextOffset; j++) |
| if (bar[j] == ' ') { |
| if (CRT_colorScheme == COLORSCHEME_MONOCHROME) { |
| bar[j] = Meter_barCharacters[i]; |
| } else { |
| bar[j] = '|'; |
| } |
| } |
| offset = nextOffset; |
| total += this->values[i]; |
| } |
| |
| // ...then print the buffer. |
| offset = 0; |
| for (int i = 0; i < this->items; i++) { |
| attrset(CRT_colors[this->attributes[i]]); |
| mvaddnstr(y, x + offset, bar + offset, blockSizes[i]); |
| offset += blockSizes[i]; |
| offset = MAX(offset, 0); |
| offset = MIN(offset, w); |
| } |
| if (offset < w) { |
| attrset(CRT_colors[BAR_SHADOW]); |
| mvaddnstr(y, x + offset, bar + offset, w - offset); |
| } |
| |
| move(y, x + w + 1); |
| attrset(CRT_colors[RESET_COLOR]); |
| } |
| |
| /* private */ |
| void Meter_drawText(Meter* this, int x, int y, int w) { |
| this->setValues(this); |
| this->w = w; |
| attrset(CRT_colors[METER_TEXT]); |
| mvaddstr(y, x, this->caption); |
| int captionLen = strlen(this->caption); |
| w -= captionLen; |
| x += captionLen; |
| ((Object*)this)->display((Object*)this, this->displayBuffer.rs); |
| mvhline(y, x, ' ', CRT_colors[DEFAULT_COLOR]); |
| attrset(CRT_colors[RESET_COLOR]); |
| mvaddchstr(y, x, this->displayBuffer.rs->chstr); |
| } |
| |
| /* private */ |
| void Meter_drawDigit(int x, int y, int n) { |
| for (int i = 0; i < 3; i++) { |
| mvaddstr(y+i, x, Meter_ledDigits[i][n]); |
| } |
| } |
| |
| /* private */ |
| void Meter_drawLed(Meter* this, int x, int y, int w) { |
| this->setValues(this); |
| ((Object*)this)->display((Object*)this, this->displayBuffer.rs); |
| attrset(CRT_colors[LED_COLOR]); |
| mvaddstr(y+2, x, this->caption); |
| int xx = x + strlen(this->caption); |
| for (int i = 0; i < this->displayBuffer.rs->len; i++) { |
| char c = this->displayBuffer.rs->chstr[i]; |
| if (c >= '0' && c <= '9') { |
| Meter_drawDigit(xx, y, c-48); |
| xx += 4; |
| } else { |
| mvaddch(y+2, xx, c); |
| xx += 1; |
| } |
| } |
| attrset(CRT_colors[RESET_COLOR]); |
| } |
| |
| #define DrawDot(a,y,c) do { \ |
| attrset(a); \ |
| mvaddstr(y, x+k, c); \ |
| } while(0) |
| |
| /* private */ |
| void Meter_drawGraph(Meter* this, int x, int y, int w) { |
| |
| for (int i = 0; i < METER_GRAPHBUFFER_LEN - 1; i++) { |
| this->displayBuffer.graph[i] = this->displayBuffer.graph[i+1]; |
| } |
| |
| this->setValues(this); |
| |
| double value = 0.0; |
| for (int i = 0; i < this->items; i++) |
| value += this->values[i] / this->total; |
| this->displayBuffer.graph[METER_GRAPHBUFFER_LEN - 1] = value; |
| |
| for (int i = METER_GRAPHBUFFER_LEN - w, k = 0; i < METER_GRAPHBUFFER_LEN; i++, k++) { |
| double value = this->displayBuffer.graph[i]; |
| DrawDot( CRT_colors[DEFAULT_COLOR], y, " " ); |
| DrawDot( CRT_colors[DEFAULT_COLOR], y+1, " " ); |
| DrawDot( CRT_colors[DEFAULT_COLOR], y+2, " " ); |
| if (value >= 1.00) DrawDot( CRT_colors[GRAPH_1], y, "^" ); |
| else if (value >= 0.95) DrawDot( CRT_colors[GRAPH_1], y, "`" ); |
| else if (value >= 0.90) DrawDot( CRT_colors[GRAPH_1], y, "'" ); |
| else if (value >= 0.85) DrawDot( CRT_colors[GRAPH_2], y, "-" ); |
| else if (value >= 0.80) DrawDot( CRT_colors[GRAPH_2], y, "." ); |
| else if (value >= 0.75) DrawDot( CRT_colors[GRAPH_2], y, "," ); |
| else if (value >= 0.70) DrawDot( CRT_colors[GRAPH_3], y, "_" ); |
| else if (value >= 0.65) DrawDot( CRT_colors[GRAPH_3], y+1, "~" ); |
| else if (value >= 0.60) DrawDot( CRT_colors[GRAPH_3], y+1, "`" ); |
| else if (value >= 0.55) DrawDot( CRT_colors[GRAPH_4], y+1, "'" ); |
| else if (value >= 0.50) DrawDot( CRT_colors[GRAPH_4], y+1, "-" ); |
| else if (value >= 0.45) DrawDot( CRT_colors[GRAPH_4], y+1, "." ); |
| else if (value >= 0.40) DrawDot( CRT_colors[GRAPH_5], y+1, "," ); |
| else if (value >= 0.35) DrawDot( CRT_colors[GRAPH_5], y+1, "_" ); |
| else if (value >= 0.30) DrawDot( CRT_colors[GRAPH_6], y+2, "~" ); |
| else if (value >= 0.25) DrawDot( CRT_colors[GRAPH_7], y+2, "`" ); |
| else if (value >= 0.20) DrawDot( CRT_colors[GRAPH_7], y+2, "'" ); |
| else if (value >= 0.15) DrawDot( CRT_colors[GRAPH_7], y+2, "-" ); |
| else if (value >= 0.10) DrawDot( CRT_colors[GRAPH_8], y+2, "." ); |
| else if (value >= 0.05) DrawDot( CRT_colors[GRAPH_8], y+2, "," ); |
| else DrawDot( CRT_colors[GRAPH_9], y+2, "_" ); |
| } |
| attrset(CRT_colors[RESET_COLOR]); |
| } |
| |
| void Meter_setMode(Meter* this, MeterMode mode) { |
| Meter_freeBuffer(this); |
| switch (mode) { |
| case UNSET: { |
| // fallthrough to a sane default. |
| mode = TEXT; |
| } |
| case TEXT: { |
| this->draw = Meter_drawText; |
| this->displayBuffer.rs = & Meter_stringBuffer; |
| this->h = 1; |
| break; |
| } |
| case LED: { |
| this->draw = Meter_drawLed; |
| this->displayBuffer.rs = & Meter_stringBuffer; |
| this->h = 3; |
| break; |
| } |
| case BAR: { |
| this->draw = Meter_drawBar; |
| this->displayBuffer.c = malloc(METER_BARBUFFER_LEN); |
| this->h = 1; |
| break; |
| } |
| case GRAPH: { |
| this->draw = Meter_drawGraph; |
| this->displayBuffer.c = calloc(METER_GRAPHBUFFER_LEN, sizeof(double)); |
| this->h = 3; |
| break; |
| } |
| default: { |
| assert(false); |
| } |
| } |
| this->mode = mode; |
| } |
| |
| ListItem* Meter_toListItem(Meter* this) { |
| char buffer[50]; char* mode = NULL; |
| switch (this->mode) { |
| case BAR: mode = "Bar"; break; |
| case LED: mode = "LED"; break; |
| case TEXT: mode = "Text"; break; |
| case GRAPH: mode = "Graph"; break; |
| default: { |
| assert(false); |
| } |
| } |
| sprintf(buffer, "%s [%s]", this->name, mode); |
| return ListItem_new(buffer, 0); |
| } |