Meter.c 10.8 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
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/

Hisham Muhammad's avatar
Hisham Muhammad committed
8
#include "RichString.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
9
10
11
12
13
#include "Meter.h"
#include "Object.h"
#include "CRT.h"
#include "ListItem.h"
#include "String.h"
14
#include "ProcessList.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
15
16
17
18
19

#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
Hisham Muhammad's avatar
Hisham Muhammad committed
20
21
22
23

#include "debug.h"
#include <assert.h>

24
#ifndef USE_FUNKY_MODES
25
#include <time.h>
26
27
28
29
#define USE_FUNKY_MODES 1
#endif

#define METER_BUFFER_LEN 128
Hisham Muhammad's avatar
Hisham Muhammad committed
30
31
32
33

/*{

typedef struct Meter_ Meter;
34
35
typedef struct MeterType_ MeterType;
typedef struct MeterMode_ MeterMode;
Hisham Muhammad's avatar
Hisham Muhammad committed
36

37
38
typedef void(*MeterType_Init)(Meter*);
typedef void(*MeterType_Done)(Meter*);
39
typedef void(*MeterType_SetMode)(Meter*, int);
40
typedef void(*Meter_SetValues)(Meter*, char*, int);
Hisham Muhammad's avatar
Hisham Muhammad committed
41
42
typedef void(*Meter_Draw)(Meter*, int, int, int);

43
struct MeterMode_ {
Hisham Muhammad's avatar
Hisham Muhammad committed
44
   Meter_Draw draw;
Hisham Muhammad's avatar
Hisham Muhammad committed
45
   const char* uiName;
46
47
48
49
   int h;
};

struct MeterType_ {
Hisham Muhammad's avatar
Hisham Muhammad committed
50
   Meter_SetValues setValues;
51
52
   Object_Display display;
   int mode;
Hisham Muhammad's avatar
Hisham Muhammad committed
53
   int items;
54
   double total;
Hisham Muhammad's avatar
Hisham Muhammad committed
55
   int* attributes;
Hisham Muhammad's avatar
Hisham Muhammad committed
56
57
58
   const char* name;
   const char* uiName;
   const char* caption;
59
60
   MeterType_Init init;
   MeterType_Done done;
61
   MeterType_SetMode setMode;
62
63
64
65
66
67
68
69
70
71
   Meter_Draw draw;
};

struct Meter_ {
   Object super;
   char* caption;
   MeterType* type;
   int mode;
   int param;
   Meter_Draw draw;
72
   void* drawData;
73
74
   int h;
   ProcessList* pl;
Hisham Muhammad's avatar
Hisham Muhammad committed
75
76
77
78
   double* values;
   double total;
};

79
80
81
82
83
84
85
#ifdef USE_FUNKY_MODES
typedef struct GraphData_ {
   time_t time;
   double values[METER_BUFFER_LEN];
} GraphData;
#endif

86
87
88
89
90
91
92
93
94
95
96
typedef enum {
   CUSTOM_METERMODE = 0,
   BAR_METERMODE,
   TEXT_METERMODE,
#ifdef USE_FUNKY_MODES
   GRAPH_METERMODE,
   LED_METERMODE,
#endif
   LAST_METERMODE
} MeterModeId;

Hisham Muhammad's avatar
Hisham Muhammad committed
97
98
}*/

99
100
101
102
103
104
#include "CPUMeter.h"
#include "MemoryMeter.h"
#include "SwapMeter.h"
#include "TasksMeter.h"
#include "LoadAverageMeter.h"
#include "UptimeMeter.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
105
#include "BatteryMeter.h"
106
#include "ClockMeter.h"
107
#include "HostnameMeter.h"
108

Hisham Muhammad's avatar
Hisham Muhammad committed
109

Hisham Muhammad's avatar
Hisham Muhammad committed
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
#ifdef DEBUG
Hisham Muhammad's avatar
Hisham Muhammad committed
118
char* METER_CLASS = "Meter";
119
120
121
#else
#define METER_CLASS NULL
#endif
Hisham Muhammad's avatar
Hisham Muhammad committed
122

123
124
125
126
MeterType* Meter_types[] = {
   &CPUMeter,
   &ClockMeter,
   &LoadAverageMeter,
Hisham Muhammad's avatar
Hisham Muhammad committed
127
   &LoadMeter,
128
129
130
131
   &MemoryMeter,
   &SwapMeter,
   &TasksMeter,
   &UptimeMeter,
Hisham Muhammad's avatar
Hisham Muhammad committed
132
   &BatteryMeter,
133
   &HostnameMeter,
134
135
136
137
138
139
   &AllCPUsMeter,
   &AllCPUs2Meter,
   &LeftCPUsMeter,
   &RightCPUsMeter,
   &LeftCPUs2Meter,
   &RightCPUs2Meter,
140
   NULL
Hisham Muhammad's avatar
Hisham Muhammad committed
141
142
};

