Meter.c 10.7 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-2010 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.
*/

8
#define _GNU_SOURCE
Hisham Muhammad's avatar
Hisham Muhammad committed
9
#include "RichString.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
10
11
12
13
14
#include "Meter.h"
#include "Object.h"
#include "CRT.h"
#include "ListItem.h"
#include "String.h"
15
#include "ProcessList.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
22
23
24

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

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

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

/*{

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

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

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

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

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

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

87
88
89
90
91
92
93
94
95
96
97
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
98
99
}*/

100
101
102
103
104
105
#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
106
#include "BatteryMeter.h"
107
#include "ClockMeter.h"
108
#include "HostnameMeter.h"
109

Hisham Muhammad's avatar
Hisham Muhammad committed
110

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

118
#ifdef DEBUG
Hisham Muhammad's avatar
Hisham Muhammad committed
119
char* METER_CLASS = "Meter";
120
121
122
#else
#define METER_CLASS NULL
#endif
Hisham Muhammad's avatar
Hisham Muhammad committed
123

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

139
140
Meter* Meter_new(ProcessList* pl, int param, MeterType* type) {
   Meter* this = calloc(sizeof(Meter), 1);
141
142
143
   Object_setClass(this, METER_CLASS);
   ((Object*)this)->delete = Meter_delete;
   ((Object*)this)->display = type->display;
144
145
146
147
148
149
150
151
152
153
154
   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);
   Meter_setMode(this, type->mode);
   if (this->type->init)
      this->type->init(this);
   return this;
Hisham Muhammad's avatar
Hisham Muhammad committed
155
156
157
158
159
}

void Meter_delete(Object* cast) {
   Meter* this = (Meter*) cast;
   assert (this != NULL);
160
161
162
   if (this->type->done) {
      this->type->done(this);
   }
163
164
   if (this->drawData)
      free(this->drawData);
165
166
   free(this->caption);
   free(this->values);
Hisham Muhammad's avatar
Hisham Muhammad committed
167
168
169
   free(this);
}

Hisham Muhammad's avatar
Hisham Muhammad committed
170
void Meter_setCaption(Meter* this, const char* caption) {
171
172
173
174
   free(this->caption);
   this->caption = strdup(caption);
}

175
static inline void Meter_displayBuffer(Meter* this, char* buffer, RichString* out) {
176
177
178
   MeterType* type = this->type;
   Object_Display display = ((Object*)this)->display;
   if (display) {
179
      display((Object*)this, out);
180
   } else {
181
      RichString_write(out, CRT_colors[type->attributes[0]], buffer);
Hisham Muhammad's avatar
Hisham Muhammad committed
182
   }
183
184
185
}

void Meter_setMode(Meter* this, int modeIndex) {
Hisham Muhammad's avatar
Hisham Muhammad committed
186
   if (modeIndex > 0 && modeIndex == this->mode)
187
188
189
190
191
192
      return;
   if (!modeIndex)
      modeIndex = 1;
   assert(modeIndex < LAST_METERMODE);
   if (this->type->mode == 0) {
      this->draw = this->type->draw;
193
194
      if (this->type->setMode)
         this->type->setMode(this, modeIndex);
195
   } else {
Hisham Muhammad's avatar
Hisham Muhammad committed
196
      assert(modeIndex >= 1);
197
198
199
      if (this->drawData)
         free(this->drawData);
      this->drawData = NULL;
Hisham Muhammad's avatar
Hisham Muhammad committed
200
201
202
203

      MeterMode* mode = Meter_modes[modeIndex];
      this->draw = mode->draw;
      this->h = mode->h;
Hisham Muhammad's avatar
Hisham Muhammad committed
204
   }
205
   this->mode = modeIndex;
Hisham Muhammad's avatar
Hisham Muhammad committed
206
207
}

208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
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
223
224
}

225
226
/* ---------- TextMeterMode ---------- */

227
static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
228
229
230
231
232
233
234
235
236
237
238
   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]);
239
240
241
242
   RichString_begin(out);
   Meter_displayBuffer(this, buffer, &out);
   RichString_printVal(out, y, x);
   RichString_end(out);
243
244
245
246
}

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

247
static char BarMeterMode_characters[] = "|#*@$%&";
248

249
static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
250
251
252
253
   MeterType* type = this->type;
   char buffer[METER_BUFFER_LEN];
   type->setValues(this, buffer, METER_BUFFER_LEN - 1);

Hisham Muhammad's avatar
Hisham Muhammad committed
254
255
   w -= 2;
   attrset(CRT_colors[METER_TEXT]);
Hisham Muhammad's avatar
Hisham Muhammad committed
256
257
   int captionLen = 3;
   mvaddnstr(y, x, this->caption, captionLen);
Hisham Muhammad's avatar
Hisham Muhammad committed
258
259
260
261
262
263
264
265
266
267
268
269
270
271
   x += captionLen;
   w -= captionLen;
   attrset(CRT_colors[BAR_BORDER]);
   mvaddch(y, x, '[');
   mvaddch(y, x + w, ']');
   
   w--;
   x++;
   char bar[w];
   
   int blockSizes[10];
   for (int i = 0; i < w; i++)
      bar[i] = ' ';

272
   sprintf(bar + (w-strlen(buffer)), "%s", buffer);
Hisham Muhammad's avatar
Hisham Muhammad committed
273
274
275
276

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

   // ...then print the buffer.
   offset = 0;
303
304
   for (int i = 0; i < type->items; i++) {
      attrset(CRT_colors[type->attributes[i]]);
Hisham Muhammad's avatar
Hisham Muhammad committed
305
306
307
308
309
310
311
312
313
314
315
316
317
318
      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]);
}

319
320
321
322
323
324
#ifdef USE_FUNKY_MODES

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

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

325
326
327
328
329
330
331
332
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
333
334
};

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

337
static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
338

339
340
341
342
343
344
345
   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;
346

347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
      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];
363
364
365
366
367
      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
368
      for (int j = 0; j < 21; j++, threshold -= 0.05)
369
         if (value >= threshold) {
Hisham Muhammad's avatar
Hisham Muhammad committed
370
            DrawDot(CRT_colors[GraphMeterMode_colors[j]], y+(j/7.0), GraphMeterMode_characters[j]);
371
372
373
            break;
         }
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
374
375
376
   attrset(CRT_colors[RESET_COLOR]);
}

377
378
/* ---------- LEDMeterMode ---------- */

Hisham Muhammad's avatar
Hisham Muhammad committed
379
static const char* LEDMeterMode_digits[3][10] = {
380
381
382
383
   { " __ ","    "," __ "," __ ","    "," __ "," __ "," __ "," __ "," __ "},
   { "|  |","   |"," __|"," __|","|__|","|__ ","|__ ","   |","|__|","|__|"},
   { "|__|","   |","|__ "," __|","   |"," __|","|__|","   |","|__|"," __|"},
};
Hisham Muhammad's avatar
Hisham Muhammad committed
384

385
386
387
388
389
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]);
}

390
static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
Hisham Muhammad's avatar
Hisham Muhammad committed
391
   (void) w;
392
393
394
   MeterType* type = this->type;
   char buffer[METER_BUFFER_LEN];
   type->setValues(this, buffer, METER_BUFFER_LEN - 1);
395
396
397
   
   RichString_begin(out);
   Meter_displayBuffer(this, buffer, &out);
398

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

417
#endif
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
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,
};

#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
};