Meter.c 16.2 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

#include "RichString.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
11
12
#include "Object.h"
#include "CRT.h"
David Hunt's avatar
David Hunt committed
13
#include "StringUtils.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
14
#include "ListItem.h"
15
#include "Settings.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
#include <assert.h>
22
#include <sys/time.h>
23

Hisham Muhammad's avatar
Hisham Muhammad committed
24
#define METER_BUFFER_LEN 256
Hisham Muhammad's avatar
Hisham Muhammad committed
25

26
27
#define GRAPH_DELAY (DEFAULT_DELAY/2)

28
29
#define GRAPH_HEIGHT 4 /* Unit: rows (lines) */

Hisham Muhammad's avatar
Hisham Muhammad committed
30
/*{
Hisham Muhammad's avatar
Hisham Muhammad committed
31
#include "ListItem.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
32
33

#include <sys/time.h>
Hisham Muhammad's avatar
Hisham Muhammad committed
34
35
36

typedef struct Meter_ Meter;

37
38
39
typedef void(*Meter_Init)(Meter*);
typedef void(*Meter_Done)(Meter*);
typedef void(*Meter_UpdateMode)(Meter*, int);
40
typedef void(*Meter_SetValues)(Meter*, char*, int);
Hisham Muhammad's avatar
Hisham Muhammad committed
41
42
typedef void(*Meter_Draw)(Meter*, int, int, int);

43
44
45
46
47
48
49
50
51
52
typedef struct MeterClass_ {
   ObjectClass super;
   const Meter_Init init;
   const Meter_Done done;
   const Meter_UpdateMode updateMode;
   const Meter_Draw draw;
   const Meter_SetValues setValues;
   const int defaultMode;
   const double total;
   const int* attributes;
Hisham Muhammad's avatar
Hisham Muhammad committed
53
54
55
   const char* name;
   const char* uiName;
   const char* caption;
56
   const char* description;
57
58
   const char maxItems;
   char curItems;
59
60
61
62
63
64
65
66
67
68
69
70
} MeterClass;

#define As_Meter(this_)                ((MeterClass*)((this_)->super.klass))
#define Meter_initFn(this_)            As_Meter(this_)->init
#define Meter_init(this_)              As_Meter(this_)->init((Meter*)(this_))
#define Meter_done(this_)              As_Meter(this_)->done((Meter*)(this_))
#define Meter_updateModeFn(this_)      As_Meter(this_)->updateMode
#define Meter_updateMode(this_, m_)    As_Meter(this_)->updateMode((Meter*)(this_), m_)
#define Meter_drawFn(this_)            As_Meter(this_)->draw
#define Meter_doneFn(this_)            As_Meter(this_)->done
#define Meter_setValues(this_, c_, i_) As_Meter(this_)->setValues((Meter*)(this_), c_, i_)
#define Meter_defaultMode(this_)       As_Meter(this_)->defaultMode
71
72
#define Meter_getItems(this_)          As_Meter(this_)->curItems
#define Meter_setItems(this_, n_)      As_Meter(this_)->curItems = (n_)
73
74
75
#define Meter_attributes(this_)        As_Meter(this_)->attributes
#define Meter_name(this_)              As_Meter(this_)->name
#define Meter_uiName(this_)            As_Meter(this_)->uiName
76
77
78

struct Meter_ {
   Object super;
79
80
   Meter_Draw draw;
   
81
82
83
   char* caption;
   int mode;
   int param;
84
   void* drawData;
85
   int h;
Hisham Muhammad's avatar
Hisham Muhammad committed
86
   struct ProcessList_* pl;
Hisham Muhammad's avatar
Hisham Muhammad committed
87
88
89
90
   double* values;
   double total;
};

91
92
93
94
95
typedef struct MeterMode_ {
   Meter_Draw draw;
   const char* uiName;
   int h;
} MeterMode;
96

97
98
99
100
101
102
103
104
105
typedef enum {
   CUSTOM_METERMODE = 0,
   BAR_METERMODE,
   TEXT_METERMODE,
   GRAPH_METERMODE,
   LED_METERMODE,
   LAST_METERMODE
} MeterModeId;

106
107
108
typedef struct GraphData_ {
   struct timeval time;
   double values[METER_BUFFER_LEN];
109
110
111
   int colors[METER_BUFFER_LEN][GRAPH_HEIGHT];
   double *prevItemSums;
   double *currentItemSums;
112
113
} GraphData;

Hisham Muhammad's avatar
Hisham Muhammad committed
114
115
116
117
118
}*/

