Meter.c 12.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
11
12
13
14
15
16
17
18
19

#include "CPUMeter.h"
#include "MemoryMeter.h"
#include "SwapMeter.h"
#include "TasksMeter.h"
#include "LoadAverageMeter.h"
#include "UptimeMeter.h"
#include "BatteryMeter.h"
#include "ClockMeter.h"
#include "HostnameMeter.h"
#include "RichString.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
20
21
22
#include "Object.h"
#include "CRT.h"
#include "String.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
23
#include "ListItem.h"
24
#include "Settings.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
25
26
27
28
29

#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
Hisham Muhammad's avatar
Hisham Muhammad committed
30
#include <assert.h>
31
#include <sys/time.h>
32
33

#define METER_BUFFER_LEN 128
Hisham Muhammad's avatar
Hisham Muhammad committed
34
35

/*{
Hisham Muhammad's avatar
Hisham Muhammad committed
36
37
#include "ListItem.h"
#include "ProcessList.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
38
39
40

typedef struct Meter_ Meter;

41
42
43
typedef void(*Meter_Init)(Meter*);
typedef void(*Meter_Done)(Meter*);
typedef void(*Meter_UpdateMode)(Meter*, int);
44
typedef void(*Meter_SetValues)(Meter*, char*, int);
Hisham Muhammad's avatar
Hisham Muhammad committed
45
46
typedef void(*Meter_Draw)(Meter*, int, int, int);

47
48
49
50
51
52
53
54
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;
Hisham Muhammad's avatar
Hisham Muhammad committed
55
   int items;
56
57
   const double total;
   const int* attributes;
Hisham Muhammad's avatar
Hisham Muhammad committed
58
59
60
   const char* name;
   const char* uiName;
   const char* caption;
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
} 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
#define Meter_getItems(this_)          As_Meter(this_)->items
#define Meter_setItems(this_, n_)      As_Meter(this_)->items = (n_)
#define Meter_attributes(this_)        As_Meter(this_)->attributes
#define Meter_name(this_)              As_Meter(this_)->name
#define Meter_uiName(this_)            As_Meter(this_)->uiName
78
79
80

struct Meter_ {
   Object super;
81
82
   Meter_Draw draw;
   
83
84
85
   char* caption;
   int mode;
   int param;
86
   void* drawData;
87
88
   int h;
   ProcessList* pl;
Hisham Muhammad's avatar
Hisham Muhammad committed
89
90
91
92
   double* values;
   double total;
};

93
94
95
96
97
typedef struct MeterMode_ {
   Meter_Draw draw;
   const char* uiName;
   int h;
} MeterMode;
98

99
100
101
102
103
104
105
106
107
typedef enum {
   CUSTOM_METERMODE = 0,
   BAR_METERMODE,
   TEXT_METERMODE,
   GRAPH_METERMODE,
   LED_METERMODE,
   LAST_METERMODE
} MeterModeId;

108
109
110
111
112
typedef struct GraphData_ {
   struct timeval time;
   double values[METER_BUFFER_LEN];
} GraphData;

Hisham Muhammad's avatar
Hisham Muhammad committed
113
114
115
116
117
}*/

#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif
Hisham Muhammad's avatar
Hisham Muhammad committed
118
119
120
#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
Hisham Muhammad's avatar
Hisham Muhammad committed
121

122
123
124
125
126
MeterClass Meter_class = {
   .super = {
      .extends = Class(Object)
   }
};
Hisham Muhammad's avatar
Hisham Muhammad committed
127

128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
MeterClass* Meter_types[] = {
   &CPUMeter_class,
   &ClockMeter_class,
   &LoadAverageMeter_class,
   &LoadMeter_class,
   &MemoryMeter_class,
   &SwapMeter_class,
   &TasksMeter_class,
   &UptimeMeter_class,
   &BatteryMeter_class,
   &HostnameMeter_class,
   &AllCPUsMeter_class,
   &AllCPUs2Meter_class,
   &LeftCPUsMeter_class,
   &RightCPUsMeter_class,
   &LeftCPUs2Meter_class,
   &RightCPUs2Meter_class,
145
   NULL
Hisham Muhammad's avatar
Hisham Muhammad committed
146
147
};

148
Meter* Meter_new(ProcessList* pl, int param, MeterClass* type) {
149
   Meter* this = calloc(sizeof(Meter), 1);
150
   Object_setClass(this, type);
151
152
153
154
155
156
   this->h = 1;
   this->param = param;
   this->pl = pl;
   this->values = calloc(sizeof(double), type->items);
   this->total = type->total;
   this->caption = strdup(type->caption);
157
158
159
   if (Meter_initFn(this))
      Meter_init(this);
   Meter_setMode(this, type->defaultMode);
160
   return this;
Hisham Muhammad's avatar
Hisham Muhammad committed
161
162
163
}

