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
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
19 * NOTE: menuscreens.c does not know whether a menuitem is displayed INSIDE
20 * a menu or on a separate SCREEN, for flexibility.
29 #include "screenlist.h"
30 #include "menuscreens.h"
31 #include "shared/configfile.h"
32 #include "shared/report.h"
41 /* Next include files are needed for settings that we can modify */
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)
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
);
89 left_key
= strdup(tmp
);
90 tmp
= config_get_string("menu", "RightKey", 0, NULL
);
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
);
101 input_reserve_key(left_key
, false, NULL
);
103 input_reserve_key(right_key
, false, NULL
);
106 menuscreen
= screen_create("_menu_screen", NULL
);
107 if (menuscreen
!= NULL
)
108 menuscreen
->priority
= PRI_HIDDEN
;
109 active_menuitem
= NULL
;
111 screenlist_add(menuscreen
);
114 menuscreen_create_menu();
120 int menuscreens_shutdown(void)
122 debug(RPT_DEBUG
, "%s()", __FUNCTION__
);
124 /* Program shutdown before completed startup */
128 /* Quit menu just to make sure */
129 menuscreen_switch_item(NULL
);
131 /* Destroy the menuscreen */
132 screenlist_remove(menuscreen
);
133 screen_destroy(menuscreen
);
136 /* Destroy all menus */
137 menuitem_destroy(main_menu
);
139 custom_main_menu
= NULL
;
142 /* Forget menu's key reservations */
143 input_release_client_keys(NULL
);
158 void menuscreen_inform_item_destruction(MenuItem
*item
)
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
) {
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
))
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)
195 /** This function changes the menuitem to the given one, and does necesary
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
;
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
);
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())
251 : active_menuitem
->parent
);
254 static void handle_none(void)
256 debug(RPT_DEBUG
, "%s: Staying in item", __FUNCTION__
);
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
)
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
);
295 switch (predecessor
->type
) {
296 case MENUITEM_ACTION
:
297 case MENUITEM_CHECKBOX
:
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
);
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
);
317 static void handle_successor(void)
319 MenuItem
* item
= (active_menuitem
->type
== MENUITEM_MENU
)
320 ? menu_get_item_for_successor_check(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
);
336 switch (successor
->type
) {
337 case MENUITEM_ACTION
:
338 case MENUITEM_CHECKBOX
:
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
);
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
);
358 void menuscreen_key_handler(const char *key
)
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
;
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());
394 res
= menuitem_process_input(active_menuitem
, token
, key
,
395 ((left_key
|| right_key
) ? 1 : 0));
398 case MENURESULT_ERROR
:
399 report(RPT_ERR
, "%s: Error from menuitem_process_input", __FUNCTION__
);
401 case MENURESULT_NONE
:
404 case MENURESULT_ENTER
:
407 case MENURESULT_CLOSE
:
410 case MENURESULT_QUIT
:
413 case MENURESULT_PREDECESSOR
:
414 handle_predecessor();
416 case MENURESULT_SUCCESSOR
:
420 assert(!"unexpected menuresult");
425 void menuscreen_create_menu(void)
433 #ifdef LCDPROC_TESTMENUS
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
);
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
);
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
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
);
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
);
607 menuscreen_add_screen(Screen
*s
)
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
))
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
);
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
))
654 Menu
*m
= menu_find_item(screens_menu
, s
->id
, false);
656 menu_remove_item(screens_menu
, 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
);
671 /** sets custom main menu. Use NULL pointer to reset it to the "real" main
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
;
683 menuscreen_get_main(void)
685 return custom_main_menu
? custom_main_menu
: main_menu
;