Meter.c 17.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-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_)
Explorer09's avatar
Explorer09 committed
73
#define Meter_getMaxItems(this_)       As_Meter(this_)->maxItems
74
75
76
#define Meter_attributes(this_)        As_Meter(this_)->attributes
#define Meter_name(this_)              As_Meter(this_)->name
#define Meter_uiName(this_)            As_Meter(this_)->uiName
77
78
79

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

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

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

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

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

#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif
Hisham Muhammad's avatar
Hisham Muhammad committed
120
121
122
#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
123
124
125
#ifndef CLAMP
#define CLAMP(x, low, high)  (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
#endif
Hisham Muhammad's avatar
Hisham Muhammad committed
126

127
128
129
130
131
MeterClass Meter_class = {
   .super = {
      .extends = Class(Object)
   }
};
Hisham Muhammad's avatar
Hisham Muhammad committed
132

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

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

Hisham Muhammad's avatar
Hisham Muhammad committed
158
159
160
   for(;;) {
      if (value / 1024 < powi)
         break;
Christian Hesse's avatar
Christian Hesse committed
161

Hisham Muhammad's avatar
Hisham Muhammad committed
162
163
      if (prefix[1] == 0)
         break;
Christian Hesse's avatar
Christian Hesse committed
164

Hisham Muhammad's avatar
Hisham Muhammad committed
165
166
167
      powi *= 1024;
      ++prefix;
   }
Christian Hesse's avatar
Christian Hesse committed
168

169
170
171
   if (*prefix == 'K')
      precision = 0;

Hisham Muhammad's avatar
Hisham Muhammad committed
172
173
174
175
176
   for (; precision > 0; precision--) {
      powj *= 10;
      if (value / powi < powj)
         break;
   }
Christian Hesse's avatar
Christian Hesse committed
177

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

Hisham Muhammad's avatar
Hisham Muhammad committed
181
   return written;
Christian Hesse's avatar
Christian Hesse committed
182
183
}

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

Hisham Muhammad's avatar
Hisham Muhammad committed
198
void Meter_setCaption(Meter* this, const char* caption) {
199
200
201
202
   free(this->caption);
   this->caption = strdup(caption);
}

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

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

      MeterMode* mode = Meter_modes[modeIndex];
      this->draw = mode->draw;
      this->h = mode->h;
Hisham Muhammad's avatar
Hisham Muhammad committed
230
   }
231
   this->mode = modeIndex;
Hisham Muhammad's avatar
Hisham Muhammad committed
232
233
}

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

252
253
/* ---------- TextMeterMode ---------- */

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

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

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

272
static char BarMeterMode_characters[] = "|#*@$%&.";
273

274
static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
275
   char buffer[METER_BUFFER_LEN];
276
   Meter_setValues(this, buffer, METER_BUFFER_LEN - 1);
277

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

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

301
   const size_t barOffset = w - MIN((int)strlen(buffer), w);
302
   snprintf(bar + barOffset, w - barOffset + 1, "%s", buffer);
Hisham Muhammad's avatar
Hisham Muhammad committed
303
304
305

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

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

348
349
/* ---------- GraphMeterMode ---------- */

350
351
#ifdef HAVE_LIBNCURSESW

