Meter.c 13.4 KB
Newer Older
Hisham Muhammad's avatar
Hisham Muhammad committed
1
2
/*
htop - Meter.c
Hisham Muhammad's avatar
Hisham Muhammad committed
3
(C) 2004-2011 Hisham H. Muhammad
Hisham Muhammad's avatar
Hisham Muhammad committed
4
5
6
7
8
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/

#include "Meter.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
9
10

#include "RichString.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
11
12
13
#include "Object.h"
#include "CRT.h"
#include "String.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
14
#include "ListItem.h"
15
#include "Settings.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
16
17
18
19
20

#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
Hisham Muhammad's avatar
Hisham Muhammad committed
21
#include <assert.h>
22
#include <sys/time.h>
23

Hisham Muhammad's avatar
Hisham Muhammad committed
24
#define METER_BUFFER_LEN 256
Hisham Muhammad's avatar
Hisham Muhammad committed
25

26
27
#define GRAPH_DELAY (DEFAULT_DELAY/2)

Hisham Muhammad's avatar
Hisham Muhammad committed
28
/*{
Hisham Muhammad's avatar
Hisham Muhammad committed
29
#include "ListItem.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
30
31

#include <sys/time.h>
Hisham Muhammad's avatar
Hisham Muhammad committed
32
33
34

typedef struct Meter_ Meter;

35
36
37
typedef void(*Meter_Init)(Meter*);
typedef void(*Meter_Done)(Meter*);
typedef void(*Meter_UpdateMode)(Meter*, int);
38
typedef void(*Meter_SetValues)(Meter*, char*, int);
Hisham Muhammad's avatar
Hisham Muhammad committed
39
40
typedef void(*Meter_Draw)(Meter*, int, int, int);

41
42
43
44
45
46
47
48
49
50
typedef struct MeterClass_ {
   ObjectClass super;
   const Meter_Init init;
   const Meter_Done done;
   const Meter_UpdateMode updateMode;
   const Meter_Draw draw;
   const Meter_SetValues setValues;
   const int defaultMode;
   const double total;
   const int* attributes;
Hisham Muhammad's avatar
Hisham Muhammad committed
51
52
53
   const char* name;
   const char* uiName;
   const char* caption;
54
55
   const char maxItems;
   char curItems;
56
57
58
59
60
61
62
63
64
65
66
67
} MeterClass;

#define As_Meter(this_)                ((MeterClass*)((this_)->super.klass))
#define Meter_initFn(this_)            As_Meter(this_)->init
#define Meter_init(this_)              As_Meter(this_)->init((Meter*)(this_))
#define Meter_done(this_)              As_Meter(this_)->done((Meter*)(this_))
#define Meter_updateModeFn(this_)      As_Meter(this_)->updateMode
#define Meter_updateMode(this_, m_)    As_Meter(this_)->updateMode((Meter*)(this_), m_)
#define Meter_drawFn(this_)            As_Meter(this_)->draw
#define Meter_doneFn(this_)            As_Meter(this_)->done
#define Meter_setValues(this_, c_, i_) As_Meter(this_)->setValues((Meter*)(this_), c_, i_)
#define Meter_defaultMode(this_)       As_Meter(this_)->defaultMode
68
69
#define Meter_getItems(this_)          As_Meter(this_)->curItems
#define Meter_setItems(this_, n_)      As_Meter(this_)->curItems = (n_)
70
71
72
#define Meter_attributes(this_)        As_Meter(this_)->attributes
#define Meter_name(this_)              As_Meter(this_)->name
#define Meter_uiName(this_)            As_Meter(this_)->uiName
73
74
75

struct Meter_ {
   Object super;
76
77
   Meter_Draw draw;
   
78
79
80
   char* caption;
   int mode;
   int param;
81
   void* drawData;
82
   int h;
Hisham Muhammad's avatar
Hisham Muhammad committed
83
   struct ProcessList_* pl;
Hisham Muhammad's avatar
Hisham Muhammad committed
84
85
86
87
   double* values;
   double total;
};

88
89
90
91
92
typedef struct MeterMode_ {
   Meter_Draw draw;
   const char* uiName;
   int h;
} MeterMode;
93

94
95
96
97
98
99
100
101
102
typedef enum {
   CUSTOM_METERMODE = 0,
   BAR_METERMODE,
   TEXT_METERMODE,
   GRAPH_METERMODE,
   LED_METERMODE,
   LAST_METERMODE
} MeterModeId;

103
104
105
106
107
typedef struct GraphData_ {
   struct timeval time;
   double values[METER_BUFFER_LEN];
} GraphData;

Hisham Muhammad's avatar
Hisham Muhammad committed
108
109
110
111
112
}*/