#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif
Hisham Muhammad's avatar
Hisham Muhammad committed
119
120
121
#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
Hisham Muhammad's avatar
Hisham Muhammad committed
122

123
124
125
126
127
MeterClass Meter_class = {
   .super = {
      .extends = Class(Object)
   }
};
Hisham Muhammad's avatar
Hisham Muhammad committed
128

Hisham Muhammad's avatar
Hisham Muhammad committed
129
Meter* Meter_new(struct ProcessList_* pl, int param, MeterClass* type) {
130
   Meter* this = calloc(1, sizeof(Meter));
131
   Object_setClass(this, type);
132
133
134
   this->h = 1;
   this->param = param;
   this->pl = pl;
135
136
137
138
139
140
   char maxItems = type->maxItems;
   if (maxItems == 0) {
      maxItems = 1;
   }
   type->curItems = maxItems;
   this->values = calloc(maxItems, sizeof(double));
141
142
   this->total = type->total;
   this->caption = strdup(type->caption);
143
144
145
   if (Meter_initFn(this))
      Meter_init(this);
   Meter_setMode(this, type->defaultMode);
146
   return this;
Hisham Muhammad's avatar
Hisham Muhammad committed
147
148
}

Christian Hesse's avatar
Christian Hesse committed
149
int Meter_humanUnit(char* buffer, unsigned long int value, int size) {
Hisham Muhammad's avatar
Hisham Muhammad committed
150
151
152
   const char * prefix = "KMGTPEZY";
   unsigned long int powi = 1;
   unsigned int written, powj = 1, precision = 2;
Christian Hesse's avatar
Christian Hesse committed
153

Hisham Muhammad's avatar
Hisham Muhammad committed
154
155
156
   for(;;) {
      if (value / 1024 < powi)
         break;
Christian Hesse's avatar
Christian Hesse committed
157

Hisham Muhammad's avatar
Hisham Muhammad committed
158
159
      if (prefix[1] == 0)
         break;
Christian Hesse's avatar
Christian Hesse committed
160

Hisham Muhammad's avatar
Hisham Muhammad committed
161
162
163
      powi *= 1024;
      ++prefix;
   }
Christian Hesse's avatar
Christian Hesse committed
164

165
166
167
   if (*prefix == 'K')
      precision = 0;

Hisham Muhammad's avatar
Hisham Muhammad committed
168
169
170
171
172
   for (; precision > 0; precision--) {
      powj *= 10;
      if (value / powi < powj)
         break;
   }
Christian Hesse's avatar
Christian Hesse committed
173

Hisham Muhammad's avatar
Hisham Muhammad committed
174
175
   written = snprintf(buffer, size, "%.*f%c",
      precision, (double) value / powi, *prefix);
Christian Hesse's avatar
Christian Hesse committed
176

Hisham Muhammad's avatar
Hisham Muhammad committed
177
   return written;
Christian Hesse's avatar
Christian Hesse committed
178
179
}

Hisham Muhammad's avatar
Hisham Muhammad committed
180
void Meter_delete(Object* cast) {
181
182
   if (!cast)
      return;
Hisham Muhammad's avatar
Hisham Muhammad committed
183
   Meter* this = (Meter*) cast;
184
185
   if (Meter_doneFn(this)) {
      Meter_done(this);
186
   }
187
188
   if (this->drawData)
      free(this->drawData);
189
190
   free(this->caption);
   free(this->values);
Hisham Muhammad's avatar
Hisham Muhammad committed
191
192
193
   free(this);
}

Hisham Muhammad's avatar
Hisham Muhammad committed
194
void Meter_setCaption(Meter* this, const char* caption) {
195
196
197
198
   free(this->caption);
   this->caption = strdup(caption);
}

199
static inline void Meter_displayBuffer(Meter* this, char* buffer, RichString* out) {
200
201
   if (Object_displayFn(this)) {
      Object_display(this, out);
202
   } else {
203
      RichString_write(out, CRT_colors[Meter_attributes(this)[0]], buffer);
Hisham Muhammad's avatar
Hisham Muhammad committed
204
   }
205
206
207
}