void Meter_delete(Object* cast) {
164
165
   if (!cast)
      return;
Hisham Muhammad's avatar
Hisham Muhammad committed
166
   Meter* this = (Meter*) cast;
167
168
   if (Meter_doneFn(this)) {
      Meter_done(this);
169
   }
170
171
   if (this->drawData)
      free(this->drawData);
172
173
   free(this->caption);
   free(this->values);
Hisham Muhammad's avatar
Hisham Muhammad committed
174
175
176
   free(this);
}

Hisham Muhammad's avatar
Hisham Muhammad committed
177
void Meter_setCaption(Meter* this, const char* caption) {
178
179
180
181
   free(this->caption);
   this->caption = strdup(caption);
}

182
static inline void Meter_displayBuffer(Meter* this, char* buffer, RichString* out) {
183
184
   if (Object_displayFn(this)) {
      Object_display(this, out);
185
   } else {
186
      RichString_write(out, CRT_colors[Meter_attributes(this)[0]], buffer);
Hisham Muhammad's avatar
Hisham Muhammad committed
187
   }
188
189
190
}

void Meter_setMode(Meter* this, int modeIndex) {
Hisham Muhammad's avatar
Hisham Muhammad committed
191
   if (modeIndex > 0 && modeIndex == this->mode)
192
193
194
195
      return;
   if (!modeIndex)
      modeIndex = 1;
   assert(modeIndex < LAST_METERMODE);
196
197
198
199
   if (Meter_defaultMode(this) == CUSTOM_METERMODE) {
      this->draw = Meter_drawFn(this);
      if (Meter_updateModeFn(this))
         Meter_updateMode(this, modeIndex);
200
   } else {
Hisham Muhammad's avatar
Hisham Muhammad committed
201
      assert(modeIndex >= 1);
202
203
204
      if (this->drawData)
         free(this->drawData);
      this->drawData = NULL;
Hisham Muhammad's avatar
Hisham Muhammad committed
205
206
207
208

      MeterMode* mode = Meter_modes[modeIndex];
      this->draw = mode->draw;
      this->h = mode->h;
Hisham Muhammad's avatar
Hisham Muhammad committed
209
   }
210
   this->mode = modeIndex;
Hisham Muhammad's avatar
Hisham Muhammad committed
211
212
}

213
214
215
216
217
218
219
220
221
222
223
224
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];
225
   snprintf(buffer, 50, "%s%s%s", Meter_uiName(this), number, mode);
226
   return ListItem_new(buffer, 0);
Hisham Muhammad's avatar
Hisham Muhammad committed
227
228
}

229
230
/* ---------- TextMeterMode ---------- */

231
static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
232
   char buffer[METER_BUFFER_LEN];
233
   Meter_setValues(this, buffer, METER_BUFFER_LEN - 1);
234
235
236
237
238
239
240
241

   attrset(CRT_colors[METER_TEXT]);
   mvaddstr(y, x, this->caption);
   int captionLen = strlen(this->caption);
   w -= captionLen;
   x += captionLen;
   mvhline(y, x, ' ', CRT_colors[DEFAULT_COLOR]);
   attrset(CRT_colors[RESET_COLOR]);
242
243
244
245
   RichString_begin(out);
   Meter_displayBuffer(this, buffer, &out);
   RichString_printVal(out, y, x);
   RichString_end(out);
246
247
248
249
}

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

250
static char BarMeterMode_characters[] = "|#*@$%&";
251

252
static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
253
   char buffer[METER_BUFFER_LEN];
254
   Meter_setValues(this, buffer, METER_BUFFER_LEN - 1);
255

Hisham Muhammad's avatar
Hisham Muhammad committed
256
257
   w -= 2;
   attrset(CRT_colors[METER_TEXT]);
Hisham Muhammad's avatar
Hisham Muhammad committed
258
259
   int captionLen = 3;
   mvaddnstr(y, x, this->caption, captionLen);
Hisham Muhammad's avatar
Hisham Muhammad committed
260
261
262
263
264
265
266
267
   x += captionLen;
   w -= captionLen;
   attrset(CRT_colors[BAR_BORDER]);
   mvaddch(y, x, '[');
   mvaddch(y, x + w, ']');
   
   w--;
   x++;
268
269
270
271
272
273

   if (w < 1) {
      attrset(CRT_colors[RESET_COLOR]);
      return;
   }
   char bar[w + 1];
Hisham Muhammad's avatar
Hisham Muhammad committed
274
275
276
277
278
   
   int blockSizes[10];
   for (int i = 0; i < w; i++)
      bar[i] = ' ';

279
   const size_t barOffset = w - MIN((int)strlen(buffer), w);
280
   snprintf(bar + barOffset, w - barOffset + 1, "%s", buffer);
Hisham Muhammad's avatar
Hisham Muhammad committed
281
282
283

   // First draw in the bar[] buffer...
   int offset = 0;
