Meter.c 12.5 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
24

#define METER_BUFFER_LEN 128
Hisham Muhammad's avatar
Hisham Muhammad committed
25
26

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

typedef struct Meter_ Meter;

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

38
39
40
41
42
43
44
45
46
47
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
48
49
50
   const char* name;
   const char* uiName;
   const char* caption;
51
52
   const char maxItems;
   char curItems;
53
54
55
56
57
58
59
60
61
62
63
64
} 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
65
66
#define Meter_getItems(this_)          As_Meter(this_)->curItems
#define Meter_setItems(this_, n_)      As_Meter(this_)->curItems = (n_)
67
68
69
#define Meter_attributes(this_)        As_Meter(this_)->attributes
#define Meter_name(this_)              As_Meter(this_)->name
#define Meter_uiName(this_)            As_Meter(this_)->uiName
70
71
72

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

85
86
87
88
89
typedef struct MeterMode_ {
   Meter_Draw draw;
   const char* uiName;
   int h;
} MeterMode;
90

91
92
93
94
95
96
97
98
99
typedef enum {
   CUSTOM_METERMODE = 0,
   BAR_METERMODE,
   TEXT_METERMODE,
   GRAPH_METERMODE,
   LED_METERMODE,
   LAST_METERMODE
} MeterModeId;

100
101
102
103
104
typedef struct GraphData_ {
   struct timeval time;
   double values[METER_BUFFER_LEN];
} GraphData;

Hisham Muhammad's avatar
Hisham Muhammad committed
105
106
107
108
109
}*/

#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif
Hisham Muhammad's avatar
Hisham Muhammad committed
110
111
112
#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
Hisham Muhammad's avatar
Hisham Muhammad committed
113

114
115
116
117
118
MeterClass Meter_class = {
   .super = {
      .extends = Class(Object)
   }
};
Hisham Muhammad's avatar
Hisham Muhammad committed
119

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

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

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

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

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

      MeterMode* mode = Meter_modes[modeIndex];
      this->draw = mode->draw;
      this->h = mode->h;
Hisham Muhammad's avatar
Hisham Muhammad committed
186
   }
187
   this->mode = modeIndex;
Hisham Muhammad's avatar
Hisham Muhammad committed
188
189
}

190
191
192
193
194
195
196
197
198
199
200
201
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];
202
   snprintf(buffer, 50, "%s%s%s", Meter_uiName(this), number, mode);
203
   return ListItem_new(buffer, 0);
Hisham Muhammad's avatar
Hisham Muhammad committed
204
205
}

206
207
/* ---------- TextMeterMode ---------- */

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

   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]);
219
220
221
222
   RichString_begin(out);
   Meter_displayBuffer(this, buffer, &out);
   RichString_printVal(out, y, x);
   RichString_end(out);
223
224
225
226
}

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

227
static char BarMeterMode_characters[] = "|#*@$%&";
228

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

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

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

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

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

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

303
304
305
306
/* ---------- GraphMeterMode ---------- */

#define DrawDot(a,y,c) do { attrset(a); mvaddch(y, x+k, c); } while(0)

307
308
309
310
311
312
313
314
static int GraphMeterMode_colors[21] = {
   GRAPH_1, GRAPH_1, GRAPH_1,
   GRAPH_2, GRAPH_2, GRAPH_2,
   GRAPH_3, GRAPH_3, GRAPH_3,
   GRAPH_4, GRAPH_4, GRAPH_4,
   GRAPH_5, GRAPH_5, GRAPH_6,
   GRAPH_7, GRAPH_7, GRAPH_7,
   GRAPH_8, GRAPH_8, GRAPH_9
315
316
};

Hisham Muhammad's avatar
Hisham Muhammad committed
317
static const char* GraphMeterMode_characters = "^`'-.,_~'`-.,_~'`-.,_";
318

319
static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
320

321
   if (!this->drawData) this->drawData = calloc(1, sizeof(GraphData));
322
323
324
   GraphData* data = (GraphData*) this->drawData;
   const int nValues = METER_BUFFER_LEN;
   
