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-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
267
268
269
270
271
272
   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] = ' ';

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

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

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

320
321
322
323
324
325
#ifdef USE_FUNKY_MODES

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

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

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

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

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

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

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

378
379
/* ---------- LEDMeterMode ---------- */

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

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

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

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

418
#endif
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
457

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