void Meter_setMode(Meter* this, int modeIndex) {
Hisham Muhammad's avatar
Hisham Muhammad committed
208
   if (modeIndex > 0 && modeIndex == this->mode)
209
210
211
212
      return;
   if (!modeIndex)
      modeIndex = 1;
   assert(modeIndex < LAST_METERMODE);
213
214
215
216
   if (Meter_defaultMode(this) == CUSTOM_METERMODE) {
      this->draw = Meter_drawFn(this);
      if (Meter_updateModeFn(this))
         Meter_updateMode(this, modeIndex);
217
   } else {
Hisham Muhammad's avatar
Hisham Muhammad committed
218
      assert(modeIndex >= 1);
219
220
221
      if (this->drawData)
         free(this->drawData);
      this->drawData = NULL;
Hisham Muhammad's avatar
Hisham Muhammad committed
222
223
224
225

      MeterMode* mode = Meter_modes[modeIndex];
      this->draw = mode->draw;
      this->h = mode->h;
Hisham Muhammad's avatar
Hisham Muhammad committed
226
   }
227
   this->mode = modeIndex;
Hisham Muhammad's avatar
Hisham Muhammad committed
228
229
}

230
ListItem* Meter_toListItem(Meter* this, bool moving) {
231
232
233
234
235
236
237
238
239
240
241
   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];
242
   snprintf(buffer, 50, "%s%s%s", Meter_uiName(this), number, mode);
243
244
245
   ListItem* li = ListItem_new(buffer, 0);
   li->moving = moving;
   return li;
Hisham Muhammad's avatar
Hisham Muhammad committed
246
247
}

248
249
/* ---------- TextMeterMode ---------- */

250
static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
251
   char buffer[METER_BUFFER_LEN];
252
   Meter_setValues(this, buffer, METER_BUFFER_LEN - 1);
Hisham Muhammad's avatar
Hisham Muhammad committed
253
   (void) w;
254
255
256
257
258
259

   attrset(CRT_colors[METER_TEXT]);
   mvaddstr(y, x, this->caption);
   int captionLen = strlen(this->caption);
   x += captionLen;
   attrset(CRT_colors[RESET_COLOR]);
260
261
262
263
   RichString_begin(out);
   Meter_displayBuffer(this, buffer, &out);
   RichString_printVal(out, y, x);
   RichString_end(out);
264
265
266
267
}

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

268
static char BarMeterMode_characters[] = "|#*@$%&";
269

270
static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
271
   char buffer[METER_BUFFER_LEN];
272
   Meter_setValues(this, buffer, METER_BUFFER_LEN - 1);
273

Hisham Muhammad's avatar
Hisham Muhammad committed
274
275
   w -= 2;
   attrset(CRT_colors[METER_TEXT]);
Hisham Muhammad's avatar
Hisham Muhammad committed
276
277
   int captionLen = 3;
   mvaddnstr(y, x, this->caption, captionLen);
Hisham Muhammad's avatar
Hisham Muhammad committed
278
279
280
281
282
283
284
285
   x += captionLen;
   w -= captionLen;
   attrset(CRT_colors[BAR_BORDER]);
   mvaddch(y, x, '[');
   mvaddch(y, x + w, ']');
   
   w--;
   x++;
286
287
288
289
290
291

   if (w < 1) {
      attrset(CRT_colors[RESET_COLOR]);
      return;
   }
   char bar[w + 1];
Hisham Muhammad's avatar
Hisham Muhammad committed
292
293
294
295
296
   
   int blockSizes[10];
   for (int i = 0; i < w; i++)
      bar[i] = ' ';

297
   const size_t barOffset = w - MIN((int)strlen(buffer), w);
298
   snprintf(bar + barOffset, w - barOffset + 1, "%s", buffer);
Hisham Muhammad's avatar
Hisham Muhammad committed
299
300
301

   // First draw in the bar[] buffer...
   int offset = 0;
302
303
   int items = Meter_getItems(this);
   for (int i = 0; i < items; i++) {
Hisham Muhammad's avatar
Hisham Muhammad committed
304
      double value = this->values[i];
305
306
      value = MAX(value, 0);
      value = MIN(value, this->total);
Hisham Muhammad's avatar
Hisham Muhammad committed
307
308
309
310
311
312
313
      if (value > 0) {
         blockSizes[i] = ceil((value/this->total) * w);
      } else {
         blockSizes[i] = 0;
      }
      int nextOffset = offset + blockSizes[i];
      // (Control against invalid values)
314
      nextOffset = MIN(MAX(nextOffset, 0), w);
Hisham Muhammad's avatar
Hisham Muhammad committed
315
316
317
      for (int j = offset; j < nextOffset; j++)
         if (bar[j] == ' ') {
            if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
318
               bar[j] = BarMeterMode_characters[i];
Hisham Muhammad's avatar
Hisham Muhammad committed
319
320
321
322
323
324
325
326
327
            } else {
               bar[j] = '|';
            }
         }
      offset = nextOffset;
   }

   // ...then print the buffer.
   offset = 0;
