Meter.c 10.4 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-2006 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
9
10
11
12
13
14
#define _GNU_SOURCE
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <curses.h>
#include <stdarg.h>

Hisham Muhammad's avatar
Hisham Muhammad committed
15
16
17
18
19
#include "Meter.h"
#include "Object.h"
#include "CRT.h"
#include "ListItem.h"
#include "String.h"
20
#include "ProcessList.h"
21
#include "RichString.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
22
23
24
25

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

26
27
28
29
30
#ifndef USE_FUNKY_MODES
#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;
46
47
48
49
50
   char* uiName;
   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;
57
58
59
60
61
   char* name;
   char* uiName;
   char* caption;
   MeterType_Init init;
   MeterType_Done done;
62
   MeterType_SetMode setMode;
63
64
65
66
67
68
69
70
71
72
73
74
75
   Meter_Draw draw;
};

struct Meter_ {
   Object super;
   char* caption;
   MeterType* type;
   int mode;
   int param;
   Meter_Draw draw;
   void* drawBuffer;
   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
87
88
89
90
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
91
92
}*/

93
94
95
96
97
98
#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
99
#include "BatteryMeter.h"
100
#include "ClockMeter.h"
101
#include "HostnameMeter.h"
102

Hisham Muhammad's avatar
Hisham Muhammad committed
103

Hisham Muhammad's avatar
Hisham Muhammad committed
104
105
106
#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif
Hisham Muhammad's avatar
Hisham Muhammad committed
107
108
109
#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
Hisham Muhammad's avatar
Hisham Muhammad committed
110

111
#ifdef DEBUG
Hisham Muhammad's avatar
Hisham Muhammad committed
112
char* METER_CLASS = "Meter";
113
114
115
#else
#define METER_CLASS NULL
#endif
Hisham Muhammad's avatar
Hisham Muhammad committed
116

117
118
119
120
MeterType* Meter_types[] = {
   &CPUMeter,
   &ClockMeter,
   &LoadAverageMeter,
Hisham Muhammad's avatar
Hisham Muhammad committed
121
   &LoadMeter,
122
123
124
125
   &MemoryMeter,
   &SwapMeter,
   &TasksMeter,
   &UptimeMeter,
Hisham Muhammad's avatar
Hisham Muhammad committed
126
   &BatteryMeter,
127
   &AllCPUsMeter,
128
   &HostnameMeter,
129
   NULL
Hisham Muhammad's avatar
Hisham Muhammad committed
130
131
132
133
};

static RichString Meter_stringBuffer;

134
135
Meter* Meter_new(ProcessList* pl, int param, MeterType* type) {
   Meter* this = calloc(sizeof(Meter), 1);
136
137
138
   Object_setClass(this, METER_CLASS);
   ((Object*)this)->delete = Meter_delete;
   ((Object*)this)->display = type->display;
139
140
141
142
143
144
145
146
147
148
149
   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
150
151
152
153
154
}

void Meter_delete(Object* cast) {
   Meter* this = (Meter*) cast;
   assert (this != NULL);
155
156
157
158
159
160
161
   if (this->type->done) {
      this->type->done(this);
   }
   if (this->drawBuffer)
      free(this->drawBuffer);
   free(this->caption);
   free(this->values);
Hisham Muhammad's avatar
Hisham Muhammad committed
162
163
164
   free(this);
}

165
166
167
168
169
void Meter_setCaption(Meter* this, char* caption) {
   free(this->caption);
   this->caption = strdup(caption);
}

170
static inline void Meter_displayToStringBuffer(Meter* this, char* buffer) {
171
172
173
174
175
   MeterType* type = this->type;
   Object_Display display = ((Object*)this)->display;
   if (display) {
      display((Object*)this, &Meter_stringBuffer);
   } else {
176
      RichString_initVal(Meter_stringBuffer);
177
      RichString_append(&Meter_stringBuffer, CRT_colors[type->attributes[0]], buffer);
Hisham Muhammad's avatar
Hisham Muhammad committed
178
   }
179
180
181
}

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

      MeterMode* mode = Meter_modes[modeIndex];
      this->draw = mode->draw;
      this->h = mode->h;
Hisham Muhammad's avatar
Hisham Muhammad committed
200
   }
201
   this->mode = modeIndex;
Hisham Muhammad's avatar
Hisham Muhammad committed
202
203
}

204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
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
219
220
}

221
222
/* ---------- TextMeterMode ---------- */

223
static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
224
225
226
227
228
229
230
231
232
233
234
235
   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;
   Meter_displayToStringBuffer(this, buffer);
   mvhline(y, x, ' ', CRT_colors[DEFAULT_COLOR]);
   attrset(CRT_colors[RESET_COLOR]);
