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.
*/

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
}

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

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

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

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

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

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

226
227
/* ---------- TextMeterMode ---------- */

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

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

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

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

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

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

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

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

   // ...then print the buffer.
   offset = 0;
310
311
   for (int i = 0; i < type->items; i++) {
      attrset(CRT_colors[type->attributes[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
330
331
#ifdef USE_FUNKY_MODES

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

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

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

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

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

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

354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
      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];
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 ---------- */

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

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

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

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

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

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