#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif
Hisham Muhammad's avatar
Hisham Muhammad committed
113
114
115
#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
Hisham Muhammad's avatar
Hisham Muhammad committed
116

117
118
119
120
121
MeterClass Meter_class = {
   .super = {
      .extends = Class(Object)
   }
};
Hisham Muhammad's avatar
Hisham Muhammad committed
122

Hisham Muhammad's avatar
Hisham Muhammad committed
123
Meter* Meter_new(struct ProcessList_* pl, int param, MeterClass* type) {
124
   Meter* this = calloc(1, sizeof(Meter));
125
   Object_setClass(this, type);
126
127
128
   this->h = 1;
   this->param = param;
   this->pl = pl;
129
130
131
132
133
134
   char maxItems = type->maxItems;
   if (maxItems == 0) {
      maxItems = 1;
   }
   type->curItems = maxItems;
   this->values = calloc(maxItems, sizeof(double));
135
136
   this->total = type->total;
   this->caption = strdup(type->caption);
137
138
139
   if (Meter_initFn(this))
      Meter_init(this);
   Meter_setMode(this, type->defaultMode);
140
   return this;
Hisham Muhammad's avatar
Hisham Muhammad committed
141
142
143
}

void Meter_delete(Object* cast) {
144
145
   if (!cast)
      return;
Hisham Muhammad's avatar
Hisham Muhammad committed
146
   Meter* this = (Meter*) cast;
147
148
   if (Meter_doneFn(this)) {
      Meter_done(this);
149
   }
150
151
   if (this->drawData)
      free(this->drawData);
152
153
   free(this->caption);
   free(this->values);
Hisham Muhammad's avatar
Hisham Muhammad committed
154
155
156
   free(this);
}

Hisham Muhammad's avatar
Hisham Muhammad committed
157
void Meter_setCaption(Meter* this, const char* caption) {
158
159
160
161
   free(this->caption);
   this->caption = strdup(caption);
}

162
static inline void Meter_displayBuffer(Meter* this, char* buffer, RichString* out) {
163
164
   if (Object_displayFn(this)) {
      Object_display(this, out);
165
   } else {
166
      RichString_write(out, CRT_colors[Meter_attributes(this)[0]], buffer);
Hisham Muhammad's avatar
Hisham Muhammad committed
167
   }
168
169
170
}

void Meter_setMode(Meter* this, int modeIndex) {
Hisham Muhammad's avatar
Hisham Muhammad committed
171
   if (modeIndex > 0 && modeIndex == this->mode)
172
173
174
175
      return;
   if (!modeIndex)
      modeIndex = 1;
   assert(modeIndex < LAST_METERMODE);
176
177
178
179
   if (Meter_defaultMode(this) == CUSTOM_METERMODE) {
      this->draw = Meter_drawFn(this);
      if (Meter_updateModeFn(this))
         Meter_updateMode(this, modeIndex);
180
   } else {
Hisham Muhammad's avatar
Hisham Muhammad committed
181
      assert(modeIndex >= 1);
182
183
184
      if (this->drawData)
         free(this->drawData);
      this->drawData = NULL;
Hisham Muhammad's avatar
Hisham Muhammad committed
185
186
187
188

      MeterMode* mode = Meter_modes[modeIndex];
      this->draw = mode->draw;
      this->h = mode->h;
Hisham Muhammad's avatar
Hisham Muhammad committed
189
   }
190
   this->mode = modeIndex;
Hisham Muhammad's avatar
Hisham Muhammad committed
191
192
}

193
194
195
196
197
198
199
200
201
202
203
204
ListItem* Meter_toListItem(Meter* this) {
   char mode[21];
   if (this->mode)
      snprintf(mode, 20, " [%s]", Meter_modes[this->mode]->uiName);
   else
      mode[0] = '\0';
   char number[11];
   if (this->param > 0)
      snprintf(number, 10, " %d", this->param);
   else
      number[0] = '\0';
   char buffer[51];
205
   snprintf(buffer, 50, "%s%s%s", Meter_uiName(this), number, mode);
206
   return ListItem_new(buffer, 0);
Hisham Muhammad's avatar
Hisham Muhammad committed
207
208
}