328
329
   for (int i = 0; i < items; i++) {
      attrset(CRT_colors[Meter_attributes(this)[i]]);
Hisham Muhammad's avatar
Hisham Muhammad committed
330
331
332
333
334
335
336
337
338
339
340
341
342
343
      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]);
}

344
345
/* ---------- GraphMeterMode ---------- */

346
347
#ifdef HAVE_LIBNCURSESW

Christian Hesse's avatar
Christian Hesse committed
348
349
#define PIXPERROW_UTF8 4
static const char* GraphMeterMode_dotsUtf8[] = {
350
   /*00*/" ", /*01*/"⢀", /*02*/"⢠", /*03*/"⢰", /*04*/ "⢸",
Christian Hesse's avatar
Christian Hesse committed
351
352
353
354
   /*10*/"⡀", /*11*/"⣀", /*12*/"⣠", /*13*/"⣰", /*14*/ "⣸",
   /*20*/"⡄", /*21*/"⣄", /*22*/"⣤", /*23*/"⣴", /*24*/ "⣼",
   /*30*/"⡆", /*31*/"⣆", /*32*/"⣦", /*33*/"⣶", /*34*/ "⣾",
   /*40*/"⡇", /*41*/"⣇", /*42*/"⣧", /*43*/"⣷", /*44*/ "⣿"
Hisham Muhammad's avatar
Hisham Muhammad committed
355
};
356

357
358
#endif

Christian Hesse's avatar
Christian Hesse committed
359
360
361
362
363
#define PIXPERROW_ASCII 2
static const char* GraphMeterMode_dotsAscii[] = {
   /*00*/" ", /*01*/".", /*02*/":",
   /*10*/".", /*11*/".", /*12*/":",
   /*20*/":", /*21*/":", /*22*/":"
364
365
};

Christian Hesse's avatar
Christian Hesse committed
366
static const char** GraphMeterMode_dots;
Hisham Muhammad's avatar
Hisham Muhammad committed
367
static int GraphMeterMode_pixPerRow;
368

369
static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
370

371
   const int nValues = METER_BUFFER_LEN;
372
373
374
375
376
   if (!this->drawData) {
      this->drawData = calloc(1, sizeof(GraphData));
      GraphData* data = (GraphData*) this->drawData;
      for (int i = 0; i < nValues; i++) {
         for (int line = 0; line < GRAPH_HEIGHT; line++) {
377
            data->colors[i][line] = BAR_SHADOW;
378
379
380
381
         }
      }
   }
   GraphData* data = (GraphData*) this->drawData;
Hisham Muhammad's avatar
Hisham Muhammad committed
382

383
#ifdef HAVE_LIBNCURSESW
Hisham Muhammad's avatar
Hisham Muhammad committed
384
385
   if (CRT_utf8) {
      GraphMeterMode_dots = GraphMeterMode_dotsUtf8;
Hisham Muhammad's avatar
Hisham Muhammad committed
386
      GraphMeterMode_pixPerRow = PIXPERROW_UTF8;
387
388
389
   } else
#endif
   {
Hisham Muhammad's avatar
Hisham Muhammad committed
390
      GraphMeterMode_dots = GraphMeterMode_dotsAscii;
Hisham Muhammad's avatar
Hisham Muhammad committed
391
      GraphMeterMode_pixPerRow = PIXPERROW_ASCII;
Hisham Muhammad's avatar
Hisham Muhammad committed
392
393
394
395
396
397
398
   }

   attrset(CRT_colors[METER_TEXT]);
   int captionLen = 3;
   mvaddnstr(y, x, this->caption, captionLen);
   x += captionLen;
   w -= captionLen;
399
   
