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
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
24
#include "ListItem.h"
#include "debug.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>
Hisham Muhammad's avatar
Hisham Muhammad committed
31
#include <time.h>
Hisham Muhammad's avatar
Hisham Muhammad committed
32

33
34
35
36
37
#ifndef USE_FUNKY_MODES
#define USE_FUNKY_MODES 1
#endif

#define METER_BUFFER_LEN 128
Hisham Muhammad's avatar
Hisham Muhammad committed
38
39

/*{
Hisham Muhammad's avatar
Hisham Muhammad committed
40
41
#include "ListItem.h"
#include "ProcessList.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
42
43

typedef struct Meter_ Meter;
44
45
typedef struct MeterType_ MeterType;
typedef struct MeterMode_ MeterMode;
Hisham Muhammad's avatar
Hisham Muhammad committed
46

47
48
typedef void(*MeterType_Init)(Meter*);
typedef void(*MeterType_Done)(Meter*);
49
typedef void(*MeterType_SetMode)(Meter*, int);
50
typedef void(*Meter_SetValues)(Meter*, char*, int);
Hisham Muhammad's avatar
Hisham Muhammad committed
51
52
typedef void(*Meter_Draw)(Meter*, int, int, int);

53
struct MeterMode_ {
Hisham Muhammad's avatar
Hisham Muhammad committed
54
   Meter_Draw draw;
Hisham Muhammad's avatar
Hisham Muhammad committed
55
   const char* uiName;
56
57
58
59
   int h;
};

struct MeterType_ {
Hisham Muhammad's avatar
Hisham Muhammad committed
60
   Meter_SetValues setValues;
61
62
   Object_Display display;
   int mode;
Hisham Muhammad's avatar
Hisham Muhammad committed
63
   int items;
64
   double total;
Hisham Muhammad's avatar
Hisham Muhammad committed
65
   int* attributes;
Hisham Muhammad's avatar
Hisham Muhammad committed
66
67
68
   const char* name;
   const char* uiName;
   const char* caption;
69
70
   MeterType_Init init;
   MeterType_Done done;
71
   MeterType_SetMode setMode;
72
73
74
75
76
77
78
79
80
81
   Meter_Draw draw;
};

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

89
90
91
92
93
94
95
#ifdef USE_FUNKY_MODES
typedef struct GraphData_ {
   time_t time;
   double values[METER_BUFFER_LEN];
} GraphData;
#endif

96
97
98
99
100
101
102
103
104
105
106
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
107
108
109
110
111
}*/

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

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

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

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

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

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

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

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

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

212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
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
227
228
}

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

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

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

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

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

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

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

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

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

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

327
328
329
330
331
332
#ifdef USE_FUNKY_MODES

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

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

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

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

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

347
348
349
350
351
352
353
   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;
354

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

385
386
/* ---------- LEDMeterMode ---------- */

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

393
394
395
396
397
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]);
}

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

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

425
#endif
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
457
458
459
460
461
462
463
464

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