209
210
/* ---------- TextMeterMode ---------- */

211
static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
212
   char buffer[METER_BUFFER_LEN];
213
   Meter_setValues(this, buffer, METER_BUFFER_LEN - 1);
Hisham Muhammad's avatar
Hisham Muhammad committed
214
   (void) w;
215
216
217
218
219
220
221

   attrset(CRT_colors[METER_TEXT]);
   mvaddstr(y, x, this->caption);
   int captionLen = strlen(this->caption);
   x += captionLen;
   mvhline(y, x, ' ', CRT_colors[DEFAULT_COLOR]);
   attrset(CRT_colors[RESET_COLOR]);
222
223
224
225
   RichString_begin(out);
   Meter_displayBuffer(this, buffer, &out);
   RichString_printVal(out, y, x);
   RichString_end(out);
226
227
228
229
}

/* ---------- BarMeterMode ---------- */

230
static char BarMeterMode_characters[] = "|#*@$%&";
231

232
static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
233
   char buffer[METER_BUFFER_LEN];
234
   Meter_setValues(this, buffer, METER_BUFFER_LEN - 1);
235

Hisham Muhammad's avatar
Hisham Muhammad committed
236
237
   w -= 2;
   attrset(CRT_colors[METER_TEXT]);
Hisham Muhammad's avatar
Hisham Muhammad committed
238
239
   int captionLen = 3;
   mvaddnstr(y, x, this->caption, captionLen);
Hisham Muhammad's avatar
Hisham Muhammad committed
240
241
242
243
244
245
246
247
   x += captionLen;
   w -= captionLen;
   attrset(CRT_colors[BAR_BORDER]);
   mvaddch(y, x, '[');
   mvaddch(y, x + w, ']');
   
   w--;
   x++;
248
249
250
251
252
253

   if (w < 1) {
      attrset(CRT_colors[RESET_COLOR]);
      return;
   }
   char bar[w + 1];
Hisham Muhammad's avatar
Hisham Muhammad committed
254
255
256
257
258
   
   int blockSizes[10];
   for (int i = 0; i < w; i++)
      bar[i] = ' ';

259
   const size_t barOffset = w - MIN((int)strlen(buffer), w);
260
   snprintf(bar + barOffset, w - barOffset + 1, "%s", buffer);
Hisham Muhammad's avatar
Hisham Muhammad committed
261
262
263

   // First draw in the bar[] buffer...
   int offset = 0;
264
265
   int items = Meter_getItems(this);
   for (int i = 0; i < items; i++) {
Hisham Muhammad's avatar
Hisham Muhammad committed
266
      double value = this->values[i];
267
268
      value = MAX(value, 0);
      value = MIN(value, this->total);
Hisham Muhammad's avatar
Hisham Muhammad committed
269
270
271
272
273
274
275
      if (value > 0) {
         blockSizes[i] = ceil((value/this->total) * w);
      } else {
         blockSizes[i] = 0;
      }
      int nextOffset = offset + blockSizes[i];
      // (Control against invalid values)
276
      nextOffset = MIN(MAX(nextOffset, 0), w);
Hisham Muhammad's avatar
Hisham Muhammad committed
277
278
279
      for (int j = offset; j < nextOffset; j++)
         if (bar[j] == ' ') {
            if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
280
               bar[j] = BarMeterMode_characters[i];
Hisham Muhammad's avatar
Hisham Muhammad committed
281
282
283
284
285
286
287
288
289
            } else {
               bar[j] = '|';
            }
         }
      offset = nextOffset;
   }

   // ...then print the buffer.
   offset = 0;
290
291
   for (int i = 0; i < items; i++) {
      attrset(CRT_colors[Meter_attributes(this)[i]]);
Hisham Muhammad's avatar
Hisham Muhammad committed
292
293
294
295
296
297
298
299
300
301
302
303
304
305
      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]);
}

306
307
/* ---------- GraphMeterMode ---------- */

Hisham Muhammad's avatar
Hisham Muhammad committed
308
309
310
311
312
313
314
static const char* GraphMeterMode_dotsUtf8[5][5] = {
   { /*00*/"⠀", /*01*/"⢀", /*02*/"⢠", /*03*/"⢰", /*04*/ "⢸" },
   { /*10*/"⡀", /*11*/"⣀", /*12*/"⣠", /*13*/"⣰", /*14*/ "⣸" },
   { /*20*/"⡄", /*21*/"⣄", /*22*/"⣤", /*23*/"⣴", /*24*/ "⣼" },
   { /*30*/"⡆", /*31*/"⣆", /*32*/"⣦", /*33*/"⣶", /*34*/ "⣾" },
   { /*40*/"⡇", /*41*/"⣇", /*42*/"⣧", /*43*/"⣷", /*44*/ "⣿" },
};
315