143
144
Meter* Meter_new(ProcessList* pl, int param, MeterType* type) {
   Meter* this = calloc(sizeof(Meter), 1);
145
146
147
   Object_setClass(this, METER_CLASS);
   ((Object*)this)->delete = Meter_delete;
   ((Object*)this)->display = type->display;
148
149
150
151
152
153
154
155
156
   this->h = 1;
   this->type = type;
   this->param = param;
   this->pl = pl;
   this->values = calloc(sizeof(double), type->items);
   this->total = type->total;
   this->caption = strdup(type->caption);
   if (this->type->init)
      this->type->init(this);
157
   Meter_setMode(this, type->mode);
158
   return this;
Hisham Muhammad's avatar
Hisham Muhammad committed
159
160
161
}

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

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

180
static inline void Meter_displayBuffer(Meter* this, char* buffer, RichString* out) {
181
182
183
   MeterType* type = this->type;
   Object_Display display = ((Object*)this)->display;
   if (display) {
184
      display((Object*)this, out);
185
   } else {
186
      RichString_write(out, CRT_colors[type->attributes[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
196
197
      return;
   if (!modeIndex)
      modeIndex = 1;
   assert(modeIndex < LAST_METERMODE);
   if (this->type->mode == 0) {
      this->draw = this->type->draw;
198
199
      if (this->type->setMode)
         this->type->setMode(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
225
226
227
ListItem* Meter_toListItem(Meter* this) {
   MeterType* type = this->type;
   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];
   snprintf(buffer, 50, "%s%s%s", type->uiName, number, mode);
   return ListItem_new(buffer, 0);
Hisham Muhammad's avatar
Hisham Muhammad committed
228
229
}

230
231
/* ---------- TextMeterMode ---------- */

232
static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
233
234
235
236
237
238
239
240
241
242
243
   MeterType* type = this->type;
   char buffer[METER_BUFFER_LEN];
   type->setValues(this, buffer, METER_BUFFER_LEN - 1);

   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]);
244
245
246
247
   RichString_begin(out);
   Meter_displayBuffer(this, buffer, &out);
   RichString_printVal(out, y, x);
   RichString_end(out);
248
249
250
251
}

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

252
static char BarMeterMode_characters[] = "|#*@$%&";
253

254
static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
255
256
257
258
   MeterType* type = this->type;
   char buffer[METER_BUFFER_LEN];
   type->setValues(this, buffer, METER_BUFFER_LEN - 1);

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

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

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

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

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

328
329
330
331
332
333
#ifdef USE_FUNKY_MODES

/* ---------- GraphMeterMode ---------- */

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

334
335
336
337
338
339
340
341
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
342
343
};

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

346
static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
347

348
349
350
351
352
353
354
   if (!this->drawData) this->drawData = calloc(sizeof(GraphData), 1);
   GraphData* data = (GraphData*) this->drawData;
   const int nValues = METER_BUFFER_LEN;
   
   time_t now = time(NULL);
   if (now > data->time) {
      data->time = now;
355

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

386
387
/* ---------- LEDMeterMode ---------- */

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

394
395
396
397
398
static void LEDMeterMode_drawDigit(int x, int y, int n) {
   for (int i = 0; i < 3; i++)
      mvaddstr(y+i, x, LEDMeterMode_digits[i][n]);
}

399
static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
Hisham Muhammad's avatar
Hisham Muhammad committed
400
   (void) w;
401
402
403
   MeterType* type = this->type;
   char buffer[METER_BUFFER_LEN];
   type->setValues(this, buffer, METER_BUFFER_LEN - 1);
404
405
406
   
   RichString_begin(out);
   Meter_displayBuffer(this, buffer, &out);
407

Hisham Muhammad's avatar
Hisham Muhammad committed
408
409
410
   attrset(CRT_colors[LED_COLOR]);
   mvaddstr(y+2, x, this->caption);
   int xx = x + strlen(this->caption);
411
412
413
   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
414
      if (c >= '0' && c <= '9') {
415
416
         LEDMeterMode_drawDigit(xx, y, c-48);
         xx += 4;
Hisham Muhammad's avatar
Hisham Muhammad committed
417
418
419
420
421
422
      } else {
         mvaddch(y+2, xx, c);
         xx += 1;
      }
   }
   attrset(CRT_colors[RESET_COLOR]);
423
   RichString_end(out);
Hisham Muhammad's avatar
Hisham Muhammad committed
424
425
}

426
#endif
427
428
429
430
431
432
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

static MeterMode BarMeterMode = {
   .uiName = "Bar",
   .h = 1,
   .draw = BarMeterMode_draw,
};

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

#ifdef USE_FUNKY_MODES

static MeterMode GraphMeterMode = {
   .uiName = "Graph",
   .h = 3,
   .draw = GraphMeterMode_draw,
};

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

#endif

MeterMode* Meter_modes[] = {
   NULL,
   &BarMeterMode,
   &TextMeterMode,
#ifdef USE_FUNKY_MODES
   &GraphMeterMode,
   &LEDMeterMode,
#endif
   NULL
};