325
326
327
328
329
   struct timeval now;
   gettimeofday(&now, NULL);
   if (!timercmp(&now, &(data->time), <)) {
      struct timeval delay = { .tv_sec = (int)(DEFAULT_DELAY/10), .tv_usec = (DEFAULT_DELAY-((int)(DEFAULT_DELAY/10)*10)) * 100000 };
      timeradd(&now, &delay, &(data->time));
330

331
332
333
334
      for (int i = 0; i < nValues - 1; i++)
         data->values[i] = data->values[i+1];
   
      char buffer[nValues];
335
      Meter_setValues(this, buffer, nValues - 1);
336
337
   
      double value = 0.0;
338
339
      int items = Meter_getItems(this);
      for (int i = 0; i < items; i++)
340
341
342
343
344
345
346
         value += this->values[i];
      value /= this->total;
      data->values[nValues - 1] = value;
   }
   
   for (int i = nValues - w, k = 0; i < nValues; i++, k++) {
      double value = data->values[i];
347
348
349
350
351
      DrawDot( CRT_colors[DEFAULT_COLOR], y, ' ' );
      DrawDot( CRT_colors[DEFAULT_COLOR], y+1, ' ' );
      DrawDot( CRT_colors[DEFAULT_COLOR], y+2, ' ' );
      
      double threshold = 1.00;
Hisham Muhammad's avatar
Hisham Muhammad committed
352
      for (int j = 0; j < 21; j++, threshold -= 0.05)
353
         if (value >= threshold) {
Hisham Muhammad's avatar
Hisham Muhammad committed
354
            DrawDot(CRT_colors[GraphMeterMode_colors[j]], y+(j/7.0), GraphMeterMode_characters[j]);
355
356
357
            break;
         }
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
358
359
360
   attrset(CRT_colors[RESET_COLOR]);
}

361
362
/* ---------- LEDMeterMode ---------- */

363
static const char* LEDMeterMode_digitsAscii[3][10] = {
364
365
366
367
   { " __ ","    "," __ "," __ ","    "," __ "," __ "," __ "," __ "," __ "},
   { "|  |","   |"," __|"," __|","|__|","|__ ","|__ ","   |","|__|","|__|"},
   { "|__|","   |","|__ "," __|","   |"," __|","|__|","   |","|__|"," __|"},
};
Hisham Muhammad's avatar
Hisham Muhammad committed
368

369
370
371
372
373
374
static const char* LEDMeterMode_digitsUtf8[3][10] = {
   { "┌──┐","  ┐ ","╶──┐","╶──┐","╷  ╷","┌──╴","┌──╴","╶──┐","┌──┐","┌──┐"},
   { "│  │","  │ ","┌──┘"," ──┤","└──┤","└──┐","├──┐","   │","├──┤","└──┤"},
   { "└──┘","  ╵ ","└──╴","╶──┘","   ╵","╶──┘","└──┘","   ╵","└──┘"," ──┘"},
};

375
static void LEDMeterMode_drawDigit(int x, int y, int n) {
376
377
378
379
380
381
382
   if (CRT_utf8) {
      for (int i = 0; i < 3; i++)
         mvaddstr(y+i, x, LEDMeterMode_digitsUtf8[i][n]);
   } else {
      for (int i = 0; i < 3; i++)
         mvaddstr(y+i, x, LEDMeterMode_digitsAscii[i][n]);
   }
383
384
}

385
static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
Hisham Muhammad's avatar
Hisham Muhammad committed
386
   (void) w;
387
   char buffer[METER_BUFFER_LEN];
388
   Meter_setValues(this, buffer, METER_BUFFER_LEN - 1);
389
390
391
   
   RichString_begin(out);
   Meter_displayBuffer(this, buffer, &out);
392

393
   int yText = CRT_utf8 ? y+1 : y+2;
Hisham Muhammad's avatar
Hisham Muhammad committed
394
   attrset(CRT_colors[LED_COLOR]);
395
   mvaddstr(yText, x, this->caption);
Hisham Muhammad's avatar
Hisham Muhammad committed
396
   int xx = x + strlen(this->caption);
397
398
399
   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
400
      if (c >= '0' && c <= '9') {
401
402
         LEDMeterMode_drawDigit(xx, y, c-48);
         xx += 4;
Hisham Muhammad's avatar
Hisham Muhammad committed
403
      } else {
404
         mvaddch(yText, xx, c);
Hisham Muhammad's avatar
Hisham Muhammad committed
405
406
407
408
         xx += 1;
      }
   }
   attrset(CRT_colors[RESET_COLOR]);
409
   RichString_end(out);
Hisham Muhammad's avatar
Hisham Muhammad committed
410
411
}

412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
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",
   .h = 3,
   .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
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473

/* 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 = ""
};