Base: LCDproc 0.5.2
[lcdproc-de200c.git] / server / menuscreens.c
blob94cb07232640e1a2694c9a9b4020954a2d0a127f
1 /*
2 * menuscreens.c
3 * This file is part of LCDd, the lcdproc server.
5 * This file is released under the GNU General Public License. Refer to the
6 * COPYING file distributed with this package.
8 * Copyright (c) 1999, William Ferrell, Scott Scriven
9 * 2002, Joris Robijn
10 * 2004, F5 Networks, Inc. - IP-address input
11 * 2005, Peter Marschall - error checks, ...
14 * Creates the server menu screen(s) and creates the menus that should be
15 * displayed on this screen.
16 * It also handles its keypresses and converts them to menu tokens for
17 * easier processing.
19 * NOTE: menuscreens.c does not know whether a menuitem is displayed INSIDE
20 * a menu or on a separate SCREEN, for flexibility.
24 #include <string.h>
25 #include <unistd.h>
26 #include <assert.h>
28 #include "screen.h"
29 #include "screenlist.h"
30 #include "menuscreens.h"
31 #include "shared/configfile.h"
32 #include "shared/report.h"
33 #include "input.h"
34 #include "driver.h"
35 #include "drivers.h"
37 #ifdef HAVE_CONFIG_H
38 # include "config.h"
39 #endif
41 /* Next include files are needed for settings that we can modify */
42 #include "render.h"
44 char *menu_key;
45 char *enter_key;
46 char *up_key;
47 char *down_key;
48 char *left_key;
49 char *right_key;
51 Screen *menuscreen = NULL;
52 MenuItem *active_menuitem = NULL;
53 /** the "real" main_menu */
54 Menu *main_menu = NULL;
55 /** customizable entry point into the menu system (see menu_set_main()). */
56 Menu *custom_main_menu = NULL;
57 Menu *screens_menu = NULL;
59 /* Local prototypes */
60 static void handle_quit(void);
61 static void handle_close(void);
62 static void handle_none(void);
63 static void handle_enter(void);
64 static void handle_successor(void);
65 void menuscreen_switch_item(MenuItem *new_menuitem);
66 void menuscreen_create_menu(void);
67 Menu *menuscreen_get_main(void);
68 MenuEventFunc(heartbeat_handler);
69 MenuEventFunc(backlight_handler);
70 MenuEventFunc(contrast_handler);
71 MenuEventFunc(brightness_handler);
73 int menuscreens_init(void)
75 const char *tmp;
77 debug(RPT_DEBUG, "%s()", __FUNCTION__);
79 /* Get keys from config file */
80 menu_key = strdup(config_get_string("menu", "MenuKey", 0, "Menu"));
81 enter_key = strdup(config_get_string("menu", "EnterKey", 0, "Enter"));
82 up_key = strdup(config_get_string("menu", "UpKey", 0, "Up"));
83 down_key = strdup(config_get_string("menu", "DownKey", 0, "Down"));
85 /* if the user has specified in the conf file a left and right key */
86 left_key = right_key = NULL;
87 tmp = config_get_string("menu", "LeftKey", 0, NULL);
88 if (tmp)
89 left_key = strdup(tmp);
90 tmp = config_get_string("menu", "RightKey", 0, NULL);
91 if (tmp)
92 right_key = strdup(tmp);
95 /* Now reserve keys */
96 input_reserve_key(menu_key, true, NULL);
97 input_reserve_key(enter_key, false, NULL);
98 input_reserve_key(up_key, false, NULL);
99 input_reserve_key(down_key, false, NULL);
100 if (left_key)
101 input_reserve_key(left_key, false, NULL);
102 if (right_key)
103 input_reserve_key(right_key, false, NULL);
105 /* Create screen */
106 menuscreen = screen_create("_menu_screen", NULL);
107 if (menuscreen != NULL)
108 menuscreen->priority = PRI_HIDDEN;
109 active_menuitem = NULL;
111 screenlist_add(menuscreen);
113 /* Build menu */
114 menuscreen_create_menu();
116 return 0;
120 int menuscreens_shutdown(void)
122 debug(RPT_DEBUG, "%s()", __FUNCTION__);
124 /* Program shutdown before completed startup */
125 if (!menuscreen)
126 return -1;
128 /* Quit menu just to make sure */
129 menuscreen_switch_item(NULL);
131 /* Destroy the menuscreen */
132 screenlist_remove(menuscreen);
133 screen_destroy(menuscreen);
134 menuscreen = NULL;
136 /* Destroy all menus */
137 menuitem_destroy(main_menu);
138 main_menu = NULL;
139 custom_main_menu = NULL;
140 screens_menu = NULL;
142 /* Forget menu's key reservations */
143 input_release_client_keys(NULL);
145 free(menu_key);
146 free(enter_key);
147 free(up_key);
148 free(down_key);
149 if (left_key)
150 free(left_key);
151 if (right_key)
152 free(right_key);
154 return 0;
158 void menuscreen_inform_item_destruction(MenuItem *item)
160 MenuItem *i;
162 debug(RPT_DEBUG, "%s(item=[%s])", __FUNCTION__,
163 ((item != NULL) ? item->id : "(null)"));
165 /* Are we currently in (a subitem of) the given item ? */
166 for (i = active_menuitem; i != NULL; i = i->parent) {
167 if (i == item) {
168 menuscreen_switch_item(item->parent);
173 void menuscreen_inform_item_modified(MenuItem *item)
175 debug(RPT_DEBUG, "%s(item=[%s])", __FUNCTION__,
176 ((item != NULL) ? item->id : "(null)"));
178 if ((active_menuitem == NULL) || (item == NULL))
179 return;
181 /* Are we currently in the item or the parent of the item ? */
182 if (active_menuitem == item || active_menuitem == item->parent) {
183 menuitem_rebuild_screen(active_menuitem, menuscreen);
187 bool is_menu_key(const char *key)
189 if (menu_key && key && strcmp(key, menu_key) == 0)
190 return true;
191 else
192 return false;
195 /** This function changes the menuitem to the given one, and does necesary
196 * actions.
197 * To leave the menu system, specify NULL for new_menuitem.
198 * The item will not be reset when the new item is a child of the last one.
200 void menuscreen_switch_item(MenuItem *new_menuitem)
202 MenuItem *old_menuitem = active_menuitem;
204 debug(RPT_DEBUG, "%s(item=[%s]) from active_menuitem=[%s]", __FUNCTION__,
205 ((new_menuitem != NULL) ? new_menuitem->id : "(null)"),
206 ((old_menuitem != NULL) ? old_menuitem->id : "(null)"));
208 /* First we do the switch */
209 active_menuitem = new_menuitem;
211 /* What was the state change ? */
212 if (!old_menuitem && !new_menuitem) {
213 /* Nothing to be done */
214 } else if (old_menuitem && !new_menuitem) {
215 /* leave menu system */
216 menuscreen->priority = PRI_HIDDEN;
217 } else if (!old_menuitem && new_menuitem) {
218 /* Menu is becoming active */
219 menuitem_reset(active_menuitem);
220 menuitem_rebuild_screen(active_menuitem, menuscreen);
222 menuscreen->priority = PRI_INPUT;
223 } else {
224 /* We're left with the usual case: a menu level switch */
225 if (old_menuitem->parent != new_menuitem) {
226 menuitem_reset(new_menuitem);
228 menuitem_rebuild_screen(active_menuitem, menuscreen);
231 if (old_menuitem && old_menuitem->event_func)
232 old_menuitem->event_func(old_menuitem, MENUEVENT_LEAVE);
233 if (new_menuitem && new_menuitem->event_func)
234 new_menuitem->event_func(new_menuitem, MENUEVENT_ENTER);
236 return;
239 static void handle_quit(void)
241 debug(RPT_DEBUG, "%s: Closing menu screen", __FUNCTION__);
242 menuscreen_switch_item(NULL);
245 static void handle_close(void)
247 debug(RPT_DEBUG, "%s: Closing item", __FUNCTION__);
248 menuscreen_switch_item(
249 (active_menuitem == menuscreen_get_main())
250 ? NULL
251 : active_menuitem->parent);
254 static void handle_none(void)
256 debug(RPT_DEBUG, "%s: Staying in item", __FUNCTION__);
257 if (active_menuitem)
259 menuitem_update_screen(active_menuitem, menuscreen);
260 /* No rebuild needed, only value can be changed */
262 /* Nothing extra to be done */
265 /** Enter the selected menuitem
266 * Note: this is not for checkboxes etc that don't have their
267 * own screen. The menuitem_process_input function should do
268 * things like toggling checkboxes !
270 static void handle_enter(void)
272 debug(RPT_DEBUG, "%s: Entering subitem", __FUNCTION__);
273 menuscreen_switch_item(menu_get_current_item(active_menuitem));
276 static void handle_predecessor(void)
278 MenuItem* item = (active_menuitem->type == MENUITEM_MENU)
279 ? menu_get_item_for_predecessor_check(active_menuitem)
280 : active_menuitem;
281 assert(item != NULL);
282 debug(RPT_DEBUG, "%s: Switching to registered predecessor '%s' of '%s'.",
283 __FUNCTION__, item->predecessor_id, item->id);
284 MenuItem *predecessor = menuitem_search(
285 item->predecessor_id, (Client*)active_menuitem->client);
286 if (predecessor == NULL)
288 // note: if _quit_, _close_, _none_ get here this
289 // would be an implementation error - they should
290 // have been handled via different MENURESULT codes.
291 report(RPT_ERR, "%s: cannot find predecessor '%s' of '%s'.",
292 __FUNCTION__, item->predecessor_id, item->id);
293 return;
295 switch (predecessor->type) {
296 case MENUITEM_ACTION:
297 case MENUITEM_CHECKBOX:
298 case MENUITEM_RING:
299 if (active_menuitem != predecessor->parent)
300 menuscreen_switch_item(predecessor->parent);
301 // this won't work for hidden subitems
302 menu_select_subitem(active_menuitem, item->predecessor_id);
303 menuitem_update_screen(active_menuitem, menuscreen);
304 break;
305 default:
306 if (predecessor->parent != NULL
307 && predecessor->parent->type == MENUITEM_MENU)
309 // update parent menu too
310 menu_select_subitem(predecessor->parent, predecessor->id);
312 menuscreen_switch_item(predecessor);
313 break;
317 static void handle_successor(void)
319 MenuItem* item = (active_menuitem->type == MENUITEM_MENU)
320 ? menu_get_item_for_successor_check(active_menuitem)
321 : active_menuitem;
322 assert(item != NULL);
323 debug(RPT_DEBUG, "%s: Switching to registered successor '%s' of '%s'.",
324 __FUNCTION__, item->successor_id, item->id);
325 MenuItem *successor = menuitem_search(
326 item->successor_id, (Client*)active_menuitem->client);
327 if (successor == NULL)
329 // note: if _quit_, _close_, _none_ get here this
330 // would be an implementation error - they should
331 // have been handled via different MENURESULT codes.
332 report(RPT_ERR, "%s: cannot find successor '%s' of '%s'.",
333 __FUNCTION__, item->successor_id, item->id);
334 return;
336 switch (successor->type) {
337 case MENUITEM_ACTION:
338 case MENUITEM_CHECKBOX:
339 case MENUITEM_RING:
340 if (active_menuitem != successor->parent)
341 menuscreen_switch_item(successor->parent);
342 // this won't work for hidden subitems
343 menu_select_subitem(active_menuitem, item->successor_id);
344 menuitem_update_screen(active_menuitem, menuscreen);
345 break;
346 default:
347 if (successor->parent != NULL
348 && successor->parent->type == MENUITEM_MENU)
350 // update parent menu too
351 menu_select_subitem(successor->parent, successor->id);
353 menuscreen_switch_item(successor);
354 break;
358 void menuscreen_key_handler(const char *key)
360 char token = 0;
361 MenuResult res;
363 debug(RPT_DEBUG, "%s(\"%s\")", __FUNCTION__, key);
365 if (strcmp(key, menu_key) == 0) {
366 token = MENUTOKEN_MENU;
368 else if (strcmp(key, enter_key) == 0) {
369 token = MENUTOKEN_ENTER;
371 else if (strcmp(key, up_key) == 0) {
372 token = MENUTOKEN_UP;
374 else if (strcmp(key, down_key) == 0) {
375 token = MENUTOKEN_DOWN;
377 else if (left_key && strcmp(key, left_key) == 0) {
378 token = MENUTOKEN_LEFT;
380 else if (right_key && strcmp(key, right_key) == 0) {
381 token = MENUTOKEN_RIGHT;
383 else {
384 token = MENUTOKEN_OTHER;
387 /* Is the menu already active ? */
388 if (!active_menuitem) {
389 debug(RPT_DEBUG, "%s: Activating menu screen", __FUNCTION__);
390 menuscreen_switch_item(menuscreen_get_main());
391 return;
394 res = menuitem_process_input(active_menuitem, token, key,
395 ((left_key || right_key) ? 1 : 0));
397 switch (res) {
398 case MENURESULT_ERROR:
399 report(RPT_ERR, "%s: Error from menuitem_process_input", __FUNCTION__);
400 break;
401 case MENURESULT_NONE:
402 handle_none();
403 break;
404 case MENURESULT_ENTER:
405 handle_enter();
406 break;
407 case MENURESULT_CLOSE:
408 handle_close();
409 break;
410 case MENURESULT_QUIT:
411 handle_quit();
412 break;
413 case MENURESULT_PREDECESSOR:
414 handle_predecessor();
415 break;
416 case MENURESULT_SUCCESSOR:
417 handle_successor();
418 break;
419 default:
420 assert(!"unexpected menuresult");
421 break;
425 void menuscreen_create_menu(void)
427 Menu *options_menu;
428 Menu *driver_menu;
429 MenuItem *checkbox;
430 MenuItem *slider;
431 Driver *driver;
433 #ifdef LCDPROC_TESTMENUS
434 MenuItem *test_item;
435 Menu *test_menu;
436 #endif /*LCDPROC_TESTMENUS*/
438 debug(RPT_DEBUG, "%s()", __FUNCTION__);
440 main_menu = menu_create("mainmenu", NULL, "LCDproc Menu", NULL);
442 options_menu = menu_create("options", NULL, "Options", NULL);
443 menu_add_item(main_menu, options_menu);
445 #ifdef LCDPROC_TESTMENUS
446 screens_menu = menu_create("screens", NULL, "Screens", NULL);
447 menu_add_item(main_menu, screens_menu);
448 #endif /*LCDPROC_TESTMENUS*/
450 /* menu's client is NULL since we're in the server */
451 checkbox = menuitem_create_checkbox("heartbeat", heartbeat_handler, "Heartbeat", NULL, true, heartbeat);
452 menu_add_item(options_menu, checkbox);
454 /* menu's client is NULL since we're in the server */
455 checkbox = menuitem_create_checkbox("backlight", backlight_handler, "Backlight", NULL, true, backlight);
456 menu_add_item(options_menu, checkbox);
458 for (driver = drivers_getfirst(); driver; driver = drivers_getnext()) {
459 int contrast_avail = (driver->get_contrast && driver->set_contrast) ? 1 : 0;
460 int brightness_avail = (driver->get_brightness && driver->set_brightness) ? 1 : 0;
462 if (contrast_avail || brightness_avail) {
463 /* menu's client is NULL since we're in the server */
464 driver_menu = menu_create(driver->name, NULL, driver->name, NULL);
465 menu_set_association(driver_menu, driver);
466 menu_add_item(options_menu, driver_menu);
467 if (contrast_avail) {
468 int contrast = driver->get_contrast(driver);
470 /* menu's client is NULL since we're in the server */
471 slider = menuitem_create_slider("contrast", contrast_handler, "Contrast",
472 NULL, "min", "max", 0, 1000, 25, contrast);
473 menu_add_item(driver_menu, slider);
475 if (brightness_avail) {
476 int onbrightness = driver->get_brightness(driver, BACKLIGHT_ON);
477 int offbrightness = driver->get_brightness(driver, BACKLIGHT_OFF);
479 slider = menuitem_create_slider("onbrightness", brightness_handler, "On Brightness",
480 NULL, "min", "max", 0, 1000, 25, onbrightness);
481 menu_add_item(driver_menu, slider);
483 slider = menuitem_create_slider("offbrightness", brightness_handler, "Off Brightness",
484 NULL, "min", "max", 0, 1000, 25, offbrightness);
485 menu_add_item(driver_menu, slider);
490 #ifdef LCDPROC_TESTMENUS
491 test_menu = menu_create("test", NULL, "Test menu", NULL);
492 menu_add_item(main_menu, test_menu);
494 /* menu's client is NULL since we're in the server */
495 test_item = menuitem_create_action("", NULL, "Action", NULL, MENURESULT_NONE);
496 menu_add_item(test_menu, test_item);
497 test_item = menuitem_create_action("", NULL, "Action,closing", NULL, MENURESULT_CLOSE);
498 menu_add_item(test_menu, test_item);
499 test_item = menuitem_create_action("", NULL, "Action,quitting", NULL, MENURESULT_QUIT);
500 menu_add_item(test_menu, test_item);
502 test_item = menuitem_create_checkbox("", NULL, "Checkbox", NULL, false, false);
503 menu_add_item(test_menu, test_item);
504 test_item = menuitem_create_checkbox("", NULL, "Checkbox, gray", NULL, true, false);
505 menu_add_item(test_menu, test_item);
507 test_item = menuitem_create_ring("", NULL, "Ring", NULL, "ABC\tDEF\t01234567890\tOr a very long string that will not fit on any display", 1);
508 menu_add_item(test_menu, test_item);
510 test_item = menuitem_create_slider("", NULL, "Slider", NULL, "mintext", "maxtext", -20, 20, 1, 0);
511 menu_add_item(test_menu, test_item);
512 test_item = menuitem_create_slider("", NULL, "Slider,step=5", NULL, "mintext", "maxtext", -20, 20, 5, 0);
513 menu_add_item(test_menu, test_item);
515 test_item = menuitem_create_numeric("", NULL, "Numeric", NULL, 1, 365, 15);
516 menu_add_item(test_menu, test_item);
517 test_item = menuitem_create_numeric("", NULL, "Numeric,signed", NULL, -20, +20, 15);
518 menu_add_item(test_menu, test_item);
520 test_item = menuitem_create_alpha("", NULL, "Alpha", NULL, 0, 3, 12, true, true, true, ".-+@", "LCDproc-v0.5");
521 menu_add_item(test_menu, test_item);
522 test_item = menuitem_create_alpha("", NULL, "Alpha, caps only", NULL, 0, 3, 12, true, false, false, "-", "LCDPROC");
523 menu_add_item(test_menu, test_item);
525 test_item = menuitem_create_ip("", NULL, "IPv4", NULL, 0, "192.168.1.245");
526 menu_add_item(test_menu, test_item);
527 test_item = menuitem_create_ip("", NULL, "IPv6", NULL, 1, "1080:0:0:0:8:800:200C:417A");
528 menu_add_item(test_menu, test_item);
529 #endif /*LCDPROC_TESTMENUS*/
532 MenuEventFunc (heartbeat_handler)
534 debug(RPT_DEBUG, "%s(item=[%s], event=%d)", __FUNCTION__,
535 ((item != NULL) ? item->id : "(null)"), event);
537 if (event == MENUEVENT_UPDATE) {
538 /* Set heartbeat setting */
539 heartbeat = item->data.checkbox.value;
540 report(RPT_INFO, "Menu: set heartbeat to %d",
541 item->data.checkbox.value);
543 return 0;
546 MenuEventFunc (backlight_handler)
548 debug(RPT_DEBUG, "%s(item=[%s], event=%d)", __FUNCTION__,
549 ((item != NULL) ? item->id : "(null)"), event);
551 if (event == MENUEVENT_UPDATE)
553 /* Set backlight setting */
554 backlight = item->data.checkbox.value;
555 report(RPT_INFO, "Menu: set backlight to %d",
556 item->data.checkbox.value);
558 return 0;
561 MenuEventFunc (contrast_handler)
563 debug(RPT_DEBUG, "%s(item=[%s], event=%d)", __FUNCTION__,
564 ((item != NULL) ? item->id : "(null)"), event);
566 /* This function can be called by one of several drivers that
567 * support contrast !
568 * We need to check the menu association to see which driver. */
569 if (event == MENUEVENT_MINUS || event == MENUEVENT_PLUS) {
570 /* Determine the driver */
571 Driver *driver = item->parent->data.menu.association;
573 if (driver != NULL) {
574 driver->set_contrast(driver, item->data.slider.value);
575 report(RPT_INFO, "Menu: set contrast of [%.40s] to %d",
576 driver->name, item->data.slider.value);
579 return 0;
582 MenuEventFunc (brightness_handler)
584 debug(RPT_DEBUG, "%s(item=[%s], event=%d)", __FUNCTION__,
585 ((item != NULL) ? item->id : "(null)"), event);
587 /* This function can be called by one of several drivers that
588 * support brightness !
589 * We need to check the menu association to see which driver. */
590 if (event == MENUEVENT_MINUS || event == MENUEVENT_PLUS) {
591 /* Determine the driver */
592 Driver *driver = item->parent->data.menu.association;
594 if (driver != NULL) {
595 if (strcmp(item->id, "onbrightness") == 0) {
596 driver->set_brightness(driver, BACKLIGHT_ON, item->data.slider.value);
598 else if (strcmp(item->id, "offbrightness") == 0) {
599 driver->set_brightness(driver, BACKLIGHT_OFF, item->data.slider.value);
603 return 0;
606 void
607 menuscreen_add_screen(Screen *s)
609 Menu *m;
610 MenuItem *mi;
612 debug(RPT_DEBUG, "%s(s=[%s])", __FUNCTION__,
613 ((s != NULL) ? s->id : "(null)"));
615 /* screens have not been created or no screen given ... */
616 if ((screens_menu == NULL) || (s == NULL))
617 return;
619 /* Create a menu entry for the screen */
620 m = menu_create(s->id, NULL, ((s->name != NULL) ? s->name : s->id), s->client);
621 menu_set_association(m, s);
622 menu_add_item(screens_menu, m);
624 /* And add some items for it... */
625 mi = menuitem_create_action("", NULL, "(don't work yet)", s->client, MENURESULT_NONE);
626 menu_add_item(m, mi);
628 mi = menuitem_create_action("", NULL, "To Front", s->client, MENURESULT_QUIT);
629 menu_add_item(m, mi);
631 mi = menuitem_create_checkbox("", NULL, "Visible", s->client, false, true);
632 menu_add_item(m, mi);
634 mi = menuitem_create_numeric("", NULL, "Duration", s->client, 2, 3600, s->duration);
635 menu_add_item(m, mi);
637 mi = menuitem_create_ring("", NULL, "Priority", s->client,
638 "Hidden\tBackground\tForeground\tAlert\tInput", s->priority);
639 menu_add_item(m, mi);
643 void
644 menuscreen_remove_screen(Screen *s)
646 debug(RPT_DEBUG, "%s(s=[%s])", __FUNCTION__,
647 (s != NULL) ? s->id : "(NULL)");
649 /* allow to remove the menuscreen itself */
650 if ((s == NULL) || (s == menuscreen))
651 return;
653 if (screens_menu) {
654 Menu *m = menu_find_item(screens_menu, s->id, false);
656 menu_remove_item(screens_menu, m);
657 menuitem_destroy(m);
662 menuscreen_goto(Menu *menu)
664 debug(RPT_DEBUG, "%s(m=[%s]): active_menuitem=[%s]",
665 __FUNCTION__, (menu != NULL) ? menu->id : "(NULL)",
666 (active_menuitem != NULL) ? active_menuitem->id : "(NULL)");
667 menuscreen_switch_item(menu);
668 return 0;
671 /** sets custom main menu. Use NULL pointer to reset it to the "real" main
672 * menu. */
674 menuscreen_set_main(Menu *menu)
676 debug(RPT_DEBUG, "%s(m=[%s])",
677 __FUNCTION__, (menu != NULL) ? menu->id : "(NULL)");
678 custom_main_menu = menu;
679 return 0;
682 Menu *
683 menuscreen_get_main(void)
685 return custom_main_menu ? custom_main_menu : main_menu;