-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathdisplay-menu.c
346 lines (295 loc) · 7.66 KB
/
display-menu.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
#include <stdlib.h>
#include <string.h>
#include "display-menu.h"
#include "display.h"
#include "mcp.h"
#include "utils.h"
#define XPOS_BITS (WIDTH / 2) - 4
#define YPOS_BITS 3
#define XPOS_VALUE (WIDTH / 2) - 2
#define YPOS_VALUE 3
static menu_t *menu;
static int current;
static int xpos;
// toggle bit on current position
static void current_bit_toogle() {
int mask = 1 << xpos;
if (current & mask)
current &= ~mask; // clear
else
current |= mask; // set
}
static void menu_get_selected() {
const ITEM *citem = current_item(menu->cmenu);
const menuitem_t *item = item_userptr(citem);
const menuconfig_t *config = menu->config;
current = (config->getfunc)(config, item);
}
static void menu_set_selected() {
const ITEM *citem = current_item(menu->cmenu);
const menuitem_t *item = item_userptr(citem);
const menuconfig_t *config = menu->config;
(config->setfunc)(config, item, current);
}
static void menu_down() {
// we have items, let menu driver do it's work
if (menu->items) {
menu_driver(menu->cmenu, REQ_DOWN_ITEM);
return;
}
switch (menu->config->style) {
case bits:
wchgat(menu->cwindow, 1, A_NORMAL, YELLOWONBLUE, NULL);
xpos++;
if (xpos > 8) {
// position 8 is outside and means "go back"
xpos = 8;
}
return;
case value:
// check and store decremented value
if (current > menu->config->min) {
--current;
menu_set_selected();
menu_get_selected();
}
return;
default:
return;
}
}
static void menu_up() {
// we have items, let menu driver do it's work
if (menu->items) {
menu_driver(menu->cmenu, REQ_UP_ITEM);
return;
}
switch (menu->config->style) {
case bits:
wchgat(menu->cwindow, 1, A_NORMAL, YELLOWONBLUE, NULL);
xpos--;
if (xpos < -1) {
// position -1 is outside and means "go back"
xpos = -1;
}
return;
case value:
// check and store incremented value
if (current < menu->config->max) {
++current;
menu_set_selected();
menu_get_selected();
}
return;
default:
return;
}
}
static void menu_select() {
ITEM *citem = current_item(menu->cmenu);
menuitem_t *item = item_userptr(citem);
// this is the back item -> go to parent menu or exit
if (!item) {
if (!menu->back) {
mcp->menu = 0;
xlog("leaving menu mode");
return;
} else {
return menu_open(menu->back);
}
}
// item has sub menu -> open sub menu
if (item->submenu)
return menu_open(item->submenu);
// determine action depending on menuconfig's style
if (menu->config) {
switch (menu->config->style) {
case bits:
xlog("menu_select bit %i", xpos);
if (xpos < 0 || xpos > 7) {
// position outside - go back
return menu_open(menu->back);
} else {
// toggle bit on current position
current_bit_toogle();
// write new value with config's setter function and read back
menu_set_selected();
menu_get_selected();
}
return;
case value:
// direct value input by up/down - go back
xlog("menu_select done, going back");
return menu_open(menu->back);
case selection:
// write selected value with config's setter function and read back
current = item->index;
menu_set_selected();
menu_get_selected();
return;
case onoff:
// toggle value, write with config's setter function and read back
menu_get_selected();
if (current)
current = 0;
else
current = 1;
menu_set_selected();
return;
default:
break;
}
}
// we do not have menuconfig, execute void/integer functions if available
if (item->vfunc) {
mcp->menu = 0;
xlog("executing void function for %s", item->name);
(*item->vfunc)();
return;
}
if (item->ifunc) {
mcp->menu = 0;
xlog("executing integer function for %s with %d", item->name, item->index);
(*item->ifunc)(item->index);
return;
}
}
static void menu_paint() {
if (menu->config) {
switch (menu->config->style) {
case bits:
menu_get_selected();
// bitwise input > print current value as bits
char *out = printbits(current);
mvwprintw(menu->cwindow, 3, XPOS_BITS, "%s", out);
wmove(menu->cwindow, YPOS_BITS, XPOS_BITS + 7 - xpos);
if (xpos >= 0 && xpos <= 7)
// highlight position if cursor is inside
wchgat(menu->cwindow, 1, A_REVERSE | A_BOLD, 0, NULL);
free(out);
break;
case value:
menu_get_selected();
// direct input -> print current value as integer
mvwprintw(menu->cwindow, YPOS_VALUE, XPOS_VALUE, "%03d", current);
break;
case selection:
menu_get_selected();
// we have items and configuration -> mark current as not selectable
for (ITEM **citem = menu->cmenu->items; *citem; citem++) {
const menuitem_t *item = item_userptr(*citem);
if (item && item->index == current)
item_opts_off(*citem, O_SELECTABLE);
else
item_opts_on(*citem, O_SELECTABLE);
}
break;
case onoff:
// read status for each item
for (ITEM **citem = menu->cmenu->items; *citem; citem++) {
const menuitem_t *item = item_userptr(*citem);
const menuconfig_t *config = menu->config;
if (item) {
int state = (config->getfunc)(config, item);
mvwprintw(menu->cwindow, (*citem)->index + 1, WIDTH - 3, "%1d", state);
}
}
break;
default:
break;
}
}
redrawwin(menu->cwindow);
wrefresh(menu->cwindow);
}
void menu_create(menu_t *menu, menu_t *parent) {
menu->back = parent;
// create a window for the menu
WINDOW *cwindow = newwin(HEIGHT, WIDTH, 0, 0);
menu->cwindow = cwindow;
wbkgd(cwindow, COLOR_PAIR(YELLOWONBLUE) | A_BOLD);
wborder(cwindow, 0, 0, 0, 0, 0, 0, 0, 0);
// set window title
int center_pos = (int) (WIDTH / 2) - (strlen(menu->title) / 2);
mvwprintw(cwindow, 0, center_pos - 2, " %s ", menu->title);
if (!menu->items) {
// TODO window is too small & overwriting borders
// mvwprintw(cwindow, 3, 1, menu->descr);
return;
}
// we have items then build the curses menu
int length = menu->size;
xlog("creating '%s' with %d entries", menu->title, length);
ITEM **citems = malloc((length + 2) * sizeof(citems[0])); // +back +NULL
if (citems == NULL) {
xlog("not enough memory");
exit(EXIT_FAILURE);
}
// make menu items
const menuitem_t *items = menu->items;
for (int i = 0; i < length; ++i) {
citems[i] = new_item(items[i].name, NULL);
set_item_userptr(citems[i], (void*) &items[i]); // set to menu definition
}
// back item with empty item_userptr
if (!parent)
citems[length] = new_item("Exit", NULL);
else
citems[length] = new_item("Back", NULL);
set_item_userptr(citems[length], NULL);
// NULL terminated list
citems[length + 1] = NULL;
// create the menu
MENU *cmenu = new_menu(citems);
menu->cmenu = cmenu;
set_menu_format(cmenu, HEIGHT - 2, 1); // 5 rows, 1 column
set_menu_mark(cmenu, NULL);
set_menu_fore(cmenu, COLOR_PAIR(REDONWHITE) | A_BOLD | A_REVERSE);
set_menu_back(cmenu, COLOR_PAIR(YELLOWONBLUE) | A_BOLD);
set_menu_grey(cmenu, COLOR_PAIR(CYANONBLUE));
set_menu_mark(cmenu, "*");
// use full width height (without box and one space left/right)
set_menu_win(cmenu, cwindow);
set_menu_sub(cmenu, derwin(cwindow, HEIGHT - 2, WIDTH - 4, 1, 2));
post_menu(cmenu);
}
void menu_open(menu_t *m) {
menu = m;
xpos = 0;
current = 0;
// update screen
xlog("painting '%s'", menu->title);
menu_paint();
}
// !!! DO NOT use key names from linux/input.h - this breaks curses.h !!!
void menu_handle(int c) {
if (!menu)
return;
switch (c) {
case 0x42: // down
case 115: // KEY_VOLUMEUP
case KEY_DOWN:
menu_down();
break;
case 0x41: // up
case 114: // KEY_VOLUMEDOWN
case KEY_UP:
menu_up();
break;
case 207: // KEY_PLAY
case 99: // KEY_SYSRQ
case 0x0d:
case '\n':
menu_select();
break;
case 59: // KEY_F1
case 128: // KEY_STOP
case 'q':
mcp->menu = 0;
xlog("leaving menu mode");
break;
}
// update screen
if (mcp->menu)
menu_paint();
}