400
401
402
   struct timeval now;
   gettimeofday(&now, NULL);
   if (!timercmp(&now, &(data->time), <)) {
403
      struct timeval delay = { .tv_sec = (int)(CRT_delay/10), .tv_usec = (CRT_delay-((int)(CRT_delay/10)*10)) * 100000 };
404
      timeradd(&now, &delay, &(data->time));
405

406
      for (int i = 0; i < nValues - 1; i++) {
407
         data->values[i] = data->values[i+1];
408
409
         memcpy(data->colors[i], data->colors[i+1], sizeof(data->colors[i]));
      }
410
411
   
      char buffer[nValues];
412
      Meter_setValues(this, buffer, nValues - 1);
413

414
      int items = Meter_getItems(this);
415
416
417
418
419
420
421
422
423
424
425
426
427

      if (!data->prevItemSums) {
         data->prevItemSums = calloc(items, sizeof(*data->prevItemSums));
      }
      if (!data->currentItemSums) {
         data->currentItemSums = calloc(items, sizeof(*data->currentItemSums));
      }
      double *prevItemSums = data->prevItemSums;
      double *currentItemSums = data->currentItemSums;
      
      currentItemSums[0] = this->values[0];
      for (int i = 0; i < items; i++) {
         prevItemSums[i] = currentItemSums[i];
428
         currentItemSums[i] = ((i > 0) ? currentItemSums[i-1] : 0.0) + this->values[i];
429
430
431
432
433
      }
      data->values[nValues - 1] = currentItemSums[items - 1] / this->total;

      // Determine the dominant color of the cell in graph
      for (int line = 0; line < GRAPH_HEIGHT; line++) {
434
         int dominantColor = BAR_SHADOW;
435
436
437
         double maxArea = 0.0;
         for (int i = 0; i < items; i++) {
             double area;
438
439
440
441
             double upperBound = this->total * (GRAPH_HEIGHT - line) / GRAPH_HEIGHT;
             double lowerBound = this->total * (GRAPH_HEIGHT - 1 - line) / GRAPH_HEIGHT;
             area = MAX(lowerBound, MIN(currentItemSums[i], upperBound)) +
                    MAX(lowerBound, MIN(prevItemSums[i], upperBound));
442
443
             area -= MAX(lowerBound, MIN(((i > 0) ? currentItemSums[i-1] : 0.0), upperBound)) +
                     MAX(lowerBound, MIN(((i > 0) ? prevItemSums[i-1] : 0.0), upperBound));
444
445
             if (area > maxArea) {
                maxArea = area;
446
                dominantColor = Meter_attributes(this)[i];
447
448
449
450
             }
         }
         data->colors[nValues - 1][line] = dominantColor;
      }
451
452
   }
   
Hisham Muhammad's avatar
Hisham Muhammad committed
453
   for (int i = nValues - (w*2) + 2, k = 0; i < nValues; i+=2, k++) {
454
455
456
      const double dot = (1.0 / (GraphMeterMode_pixPerRow * GRAPH_HEIGHT));
      int v1 = MIN(GraphMeterMode_pixPerRow * GRAPH_HEIGHT, MAX(1, data->values[i] / dot));
      int v2 = MIN(GraphMeterMode_pixPerRow * GRAPH_HEIGHT, MAX(1, data->values[i+1] / dot));
Christian Hesse's avatar
Christian Hesse committed
457

458
459
460
      for (int line = 0; line < GRAPH_HEIGHT; line++) {
         int line1 = MIN(GraphMeterMode_pixPerRow, MAX(0, v1 - (GraphMeterMode_pixPerRow * (GRAPH_HEIGHT - 1 - line))));
         int line2 = MIN(GraphMeterMode_pixPerRow, MAX(0, v2 - (GraphMeterMode_pixPerRow * (GRAPH_HEIGHT - 1 - line))));
461
         
462
         attrset(CRT_colors[data->colors[i+1][line]]);
Hisham Muhammad's avatar
Hisham Muhammad committed
463
         mvaddstr(y+line, x+k, GraphMeterMode_dots[line1 * (GraphMeterMode_pixPerRow + 1) + line2]);
Hisham Muhammad's avatar
Hisham Muhammad committed
464
      }
465
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
466
467
468
   attrset(CRT_colors[RESET_COLOR]);
}

469
470
/* ---------- LEDMeterMode ---------- */

Christian Hesse's avatar
Christian Hesse committed
471
472
473
474
static const char* LEDMeterMode_digitsAscii[] = {
   " __ ","    "," __ "," __ ","    "," __ "," __ "," __ "," __ "," __ ",
   "|  |","   |"," __|"," __|","|__|","|__ ","|__ ","   |","|__|","|__|",
   "|__|","   |","|__ "," __|","   |"," __|","|__|","   |","|__|"," __|"
475
};
Hisham Muhammad's avatar
Hisham Muhammad committed
476

477
478
#ifdef HAVE_LIBNCURSESW