Christian Hesse's avatar
Christian Hesse committed
352
353
#define PIXPERROW_UTF8 4
static const char* GraphMeterMode_dotsUtf8[] = {
354
   /*00*/" ", /*01*/"⢀", /*02*/"⢠", /*03*/"⢰", /*04*/ "⢸",
Christian Hesse's avatar
Christian Hesse committed
355
356
357
358
   /*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
359
};
360

361
362
#endif

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

Christian Hesse's avatar
Christian Hesse committed
370
static const char** GraphMeterMode_dots;
Hisham Muhammad's avatar
Hisham Muhammad committed
371
static int GraphMeterMode_pixPerRow;
372

373
static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
374

375
   const int nValues = METER_BUFFER_LEN;
376
377
378
   if (!this->drawData) {
      this->drawData = calloc(1, sizeof(GraphData));
      GraphData* data = (GraphData*) this->drawData;
379
380
381
382
      if (!data->prevItems)
         data->prevItems = calloc(Meter_getMaxItems(this), sizeof(*data->prevItems));
      if (!data->currentItems)
         data->currentItems = calloc(Meter_getMaxItems(this), sizeof(*data->currentItems));
383
384
      for (int i = 0; i < nValues; i++) {
         for (int line = 0; line < GRAPH_HEIGHT; line++) {
385
            data->colors[i][line] = BAR_SHADOW;
386
387
388
389
         }
      }
   }
   GraphData* data = (GraphData*) this->drawData;
Hisham Muhammad's avatar
Hisham Muhammad committed
390

391
#ifdef HAVE_LIBNCURSESW
Hisham Muhammad's avatar
Hisham Muhammad committed
392
393
   if (CRT_utf8) {
      GraphMeterMode_dots = GraphMeterMode_dotsUtf8;
Hisham Muhammad's avatar
Hisham Muhammad committed
394
      GraphMeterMode_pixPerRow = PIXPERROW_UTF8;
395
396
397
   } else
#endif
   {
Hisham Muhammad's avatar
Hisham Muhammad committed
398
      GraphMeterMode_dots = GraphMeterMode_dotsAscii;
Hisham Muhammad's avatar
Hisham Muhammad committed
399
      GraphMeterMode_pixPerRow = PIXPERROW_ASCII;
Hisham Muhammad's avatar
Hisham Muhammad committed
400
401
402
403
404
405
406
   }

   attrset(CRT_colors[METER_TEXT]);
   int captionLen = 3;
   mvaddnstr(y, x, this->caption, captionLen);
   x += captionLen;
   w -= captionLen;
407
   
408
409
410
   struct timeval now;
   gettimeofday(&now, NULL);
   if (!timercmp(&now, &(data->time), <)) {
411
      struct timeval delay = { .tv_sec = (int)(CRT_delay/10), .tv_usec = (CRT_delay-((int)(CRT_delay/10)*10)) * 100000 };
412
      timeradd(&now, &delay, &(data->time));
413

414
      for (int i = 0; i < nValues - 1; i++) {
415
         data->values[i] = data->values[i+1];
416
417
         memcpy(data->colors[i], data->colors[i+1], sizeof(data->colors[i]));
      }
418
419
   
      char buffer[nValues];
420
      Meter_setValues(this, buffer, nValues - 1);
421

422
      int items = Meter_getItems(this);
423

424
425
      double *prevItems = data->prevItems;
      double *currentItems = data->currentItems;
426
      
427
      data->values[nValues - 1] = 0.0;
428
      for (int i = 0; i < items; i++) {
429
430
431
         prevItems[i] = currentItems[i];
         currentItems[i] = this->values[i];
         data->values[nValues - 1] += this->values[i];
432
      }
433
      data->values[nValues - 1] /= this->total;
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
465
466
467
468
469
470
471
472
473
474
475
476
477
      double cellAmount = (this->total * 2) / GRAPH_HEIGHT;
      int    currCell = GRAPH_HEIGHT - 1;
      int    currCellColor = BAR_SHADOW;
      double currCellAvailable = cellAmount;
      double currCellDominant = 0.0;
      for (int i = 0; i < items; i++) {
         int    itemColor    = Meter_attributes(this)[i];
         double itemAmount   = prevItems[i] + currentItems[i];
         // How much I can contribute to the current bucket
         double itemFillCurr = CLAMP(itemAmount, itemAmount, currCellAvailable);
         // If I'm winning I take over the bucket color...
         if (itemFillCurr > currCellDominant) {
            currCellDominant = itemFillCurr;
            currCellColor = itemColor;
         }
         currCellAvailable -= itemFillCurr;
         // If I filled the bucket, time to give it to the winner:
         if (currCellAvailable == 0.0) {
            data->colors[nValues - 1][currCell] = currCellColor;
            // on to the next bucket
            currCell--;
            currCellColor = BAR_SHADOW;
            currCellAvailable = cellAmount;
            currCellDominant = 0.0;
            // Do I have anything else for more buckets?
            itemAmount -= itemFillCurr;
            if (itemAmount > 0.0) {
               // Anything to fill entire buckets?
               int fullCells = itemAmount / cellAmount;
               if (fullCells > 0) {
                  for (int c = 0; c < fullCells; c++) {
                     data->colors[nValues - 1][currCell - c] = itemColor;
                  }
                  currCell -= fullCells;
               }
               // If there's anything left, I'm the first in the next open bucket.
               double restAmount = itemAmount - (fullCells * cellAmount);
               if (restAmount > 0.0) {
                  currCellColor = itemColor;
                  currCellAvailable = cellAmount - restAmount;
                  currCellDominant = restAmount;
               }
            }
478
479
         }
      }
480
481
482
483
484
      for (int c = currCell; c >= 0; c--) {
         data->colors[nValues - 1][c] = currCellColor;
         currCellColor = BAR_SHADOW;
      }

485
   }
486
      
Hisham Muhammad's avatar
Hisham Muhammad committed
487
   for (int i = nValues - (w*2) + 2, k = 0; i < nValues; i+=2, k++) {
488
489
490
      int pix = GraphMeterMode_pixPerRow * GRAPH_HEIGHT;
      int v1 = MIN(pix, MAX(1, data->values[i] * pix));
      int v2 = MIN(pix, MAX(1, data->values[i+1] * pix));
Christian Hesse's avatar
Christian Hesse committed
491

492
493
494
      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))));
495
         
496
         attrset(CRT_colors[data->colors[i+1][line]]);
Hisham Muhammad's avatar
Hisham Muhammad committed
497
         mvaddstr(y+line, x+k, GraphMeterMode_dots[line1 * (GraphMeterMode_pixPerRow + 1) + line2]);
Hisham Muhammad's avatar
Hisham Muhammad committed
498
      }
499
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
500
501
502
   attrset(CRT_colors[RESET_COLOR]);
}

503
504
/* ---------- LEDMeterMode ---------- */

Christian Hesse's avatar
Christian Hesse committed
505
506
507
508
static const char* LEDMeterMode_digitsAscii[] = {
   " __ ","    "," __ "," __ ","    "," __ "," __ "," __ "," __ "," __ ",
   "|  |","   |"," __|"," __|","|__|","|__ ","|__ ","   |","|__|","|__|",
   "|__|","   |","|__ "," __|","   |"," __|","|__|","   |","|__|"," __|"
509
};
Hisham Muhammad's avatar
Hisham Muhammad committed
510

511
512
#ifdef HAVE_LIBNCURSESW

Christian Hesse's avatar
Christian Hesse committed
513
514
515
516
static const char* LEDMeterMode_digitsUtf8[] = {
   "┌──┐","  ┐ ","╶──┐","╶──┐","╷  ╷","┌──╴","┌──╴","╶──┐","┌──┐","┌──┐",
   "│  │","  │ ","┌──┘"," ──┤","└──┤","└──┐","├──┐","   │","├──┤","└──┤",
   "└──┘","  ╵ ","└──╴","╶──┘","   ╵","╶──┘","└──┘","   ╵","└──┘"," ──┘"
517
518
};

519
520
#endif

Christian Hesse's avatar
Christian Hesse committed
521
static const char** LEDMeterMode_digits;
Hisham Muhammad's avatar
Hisham Muhammad committed
522

523
static void LEDMeterMode_drawDigit(int x, int y, int n) {
Hisham Muhammad's avatar
Hisham Muhammad committed
524
   for (int i = 0; i < 3; i++)
Christian Hesse's avatar
Christian Hesse committed
525
      mvaddstr(y+i, x, LEDMeterMode_digits[i * 10 + n]);
526
527
}

528
static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
Hisham Muhammad's avatar
Hisham Muhammad committed
529
   (void) w;
Hisham Muhammad's avatar
Hisham Muhammad committed
530

531
532
#ifdef HAVE_LIBNCURSESW
   if (CRT_utf8)
Hisham Muhammad's avatar
Hisham Muhammad committed
533
      LEDMeterMode_digits = LEDMeterMode_digitsUtf8;
534
535
   else
#endif
Hisham Muhammad's avatar
Hisham Muhammad committed
536
537
      LEDMeterMode_digits = LEDMeterMode_digitsAscii;

538
   char buffer[METER_BUFFER_LEN];
539
   Meter_setValues(this, buffer, METER_BUFFER_LEN - 1);
540
541
542
   
   RichString_begin(out);
   Meter_displayBuffer(this, buffer, &out);
543

544
545
   int yText =
#ifdef HAVE_LIBNCURSESW
Hisham Muhammad's avatar
Hisham Muhammad committed
546
      CRT_utf8 ? y+1 :
547
#endif
Hisham Muhammad's avatar
Hisham Muhammad committed
548
      y+2;
Hisham Muhammad's avatar
Hisham Muhammad committed
549
   attrset(CRT_colors[LED_COLOR]);
550
   mvaddstr(yText, x, this->caption);
Hisham Muhammad's avatar
Hisham Muhammad committed
551
   int xx = x + strlen(this->caption);
552
553
554
   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
555
      if (c >= '0' && c <= '9') {
556
557
         LEDMeterMode_drawDigit(xx, y, c-48);
         xx += 4;
Hisham Muhammad's avatar
Hisham Muhammad committed
558
      } else {
559
         mvaddch(yText, xx, c);
Hisham Muhammad's avatar
Hisham Muhammad committed
560
561
562
563
         xx += 1;
      }
   }
   attrset(CRT_colors[RESET_COLOR]);
564
   RichString_end(out);
Hisham Muhammad's avatar
Hisham Muhammad committed
565
566
}

567
568
569
570
571
572
573
574
575
576
577
578
579
580
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",
581
   .h = GRAPH_HEIGHT,
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
   .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
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628

/* 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 = ""
};