Hisham Muhammad's avatar
Hisham Muhammad committed
316
317
318
319
320
321
static const char* GraphMeterMode_dotsAscii[5][5] = {
   { /*00*/" ", /*01*/".", /*02*/".", /*03*/":", /*04*/ ":" },
   { /*10*/"⡀", /*11*/".", /*12*/".", /*13*/":", /*14*/ ":" },
   { /*20*/".", /*21*/".", /*22*/".", /*23*/":", /*24*/ ":" },
   { /*30*/":", /*31*/":", /*32*/":", /*33*/":", /*34*/ ":" },
   { /*40*/":", /*41*/":", /*42*/":", /*43*/":", /*44*/ ":" },
322
323
};

Hisham Muhammad's avatar
Hisham Muhammad committed
324
static const char* (*GraphMeterMode_dots)[5];
325

326
static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
327

328
   if (!this->drawData) this->drawData = calloc(1, sizeof(GraphData));
Hisham Muhammad's avatar
Hisham Muhammad committed
329
    GraphData* data = (GraphData*) this->drawData;
330
   const int nValues = METER_BUFFER_LEN;
Hisham Muhammad's avatar
Hisham Muhammad committed
331
332
333
334
335
336
337
338
339
340
341
342

   if (CRT_utf8) {
      GraphMeterMode_dots = GraphMeterMode_dotsUtf8;
   } else {
      GraphMeterMode_dots = GraphMeterMode_dotsAscii;
   }

   attrset(CRT_colors[METER_TEXT]);
   int captionLen = 3;
   mvaddnstr(y, x, this->caption, captionLen);
   x += captionLen;
   w -= captionLen;
343
   
344
345
346
   struct timeval now;
   gettimeofday(&now, NULL);
   if (!timercmp(&now, &(data->time), <)) {
347
      struct timeval delay = { .tv_sec = (int)(GRAPH_DELAY/10), .tv_usec = (GRAPH_DELAY-((int)(GRAPH_DELAY/10)*10)) * 100000 };
348
      timeradd(&now, &delay, &(data->time));
349

350
351
352
353
      for (int i = 0; i < nValues - 1; i++)
         data->values[i] = data->values[i+1];
   
      char buffer[nValues];
354
      Meter_setValues(this, buffer, nValues - 1);
355
356
   
      double value = 0.0;
357
358
      int items = Meter_getItems(this);
      for (int i = 0; i < items; i++)
359
360
361
362
363
         value += this->values[i];
      value /= this->total;
      data->values[nValues - 1] = value;
   }
   
Hisham Muhammad's avatar
Hisham Muhammad committed
364
365
366
367
   for (int i = nValues - (w*2) + 2, k = 0; i < nValues; i+=2, k++) {
      const double dot = (1.0 / 16);
      int v1 = data->values[i] / dot;
      int v2 = data->values[i+1] / dot;
368
      
Hisham Muhammad's avatar
Hisham Muhammad committed
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
      if (v1 == 0) v1 = 1;
      if (v2 == 0) v2 = 1;
      
      int level = 12;
      int colorIdx = GRAPH_1;
      for (int line = 0; line < 4; line++) {
         
         int line1 = MIN(4, MAX(0, v1 - level));
         int line2 = MIN(4, MAX(0, v2 - level));
 
         attrset(CRT_colors[colorIdx]);
         mvaddstr(y+line, x+k, GraphMeterMode_dots[line1][line2]);
         colorIdx = GRAPH_2;
         level -= 4;
      }
384
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
385
386
387
   attrset(CRT_colors[RESET_COLOR]);
}

388
389
/* ---------- LEDMeterMode ---------- */

390
static const char* LEDMeterMode_digitsAscii[3][10] = {
391
392
393
394
   { " __ ","    "," __ "," __ ","    "," __ "," __ "," __ "," __ "," __ "},
   { "|  |","   |"," __|"," __|","|__|","|__ ","|__ ","   |","|__|","|__|"},
   { "|__|","   |","|__ "," __|","   |"," __|","|__|","   |","|__|"," __|"},
};
Hisham Muhammad's avatar
Hisham Muhammad committed
395