284
285
   int items = Meter_getItems(this);
   for (int i = 0; i < items; i++) {
Hisham Muhammad's avatar
Hisham Muhammad committed
286
      double value = this->values[i];
287
288
      value = MAX(value, 0);
      value = MIN(value, this->total);
Hisham Muhammad's avatar
Hisham Muhammad committed
289
290
291
292
293
294
295
      if (value > 0) {
         blockSizes[i] = ceil((value/this->total) * w);
      } else {
         blockSizes[i] = 0;
      }
      int nextOffset = offset + blockSizes[i];
      // (Control against invalid values)
296
      nextOffset = MIN(MAX(nextOffset, 0), w);
Hisham Muhammad's avatar
Hisham Muhammad committed
297
298
299
      for (int j = offset; j < nextOffset; j++)
         if (bar[j] == ' ') {
            if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
300
               bar[j] = BarMeterMode_characters[i];
Hisham Muhammad's avatar
Hisham Muhammad committed
301
302
303
304
305
306
307
308
309
            } else {
               bar[j] = '|';
            }
         }
      offset = nextOffset;
   }

   // ...then print the buffer.
   offset = 0;
310
311
   for (int i = 0; i < items; i++) {
      attrset(CRT_colors[Meter_attributes(this)[i]]);
Hisham Muhammad's avatar
Hisham Muhammad committed
312
313
314
315
316
317
318
319
320
321
322
323
324
325
      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]);
}

326
327
328
329
/* ---------- GraphMeterMode ---------- */

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

330
331
332
333
334
335
336
337
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
338
339
};

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

342
static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
343

344
345
346
347
   if (!this->drawData) this->drawData = calloc(sizeof(GraphData), 1);
   GraphData* data = (GraphData*) this->drawData;
   const int nValues = METER_BUFFER_LEN;
   
348
349
350
351
352
   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));
353

354
355
356
357
      for (int i = 0; i < nValues - 1; i++)
         data->values[i] = data->values[i+1];
   
      char buffer[nValues];
358
      Meter_setValues(this, buffer, nValues - 1);
359
360
   
      double value = 0.0;
361
362
      int items = Meter_getItems(this);
      for (int i = 0; i < items; i++)
363
364
365
366
367
368
369
         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];
370
371
372
373
374
      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
375
      for (int j = 0; j < 21; j++, threshold -= 0.05)
376
         if (value >= threshold) {
Hisham Muhammad's avatar
Hisham Muhammad committed
377
            DrawDot(CRT_colors[GraphMeterMode_colors[j]], y+(j/7.0), GraphMeterMode_characters[j]);
378
379
380
            break;
         }
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
381
382
383
   attrset(CRT_colors[RESET_COLOR]);
}

384
385
/* ---------- LEDMeterMode ---------- */

386
static const char* LEDMeterMode_digitsAscii[3][10] = {
387
388
389
390
   { " __ ","    "," __ "," __ ","    "," __ "," __ "," __ "," __ "," __ "},
   { "|  |","   |"," __|"," __|","|__|","|__ ","|__ ","   |","|__|","|__|"},
   { "|__|","   |","|__ "," __|","   |"," __|","|__|","   |","|__|"," __|"},
};
Hisham Muhammad's avatar
Hisham Muhammad committed
391

392
393
394
395
396
397
static const char* LEDMeterMode_digitsUtf8[3][10] = {
   { "┌──┐","  ┐ ","╶──┐","╶──┐","╷  ╷","┌──╴","┌──╴","╶──┐","┌──┐","┌──┐"},
   { "│  │","  │ ","┌──┘"," ──┤","└──┤","└──┐","├──┐","   │","├──┤","└──┤"},
   { "└──┘","  ╵ ","└──╴","╶──┘","   ╵","╶──┘","└──┘","   ╵","└──┘"," ──┘"},
};

398
static void LEDMeterMode_drawDigit(int x, int y, int n) {
399
400
401
402
403
404
405
   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]);
   }
406
407
}

408
static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
Hisham Muhammad's avatar
Hisham Muhammad committed
409
   (void) w;
410
   char buffer[METER_BUFFER_LEN];
411
   Meter_setValues(this, buffer, METER_BUFFER_LEN - 1);
412
413
414
   
   RichString_begin(out);
   Meter_displayBuffer(this, buffer, &out);
415

416
   int yText = CRT_utf8 ? y+1 : y+2;
Hisham Muhammad's avatar
Hisham Muhammad committed
417
   attrset(CRT_colors[LED_COLOR]);
418
   mvaddstr(yText, x, this->caption);
Hisham Muhammad's avatar
Hisham Muhammad committed
419
   int xx = x + strlen(this->caption);
420
421
422
   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
423
      if (c >= '0' && c <= '9') {
424
425
         LEDMeterMode_drawDigit(xx, y, c-48);
         xx += 4;
Hisham Muhammad's avatar
Hisham Muhammad committed
426
      } else {
427
         mvaddch(yText, xx, c);
Hisham Muhammad's avatar
Hisham Muhammad committed
428
429
430
431
         xx += 1;
      }
   }
   attrset(CRT_colors[RESET_COLOR]);
432
   RichString_end(out);
Hisham Muhammad's avatar
Hisham Muhammad committed
433
434
}

435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
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
};