Christian Hesse's avatar
Christian Hesse committed
479
480
481
482
static const char* LEDMeterMode_digitsUtf8[] = {
   "┌──┐","  ┐ ","╶──┐","╶──┐","╷  ╷","┌──╴","┌──╴","╶──┐","┌──┐","┌──┐",
   "│  │","  │ ","┌──┘"," ──┤","└──┤","└──┐","├──┐","   │","├──┤","└──┤",
   "└──┘","  ╵ ","└──╴","╶──┘","   ╵","╶──┘","└──┘","   ╵","└──┘"," ──┘"
483
484
};

485
486
#endif

Christian Hesse's avatar
Christian Hesse committed
487
static const char** LEDMeterMode_digits;
Hisham Muhammad's avatar
Hisham Muhammad committed
488

489
static void LEDMeterMode_drawDigit(int x, int y, int n) {
Hisham Muhammad's avatar
Hisham Muhammad committed
490
   for (int i = 0; i < 3; i++)
Christian Hesse's avatar
Christian Hesse committed
491
      mvaddstr(y+i, x, LEDMeterMode_digits[i * 10 + n]);
492
493
}

494
static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
Hisham Muhammad's avatar
Hisham Muhammad committed
495
   (void) w;
Hisham Muhammad's avatar
Hisham Muhammad committed
496

497
498
#ifdef HAVE_LIBNCURSESW
   if (CRT_utf8)
Hisham Muhammad's avatar
Hisham Muhammad committed
499
      LEDMeterMode_digits = LEDMeterMode_digitsUtf8;
500
501
   else
#endif
Hisham Muhammad's avatar
Hisham Muhammad committed
502
503
      LEDMeterMode_digits = LEDMeterMode_digitsAscii;

504
   char buffer[METER_BUFFER_LEN];
505
   Meter_setValues(this, buffer, METER_BUFFER_LEN - 1);
506
507
508
   
   RichString_begin(out);
   Meter_displayBuffer(this, buffer, &out);
509

510
511
   int yText =
#ifdef HAVE_LIBNCURSESW
Hisham Muhammad's avatar
Hisham Muhammad committed
512
      CRT_utf8 ? y+1 :
513
#endif
Hisham Muhammad's avatar
Hisham Muhammad committed
514
      y+2;
Hisham Muhammad's avatar
Hisham Muhammad committed
515
   attrset(CRT_colors[LED_COLOR]);
516
   mvaddstr(yText, x, this->caption);
Hisham Muhammad's avatar
Hisham Muhammad committed
517
   int xx = x + strlen(this->caption);
518
519
520
   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
521
      if (c >= '0' && c <= '9') {
522
523
         LEDMeterMode_drawDigit(xx, y, c-48);
         xx += 4;
Hisham Muhammad's avatar
Hisham Muhammad committed
524
      } else {
525
         mvaddch(yText, xx, c);
Hisham Muhammad's avatar
Hisham Muhammad committed
526
527
528
529
         xx += 1;
      }
   }
   attrset(CRT_colors[RESET_COLOR]);
530
   RichString_end(out);
Hisham Muhammad's avatar
Hisham Muhammad committed
531
532
}

533
534
535
536
537
538
539
540
541
542
543
544
545
546
static MeterMode BarMeterMode = {
   .uiName = "Bar",
   .h = 1,
   .draw = BarMeterMode_draw,
};

static MeterMode TextMeterMode = {
   .uiName = "Text",
   .h = 1,
   .draw = TextMeterMode_draw,
};

static MeterMode GraphMeterMode = {
   .uiName = "Graph",
547
   .h = GRAPH_HEIGHT,
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
   .draw = GraphMeterMode_draw,
};

static MeterMode LEDMeterMode = {
   .uiName = "LED",
   .h = 3,
   .draw = LEDMeterMode_draw,
};

MeterMode* Meter_modes[] = {
   NULL,
   &BarMeterMode,
   &TextMeterMode,
   &GraphMeterMode,
   &LEDMeterMode,
   NULL
};
Hisham Muhammad's avatar
Hisham Muhammad committed
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594

/* Blank meter */

static void BlankMeter_setValues(Meter* this, char* buffer, int size) {
   (void) this; (void) buffer; (void) size;
}

static void BlankMeter_display(Object* cast, RichString* out) {
   (void) cast;
   RichString_prune(out);
}

int BlankMeter_attributes[] = {
   DEFAULT_COLOR
};

MeterClass BlankMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = BlankMeter_display,
   },
   .setValues = BlankMeter_setValues,
   .defaultMode = TEXT_METERMODE,
   .total = 100.0,
   .attributes = BlankMeter_attributes,
   .name = "Blank",
   .uiName = "Blank",
   .caption = ""
};