396
397
398
399
400
401
static const char* LEDMeterMode_digitsUtf8[3][10] = {
   { "┌──┐","  ┐ ","╶──┐","╶──┐","╷  ╷","┌──╴","┌──╴","╶──┐","┌──┐","┌──┐"},
   { "│  │","  │ ","┌──┘"," ──┤","└──┤","└──┐","├──┐","   │","├──┤","└──┤"},
   { "└──┘","  ╵ ","└──╴","╶──┘","   ╵","╶──┘","└──┘","   ╵","└──┘"," ──┘"},
};

Hisham Muhammad's avatar
Hisham Muhammad committed
402
403
static const char* (*LEDMeterMode_digits)[10];

404
static void LEDMeterMode_drawDigit(int x, int y, int n) {
Hisham Muhammad's avatar
Hisham Muhammad committed
405
406
   for (int i = 0; i < 3; i++)
      mvaddstr(y+i, x, LEDMeterMode_digits[i][n]);
407
408
}

409
static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
Hisham Muhammad's avatar
Hisham Muhammad committed
410
   (void) w;
Hisham Muhammad's avatar
Hisham Muhammad committed
411
412
413
414
415
416
417

   if (CRT_utf8) {
      LEDMeterMode_digits = LEDMeterMode_digitsUtf8;
   } else {
      LEDMeterMode_digits = LEDMeterMode_digitsAscii;
   }

418
   char buffer[METER_BUFFER_LEN];
419
   Meter_setValues(this, buffer, METER_BUFFER_LEN - 1);
420
421
422
   
   RichString_begin(out);
   Meter_displayBuffer(this, buffer, &out);
423

424
   int yText = CRT_utf8 ? y+1 : y+2;
Hisham Muhammad's avatar
Hisham Muhammad committed
425
   attrset(CRT_colors[LED_COLOR]);
426
   mvaddstr(yText, x, this->caption);
Hisham Muhammad's avatar
Hisham Muhammad committed
427
   int xx = x + strlen(this->caption);
428
429
430
   int len = RichString_sizeVal(out);
   for (int i = 0; i < len; i++) {
      char c = RichString_getCharVal(out, i);
Hisham Muhammad's avatar
Hisham Muhammad committed
431
      if (c >= '0' && c <= '9') {
432
433
         LEDMeterMode_drawDigit(xx, y, c-48);
         xx += 4;
Hisham Muhammad's avatar
Hisham Muhammad committed
434
      } else {
435
         mvaddch(yText, xx, c);
Hisham Muhammad's avatar
Hisham Muhammad committed
436
437
438
439
         xx += 1;
      }
   }
   attrset(CRT_colors[RESET_COLOR]);
440
   RichString_end(out);
Hisham Muhammad's avatar
Hisham Muhammad committed
441
442
}

443
444
445
446
447
448
449
450
451
452
453
454
455
456
static MeterMode BarMeterMode = {
   .uiName = "Bar",
   .h = 1,
   .draw = BarMeterMode_draw,
};

static MeterMode TextMeterMode = {
   .uiName = "Text",
   .h = 1,
   .draw = TextMeterMode_draw,
};

static MeterMode GraphMeterMode = {
   .uiName = "Graph",
Hisham Muhammad's avatar
Hisham Muhammad committed
457
   .h = 4,
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
   .draw = GraphMeterMode_draw,
};

static MeterMode LEDMeterMode = {
   .uiName = "LED",
   .h = 3,
   .draw = LEDMeterMode_draw,
};

MeterMode* Meter_modes[] = {
   NULL,
   &BarMeterMode,
   &TextMeterMode,
   &GraphMeterMode,
   &LEDMeterMode,
   NULL
};
Hisham Muhammad's avatar
Hisham Muhammad committed
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504

/* Blank meter */

static void BlankMeter_setValues(Meter* this, char* buffer, int size) {
   (void) this; (void) buffer; (void) size;
}

static void BlankMeter_display(Object* cast, RichString* out) {
   (void) cast;
   RichString_prune(out);
}

int BlankMeter_attributes[] = {
   DEFAULT_COLOR
};

MeterClass BlankMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = BlankMeter_display,
   },
   .setValues = BlankMeter_setValues,
   .defaultMode = TEXT_METERMODE,
   .total = 100.0,
   .attributes = BlankMeter_attributes,
   .name = "Blank",
   .uiName = "Blank",
   .caption = ""
};