236
   RichString_printVal(Meter_stringBuffer, y, x);
237
238
239
240
}

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

241
static char BarMeterMode_characters[] = "|#*@$%&";
242

243
static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
244
245
246
247
   MeterType* type = this->type;
   char buffer[METER_BUFFER_LEN];
   type->setValues(this, buffer, METER_BUFFER_LEN - 1);

Hisham Muhammad's avatar
Hisham Muhammad committed
248
249
   w -= 2;
   attrset(CRT_colors[METER_TEXT]);
Hisham Muhammad's avatar
Hisham Muhammad committed
250
251
   int captionLen = 3;
   mvaddnstr(y, x, this->caption, captionLen);
Hisham Muhammad's avatar
Hisham Muhammad committed
252
253
254
255
256
257
258
259
260
261
262
263
264
265
   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] = ' ';

266
   sprintf(bar + (w-strlen(buffer)), "%s", buffer);
Hisham Muhammad's avatar
Hisham Muhammad committed
267
268
269
270

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

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

313
314
315
316
317
318
#ifdef USE_FUNKY_MODES

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

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

319
320
321
322
323
324
325
326
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
327
328
329
330
};

static char* GraphMeterMode_characters = "^`'-.,_~'`-.,_~'`-.,_";

331
static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360

   if (!this->drawBuffer) this->drawBuffer = calloc(sizeof(double), METER_BUFFER_LEN);
   double* drawBuffer = (double*) this->drawBuffer;

   for (int i = 0; i < METER_BUFFER_LEN - 1; i++)
      drawBuffer[i] = drawBuffer[i+1];

   MeterType* type = this->type;
   char buffer[METER_BUFFER_LEN];
   type->setValues(this, buffer, METER_BUFFER_LEN - 1);

   double value = 0.0;
   for (int i = 0; i < type->items; i++)
      value += this->values[i];
   value /= this->total;
   drawBuffer[METER_BUFFER_LEN - 1] = value;
   for (int i = METER_BUFFER_LEN - w, k = 0; i < METER_BUFFER_LEN; i++, k++) {
      double value = drawBuffer[i];
      DrawDot( CRT_colors[DEFAULT_COLOR], y, ' ' );
      DrawDot( CRT_colors[DEFAULT_COLOR], y+1, ' ' );
      DrawDot( CRT_colors[DEFAULT_COLOR], y+2, ' ' );
      
      double threshold = 1.00;
      for (int i = 0; i < 21; i++, threshold -= 0.05)
         if (value >= threshold) {
            DrawDot(CRT_colors[GraphMeterMode_colors[i]], y+(i/7.0), GraphMeterMode_characters[i]);
            break;
         }
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
361
362
363
   attrset(CRT_colors[RESET_COLOR]);
}

364
365
366
367
368
369
370
/* ---------- LEDMeterMode ---------- */

static char* LEDMeterMode_digits[3][10] = {
   { " __ ","    "," __ "," __ ","    "," __ "," __ "," __ "," __ "," __ "},
   { "|  |","   |"," __|"," __|","|__|","|__ ","|__ ","   |","|__|","|__|"},
   { "|__|","   |","|__ "," __|","   |"," __|","|__|","   |","|__|"," __|"},
};
Hisham Muhammad's avatar
Hisham Muhammad committed
371

372
373
374
375
376
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]);
}

377
static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
378
379
380
381
382
383
   MeterType* type = this->type;
   char buffer[METER_BUFFER_LEN];
   type->setValues(this, buffer, METER_BUFFER_LEN - 1);
  
   Meter_displayToStringBuffer(this, buffer);

Hisham Muhammad's avatar
Hisham Muhammad committed
384
385
386
   attrset(CRT_colors[LED_COLOR]);
   mvaddstr(y+2, x, this->caption);
   int xx = x + strlen(this->caption);
387
   for (int i = 0; i < Meter_stringBuffer.len; i++) {
388
      char c = RichString_getCharVal(Meter_stringBuffer, i);
Hisham Muhammad's avatar
Hisham Muhammad committed
389
      if (c >= '0' && c <= '9') {
390
391
         LEDMeterMode_drawDigit(xx, y, c-48);
         xx += 4;
Hisham Muhammad's avatar
Hisham Muhammad committed
392
393
394
395
396
397
398
399
      } else {
         mvaddch(y+2, xx, c);
         xx += 1;
      }
   }
   attrset(CRT_colors[RESET_COLOR]);
}

400
#endif
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439

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