diff --git a/LCDd.conf b/LCDd.conf index f084ec7..f8e7e01 100644 --- a/LCDd.conf +++ b/LCDd.conf @@ -83,6 +83,14 @@ DriverPath=server/drivers/ # Always place a slash as last character ! +[menu] +# The menu section. You can configure what keys the menu should use. +# Note that only the menukey will be reserved exclusively. +MenuKey=Escape +EnterKey=Enter +UpKey=Up +DownKey=Down + # Driver sections are below this line, in alphabetical order @@ -194,8 +202,8 @@ ConnectionType=4bit # Select what type of connection. See documentation for types. Keypad=no -# If you have a keypad connected. Keypad layout is currently not -# configureable from the config file. +# If you have a keypad connected. +# You may also need to configure the keypad layout further on in this file. Backlight=no # If you have a switchable backlight. @@ -222,6 +230,17 @@ DelayBus=true # On fast PCs it is possible your LCD does not respond correctly. # Default: true. +# If you have a keypad you can assign keystrings to the keys. +# See documentation for used terms and how to wire it. +# For example to give directly connected key 4 the string "Enter", use: +# KeyDirect_4=Enter +# For matrix keys use the X and Y coordinates of the key: +# KeyMatrix_1_3=Enter + +KeyMatrix_4_1=Enter +KeyMatrix_4_2=Up +KeyMatrix_4_3=Down +KeyMatrix_4_4=Escape [IrMan] diff --git a/server/Makefile.am b/server/Makefile.am index dc02bbb..16f80c1 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -1,5 +1,5 @@ SUBDIRS=drivers commands sbin_PROGRAMS=LCDd -LCDd_SOURCES= client.c client.h clients.c clients.h client_menu.h input.c input.h main.c main.h menu.c menu.h menuscreens.c menuscreens.h parse.c parse.h render.c render.h screen.c screen.h screenlist.c screenlist.h serverscreens.c serverscreens.h sock.c sock.h widget.c widget.h configfile.c configfile.h drivers.c drivers.h driver.c driver.h +LCDd_SOURCES= client.c client.h clients.c clients.h input.c input.h main.c main.h menuitem.c menuitem.h menu.c menu.h menuscreens.c menuscreens.h parse.c parse.h render.c render.h screen.c screen.h screenlist.c screenlist.h serverscreens.c serverscreens.h sock.c sock.h widget.c widget.h configfile.c configfile.h drivers.c drivers.h driver.c driver.h LCDd_LDADD = ../shared/libLCDstuff.a commands/libLCDcommands.a INCLUDES = -I$(top_srcdir) diff --git a/server/client.c b/server/client.c index 9d608b1..751de7b 100644 --- a/server/client.c +++ b/server/client.c @@ -1,5 +1,5 @@ /* - * client_data.h + * client.c * This file is part of LCDd, the lcdproc server. * * This file is released under the GNU General Public License. Refer to the @@ -260,4 +260,4 @@ client_remove_screen (Client * c, Screen * s) int client_screen_count (Client * c) { return LL_Length(c->screenlist); -} \ No newline at end of file +} diff --git a/server/clients.c b/server/clients.c index e80c01e..0fb9475 100644 --- a/server/clients.c +++ b/server/clients.c @@ -20,7 +20,6 @@ #include #include -#include "sock.h" #include "client.h" #include "clients.h" #include "shared/report.h" diff --git a/server/driver.c b/server/driver.c index 46ae8c6..4f09d81 100644 --- a/server/driver.c +++ b/server/driver.c @@ -312,7 +312,7 @@ driver_alt_vbar( Driver * drv, int x, int y, int len, int promille, int pattern { int pos; - debug (RPT_DEBUG, "%s( drv=[%.40s], x=%d, y=%d, len=%d, promille=%d, pattern=%d", __FILE__, drv->name, x, y, len, promille, pattern); + debug (RPT_DEBUG, "%s( drv=[%.40s], x=%d, y=%d, len=%d, promille=%d, pattern=%d )", __FUNCTION__, drv->name, x, y, len, promille, pattern); if (!drv->chr) return; @@ -330,7 +330,7 @@ driver_alt_hbar( Driver * drv, int x, int y, int len, int promille, int pattern { int pos; - debug (RPT_DEBUG, "%s( drv=[%.40s], x=%d, y=%d, len=%d, promille=%d, pattern=%d", __FILE__, drv->name, x, y, len, promille, pattern); + debug (RPT_DEBUG, "%s( drv=[%.40s], x=%d, y=%d, len=%d, promille=%d, pattern=%d )", __FUNCTION__, drv->name, x, y, len, promille, pattern); if (!drv->chr) return; @@ -406,7 +406,7 @@ driver_alt_num( Driver * drv, int x, int num ) int y, dx; - debug (RPT_DEBUG, "%s( drv=[%.40s], x=%d, num=%d", __FILE__, drv->name, x, num); + debug (RPT_DEBUG, "%s( drv=[%.40s], x=%d, num=%d )", __FUNCTION__, drv->name, x, num); if (!drv->chr) return; @@ -421,7 +421,7 @@ driver_alt_heartbeat( Driver * drv, int state ) { int icon; - debug (RPT_DEBUG, "%s( drv=[%.40s], state=%d", __FILE__, drv->name, state); + debug (RPT_DEBUG, "%s( drv=[%.40s], state=%d )", __FUNCTION__, drv->name, state); if (state == HEARTBEAT_OFF) return; @@ -447,7 +447,7 @@ driver_alt_icon( Driver * drv, int x, int y, int icon ) char ch1 = '?'; char ch2 = 0; - debug (RPT_DEBUG, "%s( drv=[%.40s], x=%d, y=%d, icon=ICON_%s", __FILE__, drv->name, x, y, widget_icon_to_iconname (icon) ); + debug (RPT_DEBUG, "%s( drv=[%.40s], x=%d, y=%d, icon=ICON_%s )", __FUNCTION__, drv->name, x, y, widget_icon_to_iconname (icon) ); if (!drv->chr) return; @@ -485,17 +485,23 @@ void driver_alt_cursor( Driver * drv, int x, int y, int state ) { /* Same question about timer in this function... */ - debug (RPT_DEBUG, "%s( drv=[%.40s], x=%d, y=%d, state=%d", __FILE__, drv->name, x, y, state); + debug (RPT_DEBUG, "%s( drv=[%.40s], x=%d, y=%d, state=%d )", __FUNCTION__, drv->name, x, y, state); switch( state ) { case CURSOR_BLOCK: case CURSOR_DEFAULT_ON: - if (timer & 2) - driver_alt_icon( drv, x, y, ICON_BLOCK_FILLED ); + if (timer & 2 && drv->chr) { + if (drv->icon) { + drv->icon( drv, x, y, ICON_BLOCK_FILLED); + } else { + driver_alt_icon( drv, x, y, ICON_BLOCK_FILLED ); + } + } break; case CURSOR_UNDER: - if (timer & 2 && drv->chr) + if (timer & 2 && drv->chr) { drv->chr( drv, x, y, '_'); + } break; } diff --git a/server/driver.h b/server/driver.h index c7bf7c6..2b9b331 100644 --- a/server/driver.h +++ b/server/driver.h @@ -14,8 +14,11 @@ #include "drivers/lcd.h" -#define bool int - +#ifndef bool +# define bool short +# define true 1 +# define false 0 +#endif Driver * driver_load( char * name, char * filename, char * args ); diff --git a/server/drivers.h b/server/drivers.h index 8b66d8e..bd7bbee 100644 --- a/server/drivers.h +++ b/server/drivers.h @@ -13,6 +13,7 @@ #define DRIVERS_H #include "drivers/lcd.h" +#include "shared/LL.h" typedef struct DisplayProps { int width, height; @@ -21,7 +22,11 @@ typedef struct DisplayProps { extern DisplayProps * display_props; -#define bool int +#ifndef bool +# define bool short +# define true 1 +# define false 0 +#endif int @@ -84,6 +89,9 @@ drivers_get_contrast(); void drivers_set_contrast( int contrast ); +void +drivers_cursor( int x, int y, int state ); + void drivers_backlight( int brightness ); @@ -96,4 +104,17 @@ drivers_get_key(); char drivers_getkey(); +extern LinkedList * loaded_drivers; +/* Please don't read this list except using the following functions */ + +static inline Driver * drivers_getfirst () +{ + return LL_GetFirst(loaded_drivers); +} + +static inline Driver * drivers_getnext () +{ + return LL_GetNext(loaded_drivers); +} + #endif diff --git a/server/drivers/MtxOrb.c b/server/drivers/MtxOrb.c index 494e4e7..1fb5a39 100644 --- a/server/drivers/MtxOrb.c +++ b/server/drivers/MtxOrb.c @@ -1190,12 +1190,11 @@ MtxOrb_set_char (Driver *drvthis, int n, char *dat) MODULE_EXPORT int MtxOrb_icon (Driver *drvthis, int x, int y, int icon) { + return -1; + /* Does not use current icon definitions */ + MtxOrb_chr (drvthis, x, y, MtxOrb_ask_bar (drvthis, icon)); - return 0; - /* We return 0, however many icons are not supported - * TODO... - */ } /* TODO: Recover the code for I2C connectivity to MtxOrb diff --git a/server/drivers/curses_drv.c b/server/drivers/curses_drv.c index d4fad5f..11f7e68 100644 --- a/server/drivers/curses_drv.c +++ b/server/drivers/curses_drv.c @@ -600,16 +600,16 @@ curses_drv_get_key (Driver *drvthis) return 0; break; case KEY_LEFT: - return "ArrowLeft"; + return "Left"; break; case KEY_UP: - return "ArrowUp"; + return "Up"; break; case KEY_DOWN: - return "ArrowDown"; + return "Down"; break; case KEY_RIGHT: - return "ArrowRight"; + return "Right"; break; case ERR: return NULL; diff --git a/server/drivers/lcd.h b/server/drivers/lcd.h index 62f1b90..8a7736d 100644 --- a/server/drivers/lcd.h +++ b/server/drivers/lcd.h @@ -43,8 +43,6 @@ /* Backlight data */ #define BACKLIGHT_OFF 0 #define BACKLIGHT_ON 1 -#define BACKLIGHT_WARNING 2 -#define BACKLIGHT_RED_ALERT 3 /* Icons. If a driver does not support an icon, it can return -1 from the * icon function, and let the core place a replacement character. diff --git a/server/input.c b/server/input.c index 99ef92b..b863a95 100644 --- a/server/input.c +++ b/server/input.c @@ -11,27 +11,6 @@ * Handles keypad (and other?) input from the user. */ -/* - - Currently, the keys are as follows: - - Context Key Function - ------- --- -------- - Normal "normal" context is handled in this source file. - A Pause/Continue - B Back(Go to previous screen) - C Forward(Go to next screen) - D Open main menu - E-Z Sent to client, if any; ignored otherwise - - (menu keys are not handled here, but in the menu code) - Menu - A Enter/select - B Up/Left - C Down/Right - D Exit/Cancel - E-Z Ignored -*/ #include @@ -44,98 +23,95 @@ #include "drivers.h" #include "client.h" -#include "clients.h" -#include "screen.h" -#include "widget.h" #include "screenlist.h" -//#include "menus.h" +#include "menuscreens.h" #include "input.h" -#define KeyWanted(a,b) ((a) && strchr((a), (b))) -#define CurrentScreen screenlist_current - int server_input (int key); +void input_send_to_client (Client * c, char * key); +void input_internal_key (KeyReservation * kr); -/* FIXME! The server tends to crash when "E" is pressed.. (?!) - * (but only when the joystick driver is the last one on the list...) - */ +LinkedList * keylist; -/* Checks for keypad input, and dispatches it */ -int -handle_input () + +int init_input() { - char str[15]; - int key; - Screen * s; - /*Widget * w; */ - Client * c; + report (RPT_INFO, "%s()", __FUNCTION__ ); - report (RPT_INFO, "handle_input()" ); - - if ((key = drivers_getkey ()) == 0) - return 0; - - debug (RPT_INFO, "handle_input got key: '%c'", key); - - /* Sequence: - * Does the current screen want the key? - * IfTrue: handle and quit - * IfFalse: - * Let ALL clients handle it if they want - * Let Server handle it, too - * - * This leads to a unique situation: - * First: multiple clients may handle the same key in multiple ways - * Second: the server may handle the key differently yet - * - * Solution: Only the current screen can handle the key press. - * Alternately, only one client can handle the key press. - */ - - /* TODO: Interpret and translate keys! */ - - /* Give current screen a shot at the key first */ - s = CurrentScreen (); - - if (KeyWanted(s->keys, key)) { - /* This screen wants this key. Tell it we got one */ - snprintf(str, sizeof(str), "key %c\n", key); - sock_send_string(s->client->sock, str); - /* Nobody else gets this key */ - } - - /* if the current screen doesn't want it, - * let the server have it... - */ - - else { - /* Give key to clients who want it */ - - c = clients_getfirst(); - - while (c) { - /* If the client should have this keypress... */ - if(KeyWanted(c->client_keys,key)) { - /* Send keypress to client */ - snprintf(str, sizeof(str), "key %c\n", key); - sock_send_string(c->sock, str); - break; /* first come, first serve */ - }; - c = clients_getnext(); - } /* while clients */ - - /* Give server a shot at all keys */ - server_input (key); - } + keylist = LL_new(); return 0; } +int +handle_input () +{ + char * key; + Client * c; + KeyReservation * kr; + + report (RPT_INFO, "%s()", __FUNCTION__ ); + + c = screenlist_current()->client; + + /* Handle all keypresses */ + while ((key = drivers_get_key ()) != NULL ) { + + /* Find what client wants the key */ + kr = input_find_key (key, c); + if (kr) { + /* A hit ! */ + if (kr->client == NULL) { + report (RPT_DEBUG, "%s: key for internal client: [%.40s]", __FUNCTION__, key ); + input_internal_key (kr); + } + else { + /* It's an external client */ + report (RPT_DEBUG, "%s: key for external client: [%.40s]", __FUNCTION__, key ); + input_send_to_client (c, key); + } + } + else { + /* What do we do with left-over keys ? */ + report (RPT_INFO, "%s: left over key: [%.40s]", __FUNCTION__, key ); + + /* Well... nothing ! */ + } + } + return 0; +} + +void input_send_to_client (Client * c, char * key) +{ + char * s; + + debug (RPT_DEBUG, "%s( client=%p, key=\"%.40s\" )", __FUNCTION__, c, key); + + /* Allocate just as much as we need */ + s = malloc (strlen(key) + strlen("key \n") + 1); + sprintf(s, "key %s\n", key); + sock_send_string(c->sock, s); + free (s); +} + +void +input_internal_key (KeyReservation * kr) +{ + if (kr->exclusive || screenlist_current() == menuscreen) { + menuscreen_key_handler (kr->key); + } + else { + /* TODO: give keys to server screen */ + } +} + + +/* TODO: REPLACE THE FOLLOWING FUNCTION BY SOMETHING NEW */ int server_input (int key) { - report(RPT_INFO, "server_input( key='%c' )", (char) key); + report(RPT_INFO, "%s( key='%c' )", __FUNCTION__, (char) key); switch ((char) key) { case INPUT_PAUSE_KEY: @@ -153,14 +129,98 @@ server_input (int key) screenlist_next (); break; case INPUT_MAIN_MENU_KEY: - debug (RPT_DEBUG, "got the menu key!"); + debug (RPT_DEBUG, "%s: got the menu key!", __FUNCTION__); //server_menu (); report (RPT_ERR, "MENU TEMPORATY DISABLED"); break; default: - debug (RPT_DEBUG, "server_input: Unused key \"%c\" (%i)", (char) key, key); + debug (RPT_DEBUG, "%s: Unused key \"%c\" (%i)", __FUNCTION__, (char) key, key); break; } return 0; } + +int input_reserve_key (char * key, bool exclusive, Client * client) +{ + KeyReservation * kr; + + debug (RPT_DEBUG, "%s( key=\"%.40s\", exclusive=%d, client=%p )", __FUNCTION__, key, exclusive, client); + + /* Find out if this key is already reserved in a way that interferes + * with the new reservation. + */ + for (kr=LL_GetFirst(keylist); kr; kr=LL_GetNext(keylist)) { + if (strcmp (kr->key, key) == 0) { + if (kr->exclusive || exclusive) { + /* Sorry ! */ + return -1; + } + } + } + + /* We can now safely add it ! */ + kr = malloc (sizeof(KeyReservation)); + kr->key = strdup (key); + kr->exclusive = exclusive; + kr->client = client; + LL_Push(keylist, kr); + + report (RPT_INFO, "%s: key [%.40s] is now reserved in %s mode", __FUNCTION__, key, (exclusive?"exclusive":"shared")); + + return 0; +} + +void input_release_key (char * key, Client * client) +{ + KeyReservation * kr; + + debug (RPT_DEBUG, "%s( key=\"%.40s\", client=%p )", __FUNCTION__, key, client); + + for (kr=LL_GetFirst(keylist); kr; kr=LL_GetNext(keylist)) { + if (kr->client == client + && strcmp (kr->key, key) == 0) { + report (RPT_INFO, "%s: key [%.40s] is being released from %s mode", __FUNCTION__, key, (kr->exclusive?"exclusive":"shared")); + free (kr->key); + free (kr); + LL_DeleteNode (keylist); + return; + } + } +} + +void input_release_client_keys (Client * client) +{ + KeyReservation * kr; + + debug (RPT_DEBUG, "%s( client=%p )", __FUNCTION__, client); + + kr=LL_GetFirst(keylist); + while (kr) { + if (kr->client == client) { + report (RPT_INFO, "%s: key [%.40s] is now released from %s mode", __FUNCTION__, kr->key, (kr->exclusive?"exclusive":"shared")); + free (kr->key); + free (kr); + LL_DeleteNode (keylist); + kr = LL_Get (keylist); + } else { + kr = LL_GetNext(keylist); + } + } +} + +KeyReservation * input_find_key (char * key, Client * client) +{ + KeyReservation * kr; + + debug (RPT_DEBUG, "%s( key=\"%.40s\", client=%p )", __FUNCTION__, key, client); + + for (kr=LL_GetFirst(keylist); kr; kr=LL_GetNext(keylist)) { + if (strcmp (kr->key, key) == 0) { + if (kr->exclusive || client==kr->client) { + return kr; + } + } + } + return NULL; +} diff --git a/server/input.h b/server/input.h index d981a52..455533b 100644 --- a/server/input.h +++ b/server/input.h @@ -12,6 +12,8 @@ #ifndef INPUT_H #define INPUT_H +#include + /* Accepts and uses keypad input while displaying screens... */ int handle_input (); @@ -25,5 +27,32 @@ int handle_input (); #define INPUT_FORWARD_KEY 'C' #define INPUT_MAIN_MENU_KEY 'D' +#ifndef bool +# define bool short +# define true 1 +# define false 0 +#endif + +typedef struct KeyReservation { + char * key; + bool exclusive; + Client * client; /* NULL for internal clients */ +} KeyReservation; + + +int init_input(); + /* Init the input handling system */ +int input_reserve_key (char * key, bool exclusive, Client * client); + /* Reserves a key for a client */ + /* Return -1 if reservation of key is not possible */ +void input_release_key (char * key, Client * client); + /* Releases a key reservation */ +void input_release_client_keys (Client * client); + /* Releases all key reservations for a given client */ +KeyReservation * input_find_key (char * key, Client * client); + /* Finds if a key reservation causes a 'hit'. + * If the key was reserved exclusively, the client will be ignored. + * If the key was reserved shared, the client must match. + */ #endif diff --git a/server/main.c b/server/main.c index 0f3e1d3..0dd0705 100644 --- a/server/main.c +++ b/server/main.c @@ -46,6 +46,7 @@ extern int optind, optopt, opterr; #include "parse.h" #include "render.h" #include "serverscreens.h" +#include "menuscreens.h" #include "input.h" #include "configfile.h" #include "drivers.h" @@ -211,6 +212,9 @@ main (int argc, char **argv) } #endif + ESSENTIAL( init_input() ); + ESSENTIAL( init_menu() ); + do_mainloop(); /* This loop never stops; we'll get out only with a signal...*/ @@ -292,11 +296,9 @@ process_command_line (int argc, char **argv) case 'b': if( strcmp( optarg, "on" ) == 0 ) { backlight = BACKLIGHT_ON; - backlight_state = backlight; } else if( strcmp( optarg, "off" ) == 0 ) { backlight = BACKLIGHT_OFF; - backlight_state = backlight; } else if( strcmp( optarg, "open" ) == 0 ) { backlight = BACKLIGHT_OPEN; @@ -398,11 +400,9 @@ process_configfile ( char *configfile ) s = config_get_string( "server", "backlight", 0, UNSET_STR ); if( strcmp( s, "on" ) == 0 ) { backlight = BACKLIGHT_ON; - backlight_state = backlight; } else if( strcmp( s, "off" ) == 0 ) { backlight = BACKLIGHT_OFF; - backlight_state = backlight; } else if( strcmp( s, "open" ) == 0 ) { backlight = BACKLIGHT_OPEN; diff --git a/server/menu.c b/server/menu.c index dc68503..b3efa6f 100644 --- a/server/menu.c +++ b/server/menu.c @@ -9,19 +9,6 @@ * 2002, Joris Robijn * * - * Handles server-supplied menus defined by a table. Read menu.h for - * more information. - * - * Menus are similar to "pull-down" menus, but have some extra features. - * They can contain "normal" menu items, checkboxes, sliders, "movers", - * etc.. - * - * I should probably find a more elegant way of doing this in order - * to handle dynamically-changing menus such as the client list. Tcl/Tk - * has neat ways to do it. Hmm... - * - * NEW DESCRIPTION - * * Handles a menu and all actions that can be performed on it. * * Menus are similar to "pull-down" menus, but have some extra features. @@ -45,80 +32,73 @@ #include "screen.h" - -/* Internal menu item drawing functions */ -void menu_build_menu_screen (MenuItem *item, Screen *s); -void menu_build_slider_screen (MenuItem *item, Screen *s); -void menu_build_numeric_screen (MenuItem *item, Screen *s); -void menu_build_string_screen (MenuItem *item, Screen *s); -void menu_update_menu_screen (MenuItem *item, Screen *s); -void menu_update_slider_screen (MenuItem *item, Screen *s); -void menu_update_numeric_screen (MenuItem *item, Screen *s); -void menu_update_string_screen (MenuItem *item, Screen *s); - - -MenuItem *menu_create_add_item (Menu *into_menu, int type, char *id, - EventFunc(*event_func)) +Menu * +menu_create (char *id, MenuEventFunc(*event_func), + char *text, void *association) { - Menu *new_item; + Menu *new_menu; - /* Allocate space and fill struct */ - new_item = malloc (sizeof(MenuItem)); - if (!new_item) { - report (RPT_ERR, "menu_add_new_item: Could not allocate memory"); - return NULL; - } - new_item->type = type; - new_item->id = strdup (id); - if (!new_item->id) { - report (RPT_ERR, "menu_add_new_item: Could not allocate memory"); - return NULL; - } - new_item->event_func = event_func; + debug (RPT_DEBUG, "%s( id=\"%s\", event_func=%p, text=\"%s\", association=%p )", __FUNCTION__, id, event_func, text, association); - /* Clear the type specific data part */ - memset ( &(new_item->data), 0, sizeof(new_item->data)); + new_menu = menuitem_create (MENUITEM_MENU, id, event_func, text); - if (type == MENUITEM_MENU ) { - /* We have created a menu, create our needed data */ - new_item->data.menu.parent = into_menu; - LL_new (new_item->data.menu.contents); - } + new_menu->data.menu.contents = LL_new(); + new_menu->data.menu.association = association; - if (into_menu) { - /* Add the item to the menu */ - LL_Push (into_menu->data.menu.contents, new_item); - } - - return new_item; + return new_menu; } -int menu_destroy_item (Menu *menu, MenuItem *item) +void +menu_destroy (Menu *menu) { - /* Is the item a menu ? */ - if (item->type == MENUITEM_MENU ) { - /* Check if the menu is empty */ - if ( LL_Length(item->data.menu.contents) != 0 ) { - report (RPT_ERR, "menu not empty, cannot be destroyed"); - return -1; - } - /* Destroy our menu data */ - if (item->data.menu.text) - free (item->data.menu.text); - LL_Destroy (item->data.menu.contents); - } - free (item->id); - free (item); + debug (RPT_DEBUG, "%s( menu=\"%s\" )", __FUNCTION__, menu->id); + + menu_destroy_all_items (menu); + LL_Destroy (menu->data.menu.contents); + menu->data.menu.contents = NULL; + + /* After this the general menuitem routine destroys the rest... */ +} + +void +menu_add_item (Menu *menu, MenuItem *item) +{ + debug (RPT_DEBUG, "%s( menu=\"%s\", item=\"%s\" )", __FUNCTION__, menu->id, item->id); + + if (!menu) return; + + /* Add the item to the menu */ + LL_Push (menu->data.menu.contents, item); + item->parent = menu; +} + +void +menu_remove_item (Menu *menu, MenuItem *item) +{ + debug (RPT_DEBUG, "%s( menu=\"%s\", item=\"%s\" )", __FUNCTION__, menu->id, item->id); - /* Remove the item from the menu */ LL_Remove (menu->data.menu.contents, item); +} - return 0; +void +menu_destroy_all_items (Menu *menu) +{ + MenuItem * item; + + debug (RPT_DEBUG, "%s( menu=\"%s\" )", __FUNCTION__, menu->id); + + for( item = menu_getfirst_item(menu); item; item = menu_getfirst_item(menu) ) { + menuitem_destroy (item); + LL_Remove (menu->data.menu.contents, item); + } } MenuItem *menu_find_item (Menu *menu, char *id) { MenuItem * item; + + debug (RPT_DEBUG, "%s( menu=\"%s\", id=\"%s\" )", __FUNCTION__, menu->id, id); + for( item = menu_getfirst_item(menu); item; item = menu_getnext_item(menu) ) { if ( strcmp(item->id, id) == 0 ) { return item; @@ -127,115 +107,279 @@ MenuItem *menu_find_item (Menu *menu, char *id) return NULL; } - -/******** MENU SCREEN BUILD FUNCTIONS ********/ - -void menu_build_screen (MenuItem *item, Screen *s) +void menu_reset (Menu *menu) { - Widget * w; - - if (display_props) { - /* Nothing to build if no display size is known */ - report (RPT_ERR, "menu_build_screen: display size unknown"); - return; - } - - /* First remove all widgets from the screen */ - for ( w=screen_getfirst_widget(s); w; w=screen_getnext_widget(s) ) { - /* We know these widgets don't have subwidgets */ - screen_remove_widget (s, w); - widget_destroy (w); - } - - switch (item->type) { - case MENUITEM_MENU: - menu_build_menu_screen (item, s); - break; - case MENUITEM_SLIDER: - menu_build_slider_screen (item, s); - break; - case MENUITEM_NUMERIC_INPUT: - menu_build_slider_screen (item, s); - break; - case MENUITEM_STRING_INPUT: - menu_build_slider_screen (item, s); - break; - default: - report (RPT_ERR, "menu_build_screen: given menuitem cannot be active"); - return; - } - menu_update_screen (item, s); + menu->data.menu.selector_pos = 0; + menu->data.menu.scroll = 0; } -void menu_build_menu_screen (MenuItem *item, Screen *s) +void menu_build_screen (MenuItem *menu, Screen *s) { Widget * w; - int linenr; + MenuItem * subitem; + int itemnr; - for (linenr=1; linenrwidth; linenr++) { - char buf[8]; - snprintf (buf, sizeof(buf)-1, "line%d", linenr); + debug (RPT_DEBUG, "%s( menu=\"%s\", screen=\"%s\" )", __FUNCTION__, menu->id, s->id); + + /* TODO: Put menu in a frame to do easy scrolling */ + /* Problem: frames are not handled correctly by renderer */ + + /* Create menu title widget */ + w = widget_create ("title", WID_TITLE, s); + screen_add_widget (s, w); + w->text = strdup(menu->text); + w->x = 1; + + /* Create widgets for each subitem in the menu */ + for (subitem = LL_GetFirst (menu->data.menu.contents), itemnr = 0; + subitem; + subitem = LL_GetNext (menu->data.menu.contents), itemnr ++ ) + { + char buf[10]; + + snprintf (buf, sizeof(buf)-1, "text%d", itemnr); buf[sizeof(buf)-1] = 0; w = widget_create (buf, WID_STRING, s); /* (buf will be copied) */ - w->text = strdup(""); screen_add_widget (s, w); + w->x = 2; + + switch (subitem->type) { + case MENUITEM_CHECKBOX: + + /* Limit string length */ + w->text = strdup (subitem->text); + if (strlen(subitem->text) >= display_props->width-2) { + (w->text)[display_props->width-2] = 0; + } + + /* Add icon for checkbox */ + snprintf (buf, sizeof(buf)-1, "icon%d", itemnr); + buf[sizeof(buf)-1] = 0; + w = widget_create (buf, WID_ICON, s); + /* (buf will be copied) */ + screen_add_widget (s, w); + w->x = display_props->width - 1; + w->length = ICON_CHECKBOX_OFF; + break; + case MENUITEM_RING: + /* Create string for text + ringtext */ + w->text = malloc (display_props->width); + break; + case MENUITEM_MENU: + case MENUITEM_ACTION: + case MENUITEM_SLIDER: + case MENUITEM_NUMERIC: + case MENUITEM_ALPHA: + /* Limit string length */ + w->text = strdup (subitem->text); + if (strlen(subitem->text) >= display_props->width-1) { + (w->text)[display_props->width-1] = 0; + } + break; + } + } + + /* Add arrow for selection on the left */ + w = widget_create ("selector", WID_ICON, s); + screen_add_widget (s, w); + w->length = ICON_SELECTOR_AT_LEFT; + w->x = 1; + + /* Add scrollers on the right side on top and bottom */ + /* TODO: when menu is in a frame, these can be removed */ + w = widget_create ("upscroller", WID_ICON, s); + screen_add_widget (s, w); + w->length = ICON_ARROW_UP; + w->x = display_props->width; + w->y = 1; + + w = widget_create ("downscroller", WID_ICON, s); + screen_add_widget (s, w); + w->length = ICON_ARROW_DOWN; + w->x = display_props->width; + w->y = display_props->height; + +} + +void menu_update_screen (MenuItem *menu, Screen *s) +{ + Widget * w; + MenuItem * subitem; + int itemnr; + + debug (RPT_INFO, "%s( menu=\"%s\", screen=\"%s\" )", __FUNCTION__, menu->id, s->id); + + /* Update widgets for the title */ + w = screen_find_widget (s, "title"); + if (!w) report (RPT_ERR, "%s: could not find widget: %s", __FUNCTION__, "title"); + w->y = 1 - menu->data.menu.scroll; + + /* TODO: remove next 5 limes when rendering is safe */ + if (w->y > 0 && w->y <= display_props->height) { + w->type = WID_TITLE; + } else { + w->type = WID_NONE; /* make invisible */ + } + + /* Update widgets for each subitem in the menu */ + for (subitem = LL_GetFirst (menu->data.menu.contents), itemnr = 0; + subitem; + subitem = LL_GetNext (menu->data.menu.contents), itemnr ++ ) + { + char buf[10]; + char * p; + + snprintf (buf, sizeof(buf)-1, "text%d", itemnr); + buf[sizeof(buf)-1] = 0; + w = screen_find_widget (s, buf); + if (!w) report (RPT_ERR, "%s: could not find widget: %s", __FUNCTION__, buf); + w->y = 2 + itemnr - menu->data.menu.scroll; + + /* TODO: remove next 5 limes when rendering is safe */ + if (w->y > 0 && w->y <= display_props->height) { + w->type = WID_STRING; + } else { + w->type = WID_NONE; /* make invisible */ + } + + switch (subitem->type) { + case MENUITEM_CHECKBOX: + + /* Update icon value for checkbox */ + snprintf (buf, sizeof(buf)-1, "icon%d", itemnr); + buf[sizeof(buf)-1] = 0; + w = screen_find_widget (s, buf); + if (!w) report (RPT_ERR, "%s: could not find widget: %s", __FUNCTION__, buf); + w->y = 2 + itemnr - menu->data.menu.scroll; + w->length = ((int[]){ICON_CHECKBOX_OFF,ICON_CHECKBOX_ON,ICON_CHECKBOX_GRAY})[subitem->data.checkbox.value]; + + /* TODO: remove next 5 limes when rendering is safe */ + if (w->y > 0 && w->y <= display_props->height) { + w->type = WID_ICON; + } else { + w->type = WID_NONE; /* make invisible */ + } + break; + case MENUITEM_RING: + /* Limit string length and add ringstring */ + p = LL_GetByIndex (subitem->data.ring.strings, subitem->data.ring.value); + if (strlen(p) > display_props->width - 3) { + short a = display_props->width - 3; + /* We need to limit the ring string and DON'T + * display the item text */ + strcpy (w->text, " "); + memcpy (w->text + 1, p, a); + w->text[a + 1] = 0; + } + else { + short b = display_props->width - 2 - strlen (p); + short c = min (strlen (subitem->text), b - 1); + /* We don't limit the ring string */ + memset (w->text, ' ', b); + memcpy (w->text, subitem->text, c); + strcpy (w->text + b, p); + } + break; + default: + } + } + + /* Update selector position */ + w = screen_find_widget (s, "selector"); + if (!w) report (RPT_ERR, "%s: could not find widget: %s", __FUNCTION__, "selector"); + w->y = 2 + menu->data.menu.selector_pos - menu->data.menu.scroll; + + /* Enable upscroller */ + w = screen_find_widget (s, "upscroller"); + if (!w) report (RPT_ERR, "%s: could not find widget: %s", __FUNCTION__, "upscroller"); + if (menu->data.menu.scroll > 0) { + w->type = WID_ICON; + } else { + w->type = WID_NONE; /* disable it this way */ + } + /* Enable downscroller */ + w = screen_find_widget (s, "downscroller"); + if (!w) report (RPT_ERR, "%s: could not find widget: %s", __FUNCTION__, "downscroller"); + if (LL_Length(menu->data.menu.contents) - menu->data.menu.scroll - display_props->height + 1 > 0 ) { + w->type = WID_ICON; + } else { + w->type = WID_NONE; /* disable it this way */ } } -void menu_build_slider_screen (MenuItem *item, Screen *s) +MenuResult menu_handle_input (Menu *menu, MenuToken token, char * key) { -} + MenuItem *subitem; -void menu_build_numeric_screen (MenuItem *item, Screen *s) -{ -} + debug (RPT_DEBUG, "%s( menu=\"%s\", token=%d, key=\"%s\" )", __FUNCTION__, menu->id, token, key); -void menu_build_string_screen (MenuItem *item, Screen *s) -{ -} - -void menu_build_password_screen (MenuItem *item, Screen *s) -{ -} - -/******** MENU SCREEN UPDATE FUNCTIONS ********/ - -void menu_update_screen (MenuItem *item, Screen *s) { - switch (item->type) { - case MENUITEM_MENU: - menu_update_menu_screen (item, s); + switch (token) { + case MENUTOKEN_MENU: + return MENURESULT_CLOSE; + case MENUTOKEN_ENTER: + subitem = LL_GetByIndex (menu->data.menu.contents, + menu->data.menu.selector_pos); + if (!subitem) break; + switch (subitem->type) { + case MENUITEM_ACTION: + if (subitem->event_func) + subitem->event_func (subitem, MENUEVENT_ENTER); + if (subitem->data.action.quit_menu) + return MENURESULT_QUIT; + if (subitem->data.action.close_menu) + return MENURESULT_CLOSE; + return MENURESULT_OK; + case MENUITEM_CHECKBOX: + if (subitem->data.checkbox.allow_gray) { + subitem->data.checkbox.value = (subitem->data.checkbox.value + 1) % 3; + } + else { + subitem->data.checkbox.value = (subitem->data.checkbox.value + 1) % 2; + } + if (subitem->event_func) + subitem->event_func (subitem, MENUEVENT_UPDATE); + return MENURESULT_OK; + case MENUITEM_RING: + subitem->data.ring.value = (subitem->data.ring.value + 1) % LL_Length (subitem->data.ring.strings); + if (subitem->event_func) + subitem->event_func (subitem, MENUEVENT_UPDATE); + return MENURESULT_OK; + case MENUITEM_MENU: + case MENUITEM_SLIDER: + case MENUITEM_NUMERIC: + case MENUITEM_ALPHA: + if (subitem->event_func) + subitem->event_func (subitem, MENUEVENT_ENTER); + return MENURESULT_ENTER; + default: break; - case MENUITEM_SLIDER: - menu_update_slider_screen (item, s); - break; - case MENUITEM_NUMERIC_INPUT: - menu_update_slider_screen (item, s); - break; - case MENUITEM_STRING_INPUT: - menu_update_slider_screen (item, s); - break; - default: - report (RPT_ERR, "menu_update_screen: given menuitem cannot be active"); + } + return MENURESULT_ERROR; + case MENUTOKEN_UP: + if (menu->data.menu.selector_pos > 0) { + menu->data.menu.selector_pos --; + if (menu->data.menu.selector_pos + 1 < menu->data.menu.scroll) { + menu->data.menu.scroll --; + } + } + else if (menu->data.menu.selector_pos == 0) { + if (menu->data.menu.selector_pos < menu->data.menu.scroll) { + menu->data.menu.scroll --; + } + } + return MENURESULT_OK; + case MENUTOKEN_DOWN: + if (menu->data.menu.selector_pos < LL_Length(menu->data.menu.contents) - 1) { + menu->data.menu.selector_pos ++; + } + if (menu->data.menu.selector_pos - menu->data.menu.scroll + 2 > display_props->height) { + menu->data.menu.scroll ++; + } + return MENURESULT_OK; + case MENUTOKEN_OTHER: + /* TODO: move to the selected number and enter it */ + return MENURESULT_OK; } + return MENURESULT_ERROR; } - -void menu_update_menu_screen (MenuItem *item, Screen *s) -{ -} - -void menu_update_slider_screen (MenuItem *item, Screen *s) -{ - -} - -void menu_update_numeric_screen (MenuItem *item, Screen *s) -{ - -} - -void menu_update_string_screen (MenuItem *item, Screen *s) -{ - -} - diff --git a/server/menu.h b/server/menu.h index 9c1fece..578a444 100644 --- a/server/menu.h +++ b/server/menu.h @@ -7,186 +7,107 @@ * * Copyright (c) 1999, William Ferrell, Scott Scriven * - * Defines all the menu data and action. + * Defines all the menu data and actions. */ #ifndef MENU_H #define MENU_H + +struct MenuData; +#include "menuitem.h" + + #include "shared/LL.h" #include "screen.h" -/********************************************************************* - * Data definitions of the menustuff - * Do not modify attributes from your code, but call functions. - */ +#ifndef bool +# define bool short +# define true 1 +# define false 0 +#endif -typedef enum MenuItemType { - MENUITEM_NONE = 0, - MENUITEM_TITLE, - MENUITEM_MENU, - MENUITEM_ACTION, - MENUITEM_CHECKBOX, - MENUITEM_SLIDER, - MENUITEM_NUMERIC_INPUT, - MENUITEM_STRING_INPUT, -} MenuItemType; +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#define max(a,b) (((a) > (b)) ? (a) : (b)) -typedef enum CheckboxValue { - CHECKBOX_OFF = 0, CHECKBOX_ON, CHECKBOX_GRAY -} CheckboxValue; - - - -/* Return codes from an event value */ -typedef enum MenuEventFuncResult { - MENUEVENTRES_ERROR = -1, /* Something has gone wrong */ - MENUEVENTRES_OK = 0, /* Event handled OK */ - MENUEVENTRES_CLOSE, /* Event handled OK, close the menu now */ - MENUEVENTRES_QUIT /* Event handled OK, close ALL menus now */ -} MenuEventFuncResult; - -/* Events that can happen */ -typedef enum MenuEventType { - MENUEVENT_SELECT, /* Item has been selected - (menu opened, action selected) */ - MENUEVENT_UPDATE, /* Item has been modified - (checkbox, numeric, string, password) */ - MENUEVENT_PLUS, /* Item has been modified in positive direction - (slider moved) */ - MENUEVENT_MINUS /* Item has been modified in negative direction - (slider moved) */ -} MenuEventType; - -#define EventFunc(f) MenuEventFuncResult (f) (void *object, MenuEventType event) - -typedef struct MenuItem { - int type; /* Type as defined above */ - char *id; /* Internal name for client supplied menus */ - EventFunc (*event_func); - /* Defines event_func to be an event function */ - union data { - struct menu { - char *text; /* Visible name of the submenu */ - int current_item_index; /* At what menuitem is the selector */ - struct MenuItem *parent; /* Parent of this menu */ - LinkedList *contents; /* What's in this menu */ - } menu; - struct checkbox { - char *text; /* Visible name of item */ - CheckboxValue value; /* Current value */ - int allowgray; /* is CHECKBOX_GRAY allowed ? */ - } checkbox; - struct slider { - char *text; /* Visible name of item */ - char *lefttext; /* Text at minimal value */ - char *righttext;/* Text at minimal value */ - int value; /* Current value */ - int minvalue; - int maxvalue; - int stepsize; - } slider; - struct numeric { - char *text; /* Visible name of item */ - int value; /* Current value */ - int maxvalue; - int minvalue; - } numeric; - struct string { - char *text; /* Visible name of item */ - char *value; /* Current value (in a buffer) */ - char passwordchar; /* For passwords */ - int maxlength; - } string; - struct password { - char *text; /* Visible name of item */ - char *value; /* Current value (in a buffer) */ - int maxlength; /* Max allowed length */ - } password; - } data; -} MenuItem; - -#define Menu MenuItem +typedef MenuItem Menu; /* A Menu is a MenuItem too. * This definition is only for better understanding of this code. */ -/********************************************************************* - * Functions to use the menustuff +Menu *menu_create (char *id, MenuEventFunc(*event_func), + char *text, void *association); +/* Creates a new menu. + */ +/* Creates a string value box. + * Association is free to set to anything you want. It's not used by the menu. + * Generated events: MENUEVENT_ENTER upon entering this menu, */ -MenuItem *menu_create_add_item (Menu *into_menu, int type, char *name, - EventFunc(*event_func)); -/* Creates a new item and adds it to the given menu. - * All attributes if the new item are cleared. - * into_menu What menu should it be in. - * type: type as defined above (determines also how the attributes will - * be filled). - * name: internal name of the item. Never visible. String will be - * copied. - * event_func: the event function that should be called upon actions on this - * item. see event.h. - * The data part of the new item is filled with 0's. - * - * Return value: the new item, of NULL on error. - * - * If you create a new menu, the menu data will automatically be allocated, - * and the contents pointer set to this menu. - * - * To initialize data for the data part, do things like: - * - * yourname = menu_create_add_item (m, MENUITEM_STRING_INPUT, "YourName", - * yourname_change_handler); - * yourname->data.text = strdup ("Your name"); - * yourname->data.maxlength = 10; - * yourname->data.value = malloc (10+1); <--- maxlength + 1 - * yourname->data.value[0] = 0; <--- clear the string - * - * The data NEEDS to be initialized immediately (at least before it's first - * used). - * +void menu_destroy (Menu *menu); +/* Deletes menu from memory. + * Destructors will be called for all subitems. + * DO NOT CALL THIS FUNCTION, CALL menuitem_destoy INSTEAD ! */ -int menu_destroy_item (Menu *menu, MenuItem *item); -/* Deletes item from menu and removes item from memory - * - * If the item is a menu, the menu data will also be deallocated. - * - * Return value: 0 for OK, -1 for error (eg. submenus not empty) - * - * To destroy the data in the data struct, do things like: - * - * free (yourname->data.value); - * free (yourname->data.text); - * menu_destroy_item (yourname); - */ +void menu_add_item (Menu *menu, MenuItem *item); +/* Adds an item to the menu */ + +void menu_remove_item (Menu *menu, MenuItem *item); +/* Removes an item from the menu (does not destroy it) */ + +void menu_destroy_all_items (Menu *menu); +/* Destroys and removes all items from the menu */ static inline MenuItem *menu_getfirst_item (Menu *menu) -/* Retrieves the first item from the list of items in the menu. */ +/* Enumeration function. + * Retrieves the first item from the list of items in the menu. + */ { return (MenuItem*) LL_GetFirst( menu->data.menu.contents ); } static inline MenuItem *menu_getnext_item (Menu *menu) -/* Retrieves the next item from the list of items in the menu. +/* Enumeration function. + * Retrieves the next item from the list of items in the menu. * No other menu calls should be made between menu_first_item() and - * this function, to keep the list-cursor where it is. */ + * this function, to keep the list-cursor where it is. + */ { return (MenuItem*) LL_GetNext( menu->data.menu.contents ); } -MenuItem *menu_find_item (Menu *menu, char *id); -/* Retrieves the first item from the list of items in the menu. */ +static inline MenuItem *menu_get_current_item (Menu *menu) +/* Retrieves the current item from the list of items in the menu. */ +{ + return LL_GetByIndex( + menu->data.menu.contents, + menu->data.menu.selector_pos); +} -void menu_build_screen (MenuItem *item, Screen *s); -/* Builds the selected menuitem on screen using widgets - * Returns the initial state. +MenuItem *menu_find_item (Menu *menu, char *id); +/* Retrieves the first item from the list of items in the menu. */ + +void menu_reset (Menu *menu); +/* Resets it to initial state. + * DO NOT CALL THIS FUNCTION, CALL menuitem_build_screen INSTEAD ! */ -void menu_update_screen (MenuItem *item, Screen *s); +void menu_build_screen (Menu *menu, Screen *s); +/* Builds the selected menuitem on screen using widgets. + * DO NOT CALL THIS FUNCTION, CALL menuitem_build_screen INSTEAD ! + */ + +void menu_update_screen (Menu *menu, Screen *s); /* Updates the widgets of the selected menuitem - * Returns the new state. + * DO NOT CALL THIS FUNCTION, CALL menuitem_build_screen INSTEAD ! + */ + +MenuResult menu_handle_input (Menu *menu, MenuToken token, char * key); +/* Does something with the given input. + * key is only used if token is MENUTOKEN_OTHER. + * DO NOT CALL THIS FUNCTION, CALL menuitem_build_screen INSTEAD ! */ #endif diff --git a/server/menuitem.c b/server/menuitem.c new file mode 100644 index 0000000..ddac4e2 --- /dev/null +++ b/server/menuitem.c @@ -0,0 +1,922 @@ +/* + * menuitem.c + * This file is part of LCDd, the lcdproc server. + * + * This file is released under the GNU General Public License. Refer to the + * COPYING file distributed with this package. + * + * Copyright (c) 1999, William Ferrell, Scott Scriven + * 2002, Joris Robijn + * + * + * Handles a menuitem and all actions that can be performed on it. + */ + +#include +#include + +#include "shared/report.h" + +#include "menuitem.h" +#include "menu.h" +#include "drivers.h" + +#define MAX_NUMERIC_LEN 40 + +char *error_strs[] = {"", "Out of range", "Too long", "Too short"}; + +void menuitem_destroy_action (MenuItem *item); +void menuitem_destroy_checkbox (MenuItem *item); +void menuitem_destroy_ring (MenuItem *item); +void menuitem_destroy_slider (MenuItem *item); +void menuitem_destroy_numeric (MenuItem *item); +void menuitem_destroy_alpha (MenuItem *item); + +void menuitem_reset_numeric (MenuItem *item); +void menuitem_reset_alpha (MenuItem *item); + +void menuitem_build_screen_slider (MenuItem *item, Screen *s); +void menuitem_build_screen_numeric (MenuItem *item, Screen *s); +void menuitem_build_screen_alpha (MenuItem *item, Screen *s); + +void menuitem_update_screen_slider (MenuItem *item, Screen *s); +void menuitem_update_screen_numeric (MenuItem *item, Screen *s); +void menuitem_update_screen_alpha (MenuItem *item, Screen *s); + +MenuResult menuitem_handle_input_slider (MenuItem *item, MenuToken token, char * key); +MenuResult menuitem_handle_input_numeric (MenuItem *item, MenuToken token, char * key); +MenuResult menuitem_handle_input_alpha (MenuItem *item, MenuToken token, char * key); + +/******** FUNCTION TABLES ********/ +/* Tables with functions to call for all different item types */ + +void (*destructor_table[NUM_ITEMTYPES] ) (MenuItem *item) = +{ + menu_destroy, + NULL, + menuitem_destroy_ring, + menuitem_destroy_slider, + menuitem_destroy_numeric, + menuitem_destroy_alpha +}; +void (*reset_table[NUM_ITEMTYPES] ) (MenuItem *item) = +{ + menu_reset, + NULL, + NULL, + NULL, + NULL, + menuitem_reset_numeric, + menuitem_reset_alpha +}; +void (*build_screen_table[NUM_ITEMTYPES] ) (MenuItem *item, Screen *s) = +{ + menu_build_screen, + NULL, + NULL, + NULL, + menuitem_build_screen_slider, + menuitem_build_screen_numeric, + menuitem_build_screen_alpha +}; +void (*update_screen_table[NUM_ITEMTYPES] ) (MenuItem *item, Screen *s) = +{ + menu_update_screen, + NULL, + NULL, + NULL, + menuitem_update_screen_slider, + menuitem_update_screen_numeric, + menuitem_update_screen_alpha +}; + +MenuResult (*handle_input_table[NUM_ITEMTYPES] ) (MenuItem *item, MenuToken token, char *key) = +{ + menu_handle_input, + NULL, + NULL, + NULL, + menuitem_handle_input_slider, + menuitem_handle_input_numeric, + menuitem_handle_input_alpha +}; + +/******** METHODS ********/ + +MenuItem *menuitem_create (MenuItemType type, char *id, MenuEventFunc(*event_func), + char *text) +{ + MenuItem *new_item; + + debug (RPT_DEBUG, "%s( type=%d, id=\"%s\", event_func=%p, text=\"%s\" )", + __FUNCTION__, type, id, event_func, text); + + /* Allocate space and fill struct */ + new_item = malloc (sizeof(MenuItem)); + if (!new_item) { + report (RPT_ERR, "%s: Could not allocate memory", __FUNCTION__); + return NULL; + } + new_item->type = type; + new_item->id = strdup (id); + if (!new_item->id) { + report (RPT_ERR, "%s: Could not allocate memory", __FUNCTION__); + return NULL; + } + new_item->parent = NULL; + new_item->event_func = event_func; + new_item->text = strdup (text); + if (!new_item->text) { + report (RPT_ERR, "%s: Could not allocate memory", __FUNCTION__); + return NULL; + } + + /* Clear the type specific data part */ + memset ( &(new_item->data), 0, sizeof(new_item->data)); + + return new_item; +} + +MenuItem *menuitem_create_action (char *id, MenuEventFunc(*event_func), + char *text, bool close_menu, bool quit_menu) +{ + MenuItem *new_item; + + debug (RPT_DEBUG, "%s( id=\"%s\", event_func=%p, text=\"%s\", close_menu=%d )", + __FUNCTION__, id, event_func, text, close_menu); + + new_item = menuitem_create (MENUITEM_ACTION, id, event_func, text); + new_item->data.action.close_menu = close_menu; + new_item->data.action.quit_menu = quit_menu; + + return new_item; +} + +MenuItem *menuitem_create_checkbox (char *id, MenuEventFunc(*event_func), + char *text, bool allow_gray, bool value) +{ + MenuItem *new_item; + + debug (RPT_DEBUG, "%s( id=\"%s\", event_func=%p, text=\"%s\", allow_gray=%d, value=%d )", + __FUNCTION__, id, event_func, text, allow_gray, value); + + new_item = menuitem_create (MENUITEM_CHECKBOX, id, event_func, text); + new_item->data.checkbox.allow_gray = allow_gray; + new_item->data.checkbox.value = value; + + return new_item; +} + +MenuItem *menuitem_create_ring (char *id, MenuEventFunc(*event_func), + char *text, char *strings, short value) +{ + MenuItem *new_item; + char *tabptr, *p, *new_s; + + debug (RPT_DEBUG, "%s( id=\"%s\", event_func=%p, text=\"%s\", strings=\"%s\", value=%d )", + __FUNCTION__, id, event_func, text, strings, value); + + new_item = menuitem_create (MENUITEM_RING, id, event_func, text); + + /* parse strings */ + p = strings; + new_item->data.ring.strings = LL_new(); + while ((tabptr = strchr(p, '\t')) != NULL) { + int len = (int)(tabptr - p); + + /* Alloc and copy substring */ + new_s = malloc (len + 1); + strncpy (new_s, p, len); + new_s[len] = 0; + + LL_Push (new_item->data.ring.strings, new_s); + + /* Go to next string */ + p = tabptr + 1; + } + /* Add last string */ + new_s = strdup (p); + LL_Push (new_item->data.ring.strings, new_s); + + new_item->data.ring.value = value; + + return new_item; +} + +MenuItem *menuitem_create_slider (char *id, MenuEventFunc(*event_func), + char *text, char *mintext, char *maxtext, + int minvalue, int maxvalue, int stepsize, int value) +{ + MenuItem *new_item; + + debug (RPT_DEBUG, "%s( id=\"%s\", event_func=%p, text=\"%s\", mintext=\"%s\", maxtext=\"%s\", minvalue=%d, maxvalue=%d, stepsize=%d, value=%d )", + __FUNCTION__, id, event_func, text, mintext, maxtext, minvalue, maxvalue, stepsize, value); + + new_item = menuitem_create (MENUITEM_SLIDER, id, event_func, text); + new_item->data.slider.mintext = strdup (mintext); + new_item->data.slider.maxtext = strdup (maxtext); + new_item->data.slider.minvalue = minvalue; + new_item->data.slider.maxvalue = maxvalue; + new_item->data.slider.stepsize = stepsize; + new_item->data.slider.value = value; + + return new_item; +} + +MenuItem *menuitem_create_numeric (char *id, MenuEventFunc(*event_func), + char *text, int minvalue, int maxvalue, int value) +{ + MenuItem *new_item; + + debug (RPT_DEBUG, "%s( id=\"%s\", event_func=%p, text=\"%s\", minvalue=%d, maxvalue=%d, value=%d )", + __FUNCTION__, id, event_func, text, minvalue, minvalue, value); + + new_item = menuitem_create (MENUITEM_NUMERIC, id, event_func, text); + new_item->data.numeric.maxvalue = maxvalue; + new_item->data.numeric.minvalue = minvalue; + new_item->data.numeric.edit_str = malloc (MAX_NUMERIC_LEN); + new_item->data.numeric.value = value; + + return new_item; +} + +MenuItem *menuitem_create_alpha (char *id, MenuEventFunc(*event_func), + char *text, char password_char, short minlength, short maxlength, + bool allow_caps, bool allow_noncaps, bool allow_numbers, + char *allowed_extra, char *value) +{ + MenuItem *new_item; + char * chars; + + debug (RPT_DEBUG, "%s( id=\"%s\", event_func=%p, text=\"%s\", password_char=%d, maxlength=%d, value=\"%s\" )", + __FUNCTION__, id, event_func, text, password_char, maxlength, value); + + new_item = menuitem_create (MENUITEM_ALPHA, id, event_func, text); + new_item->data.alpha.password_char = password_char; + new_item->data.alpha.minlength = minlength; + new_item->data.alpha.maxlength = maxlength; + + new_item->data.alpha.value = malloc (maxlength + 1); + strncpy (new_item->data.alpha.value, value, maxlength); + new_item->data.alpha.value[maxlength] = 0; + + /* Create list of allowed chars */ + chars = malloc (26 + 26 + 10 + strlen(allowed_extra) + 1); + chars[0] = 0; + if (allow_caps) + strcat (chars, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + if (allow_noncaps) + strcat (chars, "abcdefghijklmnopqrstuvwxyz"); + if (allow_numbers) + strcat (chars, "0123456789"); + if (allowed_extra) { + strcat (chars, allowed_extra); + } + new_item->data.alpha.allowed_chars = chars; + + new_item->data.alpha.edit_str = malloc (maxlength + 1); + + return new_item; +} + + +void menuitem_destroy (MenuItem *item) +{ + void (*destructor) (MenuItem *); + + debug (RPT_DEBUG, "%s( item=\"%s\" )", __FUNCTION__, item->id); + + /* First destroy type specific data */ + destructor = destructor_table[item->type]; + if (destructor) + destructor (item); + + /* Following strings should always be allocated */ + free (item->text); + free (item->id); + + /* And finally...*/ + free (item); +} + +void menuitem_destroy_ring (MenuItem *item) +{ + char * s; + + debug (RPT_DEBUG, "%s( item=\"%s\" )", __FUNCTION__, item->id); + + /* deallocate the strings */ + for (s = LL_GetFirst(item->data.ring.strings); s; s = LL_GetNext(item->data.ring.strings)) { + free (s); + } + /* and the list */ + LL_Destroy (item->data.ring.strings); +} + +void menuitem_destroy_slider (MenuItem *item) +{ + debug (RPT_DEBUG, "%s( item=\"%s\" )", __FUNCTION__, item->id); + + /* These strings should always be allocated */ + free (item->data.slider.mintext); + free (item->data.slider.maxtext); +} + +void menuitem_destroy_numeric (MenuItem *item) +{ + debug (RPT_DEBUG, "%s( item=\"%s\" )", __FUNCTION__, item->id); + + /* This string should always be allocated */ + free (item->data.alpha.edit_str); +} + +void menuitem_destroy_alpha (MenuItem *item) +{ + debug (RPT_DEBUG, "%s( item=\"%s\" )", __FUNCTION__, item->id); + + /* These strings should always be allocated */ + free (item->data.alpha.allowed_chars); + free (item->data.alpha.value); + free (item->data.alpha.edit_str); +} + +/******** MENU ITEM RESET FUNCTIONS ********/ + +void menuitem_reset (MenuItem *item) +{ + void (*func) (MenuItem *); + + debug (RPT_DEBUG, "%s( item=\"%s\" )", __FUNCTION__, item->id); + + /* First destroy type specific data */ + func = reset_table[item->type]; + if (func) + func (item); +} + +void menuitem_reset_numeric (MenuItem *item) +{ + debug (RPT_DEBUG, "%s( item=\"%s\" )", __FUNCTION__, item->id); + + item->data.numeric.edit_pos = 0; + memset ( item->data.numeric.edit_str, 0, MAX_NUMERIC_LEN); + if (item->data.numeric.minvalue < 0) { + snprintf (item->data.numeric.edit_str, MAX_NUMERIC_LEN, "%+d", item->data.numeric.value); + } else { + snprintf (item->data.numeric.edit_str, MAX_NUMERIC_LEN, "%d", item->data.numeric.value); + } +} + +void menuitem_reset_alpha (MenuItem *item) +{ + debug (RPT_DEBUG, "%s( item=\"%s\" )", __FUNCTION__, item->id); + + item->data.alpha.edit_pos = 0; + memset (item->data.alpha.edit_str, 0, item->data.alpha.maxlength+1); + strcpy (item->data.alpha.edit_str, item->data.alpha.value); +} + + +/******** MENU SCREEN BUILD FUNCTIONS ********/ + +void menuitem_build_screen (MenuItem *item, Screen *s) +{ + Widget * w; + void (*build_screen) (MenuItem *item, Screen *s); + + debug (RPT_DEBUG, "%s( item=\"%s\", screen=\"%s\" )", __FUNCTION__, item->id, s->id); + + if (!display_props) { + /* Nothing to build if no display size is known */ + report (RPT_ERR, "%s: display size unknown", __FUNCTION__ ); + return; + } + + /* First remove all widgets from the screen */ + while ( (w = screen_getfirst_widget(s)) ) { + /* We know these widgets don't have subwidgets, so we can + * easily remove them + */ + screen_remove_widget (s, w); + widget_destroy (w); + } + + /* Call type specific screen building function */ + build_screen = build_screen_table [item->type]; + if (build_screen) { + build_screen (item, s); + } else { + report (RPT_ERR, "%s: given menuitem cannot be active", __FUNCTION__); + return; + } +} + +void menuitem_build_screen_slider (MenuItem *item, Screen *s) +{ + Widget * w; + + debug (RPT_DEBUG, "%s( item=\"%s\", screen=\"%s\" )", __FUNCTION__, item->id, s->id); + + if (display_props->height >= 2 ) { + /* Only add a title if enough space... */ + w = widget_create ("text", WID_STRING, s); + screen_add_widget (s, w); + w->text = strdup(item->text); + w->x = 1; + w->y = 1; + } + + w = widget_create ("bar", WID_HBAR, s); + screen_add_widget (s, w); + w->width = display_props->width; + if (display_props->height > 2 ) { + /* This is option 1: we have enought space, so the bar and + * min/max texts can be on separate lines. + */ + w->x = 2; + w->y = display_props->height / 2 + 1; + w->length = display_props->width - 2; + } + + w = widget_create ("min", WID_STRING, s); + screen_add_widget (s, w); + w->text = NULL; + w->x = 1; + if (display_props->height > 2 ) { + w->y = display_props->height / 2 + 2; + } else { + w->y = display_props->height / 2 + 1; + } + + w = widget_create ("max", WID_STRING, s); + screen_add_widget (s, w); + w->text = NULL; + w->x = 1; + if (display_props->height > 2 ) { + w->y = display_props->height / 2 + 2; + } else { + w->y = display_props->height / 2 + 1; + } +} + +void menuitem_build_screen_numeric (MenuItem *item, Screen *s) +{ + Widget * w; + + debug (RPT_DEBUG, "%s( item=\"%s\", screen=\"%s\" )", __FUNCTION__, item->id, s->id); + + if (display_props->height >= 2 ) { + /* Only add a title if enough space... */ + w = widget_create ("text", WID_STRING, s); + screen_add_widget (s, w); + w->text = strdup(item->text); + w->x = 1; + w->y = 1; + } + + w = widget_create ("value", WID_STRING, s); + screen_add_widget (s, w); + w->text = malloc (MAX_NUMERIC_LEN); + w->x = 2; + w->y = display_props->height / 2 + 1; + + /* Only display error string if enough space... */ + if (display_props->height > 2 ) { + w = widget_create ("error", WID_STRING, s); + screen_add_widget (s, w); + w->text = strdup(""); + w->x = 1; + w->y = display_props->height; + } +} + +void menuitem_build_screen_alpha (MenuItem *item, Screen *s) +{ + Widget * w; + + debug (RPT_DEBUG, "%s( item=\"%s\", screen=\"%s\" )", __FUNCTION__, item->id, s->id); + + if (display_props->height >= 2 ) { + /* Only add a title if enough space... */ + w = widget_create ("text", WID_STRING, s); + screen_add_widget (s, w); + w->text = strdup(item->text); + w->x = 1; + w->y = 1; + } + + w = widget_create ("value", WID_STRING, s); + screen_add_widget (s, w); + w->text = malloc (item->data.alpha.maxlength+1); + w->x = 2; + w->y = display_props->height / 2 + 1; + + /* Only display error string if enough space... */ + if (display_props->height > 2 ) { + w = widget_create ("error", WID_STRING, s); + screen_add_widget (s, w); + w->text = strdup(""); + w->x = 1; + w->y = display_props->height; + } +} + +/******** MENU SCREEN UPDATE FUNCTIONS ********/ + +void menuitem_update_screen (MenuItem *item, Screen *s) +{ + void (*update_screen) (MenuItem *item, Screen *s); + + debug (RPT_DEBUG, "%s( item=\"%s\", screen=\"%s\" )", __FUNCTION__, item->id, s->id); + + /* Disable the cursor by default */ + s->cursor = CURSOR_OFF; + + /* Call type specific screen building function */ + update_screen = update_screen_table [item->type]; + if (update_screen) { + update_screen (item, s); + } else { + report (RPT_ERR, "%s: given menuitem cannot be active", __FUNCTION__); + return; + } +} + +void menuitem_update_screen_slider (MenuItem *item, Screen *s) +{ + Widget * w; + int min_len, max_len; + + debug (RPT_DEBUG, "%s( item=\"%s\", screen=\"%s\" )", __FUNCTION__, item->id, s->id); + + /* Calculate the bar position and length by filling buffers */ + min_len = strlen (item->data.slider.mintext); + max_len = strlen (item->data.slider.maxtext); + + /* And adjust the data */ + + w = screen_find_widget (s, "bar"); + if (display_props->height <= 2 ) { + /* This is option 2: we're tight on lines, so we put the bar + * and min/max texts on the same line. + */ + w->x = 1 + min_len; + w->y = display_props->height; + w->width = display_props->width - + min_len - max_len; + } + /* FUTURE: w->promille = 1000 * (item->data.slider.value - item->data.slider.minvalue) / (item->data.slider.maxvalue - item->data.slider.minvalue) */; + w->length = w->width * display_props->cellwidth + * (item->data.slider.value - item->data.slider.minvalue) + / (item->data.slider.maxvalue - item->data.slider.minvalue); + + w = screen_find_widget (s, "min"); + if (w->text) free (w->text); + w->text = strdup(item->data.slider.mintext); + + w = screen_find_widget (s, "max"); + if (w->text) free (w->text); + w->x = 1 + display_props->width - max_len; + w->text = strdup(item->data.slider.maxtext); +} + +void menuitem_update_screen_numeric (MenuItem *item, Screen *s) +{ + Widget * w; + + debug (RPT_DEBUG, "%s( item=\"%s\", screen=\"%s\" )", __FUNCTION__, item->id, s->id); + + w = screen_find_widget (s, "value"); + strcpy (w->text, item->data.numeric.edit_str); + + s->cursor = CURSOR_DEFAULT_ON; + s->cursor_x = w->x + item->data.numeric.edit_pos; + s->cursor_y = w->y; + + /* Only display error string if enough space... */ + if (display_props->height > 2 ) { + w = screen_find_widget (s, "error"); + free (w->text); + w->text = strdup (error_strs[item->data.numeric.error_code]); + } +} + +void menuitem_update_screen_alpha (MenuItem *item, Screen *s) +{ + Widget * w; + + debug (RPT_DEBUG, "%s( item=\"%s\", screen=\"%s\" )", __FUNCTION__, item->id, s->id); + + w = screen_find_widget (s, "value"); + if (item->data.alpha.password_char == 0) { + strcpy (w->text, item->data.alpha.edit_str); + } else { + memset (item->data.alpha.edit_str, 0, item->data.alpha.maxlength+1); + memset (item->data.alpha.edit_str, item->data.alpha.password_char, strlen (item->data.alpha.edit_str)); + } + + s->cursor = CURSOR_DEFAULT_ON; + s->cursor_x = w->x + item->data.alpha.edit_pos; + s->cursor_y = w->y; + + /* Only display error string if enough space... */ + if (display_props->height > 2 ) { + w = screen_find_widget (s, "error"); + free (w->text); + w->text = strdup (error_strs[item->data.alpha.error_code]); + } +} + +/******** MENU SCREEN INPUT HANDLING FUNCTIONS ********/ + +MenuResult menuitem_handle_input (MenuItem *item, MenuToken token, char * key) +{ + MenuResult (*handle_input) (MenuItem *item, MenuToken token, char * key); + + debug (RPT_DEBUG, "%s( item=\"%s\", token=%d, key=\"%s\" )", __FUNCTION__, item->id, token, key); + + /* Call type specific screen building function */ + handle_input = handle_input_table [item->type]; + if (handle_input ) { + return handle_input (item, token, key); + } else { + report (RPT_ERR, "%s: given menuitem cannot be active", __FUNCTION__); + return MENURESULT_ERROR; + } +} + +MenuResult menuitem_handle_input_slider (MenuItem *item, MenuToken token, char * key) +{ + debug (RPT_DEBUG, "%s( item=\"%s\", token=%d, key=\"%s\" )", __FUNCTION__, item->id, token, key); + + switch (token) { + case MENUTOKEN_MENU: + case MENUTOKEN_ENTER: + return MENURESULT_CLOSE; + case MENUTOKEN_UP: + item->data.slider.value = min (item->data.slider.maxvalue, + item->data.slider.value + item->data.slider.stepsize); + if (item->event_func) + item->event_func (item, MENUEVENT_PLUS); + return MENURESULT_OK; + case MENUTOKEN_DOWN: + item->data.slider.value = max (item->data.slider.minvalue, + item->data.slider.value - item->data.slider.stepsize); + if (item->event_func) + item->event_func (item, MENUEVENT_MINUS); + return MENURESULT_OK; + case MENUTOKEN_OTHER: + } + + return MENURESULT_ERROR; +} + +MenuResult menuitem_handle_input_numeric (MenuItem *item, MenuToken token, char * key) +{ + char buf1[MAX_NUMERIC_LEN]; + char buf2[MAX_NUMERIC_LEN]; + + char *format_str; + int max_len; + + /* To make life easy... */ + char *str = item->data.numeric.edit_str; + int pos = item->data.numeric.edit_pos; + int allow_signed = (item->data.numeric.minvalue < 0); + + debug (RPT_DEBUG, "%s( item=\"%s\", token=%d, key=\"%s\" )", __FUNCTION__, item->id, token, key); + + if (allow_signed) { + format_str = "%+d"; + } else { + format_str = "%d"; + } + snprintf (buf1, MAX_NUMERIC_LEN, format_str, item->data.numeric.minvalue); + snprintf (buf2, MAX_NUMERIC_LEN, format_str, item->data.numeric.maxvalue); + + max_len = max (strlen(buf1), strlen(buf2)); + + /* Clear the error */ + item->data.numeric.error_code = 0; + + switch (token) { + case MENUTOKEN_MENU: + if (pos == 0) { + return MENURESULT_CLOSE; + } + else { + /* Reset data */ + item->data.numeric.edit_pos = 0; + memset (str, 0, MAX_NUMERIC_LEN); + snprintf (str, MAX_NUMERIC_LEN, format_str, item->data.numeric.value); + } + return MENURESULT_OK; + case MENUTOKEN_ENTER: + if (str[pos] == 0) { + int value; + /* The user completed his input */ + + /* ...scan it */ + if (sscanf (str, "%d", &value) != 1) { + return MENURESULT_ERROR; + } + /* Test the value */ + if (value < item->data.numeric.minvalue + || value > item->data.numeric.maxvalue) { + /* Out of range ! + * We can't exit this screen now + */ + item->data.numeric.error_code = 1; + item->data.numeric.edit_pos = 0; + return MENURESULT_OK; + } + + /* OK, store value */ + item->data.numeric.value = value; + + /* Inform client */ + if (item->event_func) + item->event_func (item, MENUEVENT_UPDATE); + + return MENURESULT_CLOSE; + } + else { + /* The user wants to go to next digit */ + if (pos < max_len) { + item->data.numeric.edit_pos ++; + } + } + return MENURESULT_OK; + case MENUTOKEN_UP: + if (pos >= max_len) { + /* We're not allowed to add anything anymore */ + item->data.numeric.error_code = 2; + item->data.numeric.edit_pos = 0; + return MENURESULT_OK; + } + if (allow_signed && pos == 0) { + /* make negative */ + str[0] = (str[0] == '-') ? '+' : '-'; + } + else { + if (str[pos] >= '0' && str[pos] < '9') { + str[pos] ++; + } else if (str[pos] == '9') { + str[pos] = 0; + } else if (str[pos] == 0) { + str[pos] = '0'; + } + } + return MENURESULT_OK; + case MENUTOKEN_DOWN: + if (pos >= max_len) { + /* We're not allowed to add anything anymore */ + item->data.numeric.error_code = 2; + item->data.numeric.edit_pos = 0; + return MENURESULT_OK; + } + if (allow_signed && pos == 0) { + /* make negative */ + str[0] = (str[0] == '-') ? '+' : '-'; + } + else { + if (str[pos] > '0' && str[pos] <= '9') { + str[pos] --; + } else if (str[pos] == '0') { + str[pos] = 0; + } else if (str[pos] == 0) { + str[pos] = '9'; + } + } + return MENURESULT_OK; + case MENUTOKEN_OTHER: + if (pos >= max_len) { + /* We're not allowed to add anything anymore */ + item->data.numeric.error_code = 2; + item->data.numeric.edit_pos = 0; + return MENURESULT_OK; + } + /* proces numeric keys */ + if ( strlen(key) == 1 && key[0] >= '0' && key[0] <= '9') { + str[pos] = key[0]; + item->data.numeric.edit_pos ++; + } + return MENURESULT_OK; + } + return MENURESULT_ERROR; +} + +MenuResult menuitem_handle_input_alpha (MenuItem *item, MenuToken token, char * key) +{ + char * p; + char * chars = item->data.alpha.allowed_chars; + + /* To make life easy... */ + char *str = item->data.alpha.edit_str; + int pos = item->data.alpha.edit_pos; + + debug (RPT_DEBUG, "%s( item=\"%s\", token=%d, key=\"%s\" )", __FUNCTION__, item->id, token, key); + + /* Clear the error */ + item->data.alpha.error_code = 0; + + switch (token) { + case MENUTOKEN_MENU: + if (pos == 0) { + return MENURESULT_CLOSE; + } + else { + /* Reset data */ + item->data.alpha.edit_pos = 0; + memset (str, 0, item->data.alpha.maxlength+1); + strcpy (str, item->data.alpha.value); + } + return MENURESULT_OK; + case MENUTOKEN_ENTER: + if (str[item->data.alpha.edit_pos] == 0) { + /* The user completed his input */ + + /* It's not too short ? */ + if (strlen (item->data.alpha.edit_str) < item->data.alpha.minlength) { + item->data.alpha.error_code = 3; + return MENURESULT_OK; + } + + /* Store value */ + strcpy (item->data.alpha.value, item->data.alpha.edit_str); + + /* Inform client */ + if (item->event_func) + item->event_func (item, MENUEVENT_UPDATE); + + return MENURESULT_CLOSE; + } + else { + /* The user wants to go to next digit */ + if (pos < item->data.alpha.maxlength) { + item->data.alpha.edit_pos ++; + } + } + return MENURESULT_OK; + case MENUTOKEN_UP: + if (pos >= item->data.alpha.maxlength) { + /* We're not allowed to add anything anymore */ + item->data.alpha.error_code = 2; + item->data.numeric.edit_pos = 0; + return MENURESULT_OK; + } + if (str[pos] == 0) { + /* User goes past EOL */ + str[pos] = chars[0]; + } else { + /* We should have a symbol from our list */ + p = strchr (chars, str[pos]); + if (p != NULL) { + str[pos] = * (++p); /* next symbol on list */ + /* Might be 0 now */ + } else { + str[pos] = 0; + } + } + return MENURESULT_OK; + case MENUTOKEN_DOWN: + if (pos >= item->data.alpha.maxlength) { + /* We're not allowed to add anything anymore */ + item->data.alpha.error_code = 2; + item->data.numeric.edit_pos = 0; + return MENURESULT_OK; + } + if (str[pos] == 0) { + /* User goes past EOL */ + str[pos] = chars[strlen(chars)-1]; + } else { + /* We should have a symbol from our list */ + p = strchr (chars, str[pos]); + if (p != NULL) { + if (p == chars) { + str[pos] = 0; /* Go to EOL */ + } else { + str[pos] = * (--p); /* next symbol on list */ + } + } else { + str[pos] = 0; + } + } + return MENURESULT_OK; + case MENUTOKEN_OTHER: + if (pos >= item->data.alpha.maxlength) { + /* We're not allowed to add anything anymore */ + item->data.alpha.error_code = 2; + item->data.numeric.edit_pos = 0; + return MENURESULT_OK; + } + /* proces other keys */ + if ( strlen(key) == 1 && key[0] >= ' ' && key[0] <= 'Z') { + str[pos] = key[0]; + item->data.alpha.edit_pos ++; + } + return MENURESULT_OK; + } + return MENURESULT_ERROR; +} + diff --git a/server/menuitem.h b/server/menuitem.h new file mode 100644 index 0000000..9d13220 --- /dev/null +++ b/server/menuitem.h @@ -0,0 +1,277 @@ +/* + * menuitem.h + * This file is part of LCDd, the lcdproc server. + * + * This file is released under the GNU General Public License. Refer to the + * COPYING file distributed with this package. + * + * Copyright (c) 1999, William Ferrell, Scott Scriven + * + * Defines all the menuitem data and actions. + * + * There are a few different menuitems: + * - action + * - checkbox (on/off and optionally open) + * - slider (the user can increase/decrease a value) + * - numeric input + * - alphanumeric input (in short: alpha) + * - menu (a menu is a menuitem itself too) + * + * The slider, numeric & string input and menu have their own screen, + * that comes to front when the items are selected. + * One menuitem is in a different file: Menu data is in menu,h. + */ + +#ifndef MENUITEM_H +#define MENUITEM_H + +#include "shared/LL.h" + +#include "screen.h" + +#ifndef bool +# define bool short +# define true 1 +# define false 0 +#endif + +#define max(a,b) (((a) > (b)) ? (a) : (b)) + +/********************************************************************* + * Data definitions of the menustuff + */ + +#define NUM_ITEMTYPES 7 + +typedef enum MenuItemType { /* These values are used in the */ + MENUITEM_MENU = 0, /* function tables in menuitem.c ! */ + MENUITEM_ACTION = 1, + MENUITEM_CHECKBOX = 2, + MENUITEM_RING = 3, + MENUITEM_SLIDER = 4, + MENUITEM_NUMERIC = 5, + MENUITEM_ALPHA = 6, +} MenuItemType; + +typedef enum CheckboxValue { + CHECKBOX_OFF = 0, CHECKBOX_ON, CHECKBOX_GRAY +} CheckboxValue; + +/* Recognized input token codes */ +typedef enum MenuToken { + MENUTOKEN_MENU, + MENUTOKEN_ENTER, + MENUTOKEN_UP, + MENUTOKEN_DOWN, + MENUTOKEN_OTHER +} MenuToken; + +/* Return codes from an input handler */ +typedef enum MenuResult { + MENURESULT_ERROR = -1, /* Something has gone wrong */ + MENURESULT_OK = 0, /* Token handled OK */ + MENURESULT_ENTER, /* Token handled OK, enter the selected + menuitem now */ + MENURESULT_CLOSE, /* Token handled OK, close the current + menuitem now */ + MENURESULT_QUIT /* Token handled OK, close ALL menus now */ +} MenuResult; + +/* Events caused by a menuitem */ +typedef enum MenuEventType { + MENUEVENT_ENTER, /* Item has been entered + (menu opened, action chosen) + Can be used to trigger one-time update of + the items in a menu. */ + MENUEVENT_UPDATE, /* Item has been modified + (checkbox, numeric, alphanumeric) */ + MENUEVENT_PLUS, /* Item has been modified in positive direction + (slider moved) */ + MENUEVENT_MINUS /* Item has been modified in negative direction + (slider moved) */ +} MenuEventType; + +#define MenuEventFunc(f) int (f) (struct MenuItem *item, MenuEventType event) + +/* I've used a union in the struct below. Why? And why not for Widget? + * + * There are different types of menuitems. There are also types of widgets. + * Menuitems have, just like widgets, different datafields per subtype. + * The difference is that menuitems have, unlike widgets _many__different_ + * attributes. Widgets share many attributes like x, y, text. + * The code would become unreadable if we used the 'widget way', or it would + * get large if we define datafields that we use for only one type of + * menuitem. (Joris) + */ +typedef struct MenuItem { + MenuItemType type; /* Type as defined above */ + char *id; /* Internal name for client supplied menus */ + struct MenuItem *parent; /* Parent of this menuitem */ + MenuEventFunc (*event_func); + /* Defines event_func to be an event function */ + char *text; /* Visible name of the item */ + union data { + struct menu { + int selector_pos; /* At what menuitem is the + selector (0 for first) */ + int scroll; /* How much has the menu been + scrolled down */ + void *association; /* To associate an object + with this menu */ + LinkedList *contents; /* What's in this menu */ + } menu; + struct action { + bool close_menu; /* Close active menu ? */ + bool quit_menu; /* Close entire menu ? */ + } action; + struct checkbox { + bool allow_gray; /* Is CHECKBOX_GRAY allowed ? */ + CheckboxValue value; /* Current value */ + } checkbox; + struct ring { + LinkedList *strings; /* The selectable strings */ + int value; /* Current index */ + } ring; + struct slider { + char *mintext; /* Text at minimal value */ + char *maxtext; /* Text at minimal value */ + int minvalue; + int maxvalue; + int stepsize; + int value; /* Current value */ + } slider; + struct numeric { + int maxvalue; + int minvalue; + short allowed_decimals; /* Number of numbers behind dot */ + int value; /* Current value */ + char *edit_str; /* Value while being edited */ + short edit_pos; /* Position while editing */ + short error_code; + } numeric; + struct alpha { + char password_char; /* For passwords */ + short minlength; + short maxlength; + char *allowed_chars; /* Allowed characters */ + char *value; /* Current value */ + char *edit_str; /* Value while being edited */ + short edit_pos; /* Position while editing */ + short error_code; + } alpha; + } data; +} MenuItem; + + +/********************************************************************* + * Functions to use the menustuff + */ + +MenuItem *menuitem_create (MenuItemType type, char *id, + MenuEventFunc(*event_func), char *text); +/* YOU SHOULD NOT CALL THIS FUNCTION BUT THE TYPE SPECIFIC ONE INSTEAD */ + +/* For all constructor functions below the following: + * + * id: internal name of the item. Never visible. String will be + * copied. + * event_func: the event function that should be called upon actions on this + * item. see event.h. + * text: the displayed text. + * + * All strings will be copied ! + * + * Return value: the new item, of NULL on error. + * + * To create a Menu (which is also an ItemType), call menu_create. + * + */ +MenuItem *menuitem_create_action (char *id, MenuEventFunc(*event_func), + char *text, bool close_menu, bool quit_menu); +/* Creates a an action item (a string only). + * Generated events: MENUEVENT_ENTER when user selects the item. + */ + +MenuItem *menuitem_create_checkbox (char *id, MenuEventFunc(*event_func), + char *text, bool allow_gray, bool value); +/* Creates a checkbox. + * Generated events: MENUEVENT_UPDATE when user changes value (immediately). + */ + +MenuItem *menuitem_create_ring (char *id, MenuEventFunc(*event_func), + char *text, char *strings, short value); +/* Creates a ring with the given string, separated by tabs. + * value is the (initial) index in the strings. + * eg: if strings="abc\tdef" the value=1 means that "def" is selected. + * Generated events: MENUEVENT_UPDATE when user changes value (immediately). + */ + +MenuItem *menuitem_create_slider (char *id, MenuEventFunc(*event_func), + char *text, char *mintext, char *maxtext, + int minvalue, int maxvalue, int stepsize, int value); +/* Creates a slider with the given min and max values. + * If the display is big enough the mintext and maxtext will be placed + * at the end positions of the slider. + * You can set the step size. Make it 0 to disable the automatic value chaning, + * and update the value yourself. + * Generated events: MENUEVENT_ENTER upon entering this item, + * MENUEVENT_PLUS, MENUEVENT_MINUS when slider is moved (immediately). + */ + +MenuItem *menuitem_create_numeric (char *id, MenuEventFunc(*event_func), + char *text, int minvalue, int maxvalue, int value); +/* Creates a numeric value box. + * Value can range from minvalue to maxvalue. + * Generated events: MENUEVENT_ENTER upon entering this item, + * MENUEVENT_UPDATE when user finishes the value (no immediate update). + */ + +MenuItem *menuitem_create_alpha (char *id, MenuEventFunc(*event_func), + char *text, char password_char, short minlength, short maxlength, + bool allow_caps, bool allow_noncaps, bool allow_numbers, + char *allowed_extra, char *value); +/* Creates a string value box. + * Value should have given minimal and maximal length. You can set whether + * caps, non-caps and numbers are allowed. Also you can alow other characters. + * If password char is non-zero, you will only see this char, not the actual + * input. + * Generated events: MENUEVENT_ENTER upon entering this item, + * MENUEVENT_UPDATE when user finishes the value (no immediate update). + */ + +void menuitem_destroy (MenuItem *item); +/* Deletes item from memory. + * All allocated extra data (like strings) will be freed. + */ + +static inline MenuItem *menuitem_get_parent (MenuItem *item) +{ + return item->parent; +} + +void menuitem_reset (MenuItem *item); +/* Resets the item to the initial state. + * You should call menuitem_update after this to see the effects. + * This call is useless on items that have immediate effect, like a slider. + * Those items do not keep temporary data. + */ + +void menuitem_build_screen (MenuItem *item, Screen *s); +/* Builds the selected menuitem on screen using widgets. + * Should be re-called if menuitem data has been changed. + * There are a few (logical) exceptions to this: + * - the values + * - the menu scroll and menu index + */ + +void menuitem_update_screen (MenuItem *item, Screen *s); +/* Updates the widgets of the selected menuitem + * Fills all widget attributes with the corrrect values. + */ + +MenuResult menuitem_handle_input (MenuItem *item, MenuToken token, char * key); +/* Does something with the given input. + * key is only used if token is MENUTOKEN_OTHER. + */ + +#endif diff --git a/server/menuscreens.c b/server/menuscreens.c index 8aeb68b..91c3c78 100644 --- a/server/menuscreens.c +++ b/server/menuscreens.c @@ -9,11 +9,354 @@ * 2002, Joris Robijn * * - * Creates the server menu screen (s). - * Handles the keypresses for this screen. + * Creates the server menu screen(s) and creates the menus that should be + * displayed on this screen. + * It also handles its keypresses and converts them to menu tokens for + * easier processing. + * + * NOTE: menuscreens.c does not know whether a menuitem is displayed INSIDE + * a menu or on a separate SCREEN, for flexibility. * */ +#include +#include +#include "screen.h" +#include "screenlist.h" #include "menuscreens.h" +#include "configfile.h" +#include "shared/report.h" +#include "input.h" +#include "driver.h" +#include "drivers.h" +/* Next include files are needed for settings that we can modify */ +#include "render.h" + +char * menu_key; +char * enter_key; +char * up_key; +char * down_key; + +Screen * menuscreen; +MenuItem * active_menuitem; +Menu * main_menu; +Menu * screens_menu; + + +void menuscreen_create_menu (); +MenuEventFunc (menuscreen_heartbeat); +MenuEventFunc (menuscreen_backlight); +MenuEventFunc (menuscreen_contrast); +MenuEventFunc (menuscreen_brightness); +MenuEventFunc (menuscreen_screens); + +int init_menu() +{ + active_menuitem = NULL; + + debug (RPT_DEBUG, "%s()", __FUNCTION__); + + /* Get keys from config file */ + menu_key = strdup (config_get_string ("menu", "MenuKey", 0, "Menu")); + enter_key = strdup (config_get_string ("menu", "EnterKey", 0, "Enter")); + up_key = strdup (config_get_string ("menu", "UpKey", 0, "Up")); + down_key = strdup (config_get_string ("menu", "DownKey", 0, "Down")); + + /* Now reserve keys */ + input_reserve_key (menu_key, true, NULL); + input_reserve_key (enter_key, false, NULL); + input_reserve_key (up_key, false, NULL); + input_reserve_key (down_key, false, NULL); + + /* Create screen */ + menuscreen = screen_create ("_menu_screen", NULL); + + /* Build menu */ + menuscreen_create_menu (); + + return 0; +} + +bool is_menu_key (char * key) +{ + if (strcmp (key, menu_key) == 0) + return true; + else + return false; +} + +void menuscreen_key_handler (char *key) +{ + char token = 0; + MenuResult res; + + debug (RPT_DEBUG, "%s( \"%s\" )", __FUNCTION__, key); + + if (strcmp (key, menu_key) == 0) { + token = MENUTOKEN_MENU; + } + else if (strcmp (key, enter_key) == 0) { + token = MENUTOKEN_ENTER; + } + else if (strcmp (key, up_key) == 0) { + token = MENUTOKEN_UP; + } + else if (strcmp (key, down_key) == 0) { + token = MENUTOKEN_DOWN; + } + else { + token = MENUTOKEN_OTHER; + } + + /* Is the menu already active ? */ + if (!active_menuitem) { + debug (RPT_DEBUG, "%s: Activating menu screen", __FUNCTION__); + active_menuitem = main_menu; + menuitem_build_screen (active_menuitem, menuscreen); + menuitem_reset (active_menuitem); + menuitem_update_screen (active_menuitem, menuscreen); + + if (screenlist_add (menuscreen) < 0) { + report (RPT_ERR, "%s: Error queueing menu screen", __FUNCTION__); + } + /* TODO: raise it ! */ + + return; + } + + res = menuitem_handle_input (active_menuitem, token, key); + + switch (res) { + case MENURESULT_ERROR: + report (RPT_ERR, "%s: Error from menu_handle_input", __FUNCTION__); + break; + case MENURESULT_OK: + /* Nothing extra to be done */ + break; + case MENURESULT_ENTER: + /* Enter the selected menuitem + * Note: this is not for checkboxes etc that don't have their + * own screen. The menu_handle_input function should do + * things like toggling checkboxes ! + */ + debug (RPT_DEBUG, "%s: Entering subitem", __FUNCTION__); + active_menuitem = menu_get_current_item (active_menuitem); + menuitem_build_screen (active_menuitem, menuscreen); + menuitem_reset (active_menuitem); + break; + case MENURESULT_CLOSE: + debug (RPT_DEBUG, "%s: Closing item", __FUNCTION__); + active_menuitem = menuitem_get_parent (active_menuitem); + if (active_menuitem) { + /* We were in at least second level menu */ + menuitem_build_screen (active_menuitem, menuscreen); + break; + } + /* If first level menu, quit menu now + * Therefor no break; now. + */ + case MENURESULT_QUIT: + debug (RPT_DEBUG, "%s: Closing menu screen", __FUNCTION__); + active_menuitem = NULL; + /* TODO: send menu to backgr */ + if (screenlist_remove (menuscreen) < 0) { + report (RPT_ERR, "%s: Error unqueueing menu screen", __FUNCTION__); + } + break; + } + if (active_menuitem) { + menuitem_update_screen (active_menuitem, menuscreen); + } +} + +void menuscreen_create_menu () +{ + Menu * options_menu; + Menu * driver_menu; + MenuItem * checkbox; + MenuItem * slider; + Driver * driver; + + MenuItem * test_item; + Menu * test_menu; + + debug (RPT_DEBUG, "%s()", __FUNCTION__); + + main_menu = menu_create ("mainmenu", NULL, "LCDproc Menu", NULL); + + options_menu = menu_create ("options", NULL, "Options", NULL); + menu_add_item (main_menu, options_menu); + + screens_menu = menu_create ("screens", menuscreen_screens, "Screens", NULL); + menu_add_item (main_menu, screens_menu); + + checkbox = menuitem_create_checkbox ("heartbeat", menuscreen_heartbeat, "Heartbeat", true, heartbeat); + menu_add_item (options_menu, checkbox); + + checkbox = menuitem_create_checkbox ("backlight", menuscreen_backlight, "Backlight", true, backlight); + menu_add_item (options_menu, checkbox); + + for (driver = drivers_getfirst(); driver; driver = drivers_getnext()) { + int contrast_avail = 0; + int brightness_avail = 0; + + contrast_avail = (driver->get_contrast && driver->set_contrast); + brightness_avail = (driver->get_brightness && driver->set_brightness); + + if (contrast_avail || brightness_avail) { + driver_menu = menu_create (driver->name, NULL, driver->name, driver); + menu_add_item (options_menu, driver_menu); + if (contrast_avail) { + slider = menuitem_create_slider ("contrast", menuscreen_contrast, "Contrast", "min", "max", 0, 1000, 100, 500); + menu_add_item (driver_menu, slider); + } + if (brightness_avail) { + slider = menuitem_create_slider ("onbrightness", menuscreen_brightness, "On Brightness", "min", "max", 0, 1000, 100, 500); + menu_add_item (driver_menu, slider); + + slider = menuitem_create_slider ("offbrightness", menuscreen_brightness, "Off Brightness", "min", "max", 0, 1000, 100, 500); + menu_add_item (driver_menu, slider); + } + } + } + test_menu = menu_create ("test", NULL, "Test menu", NULL); + menu_add_item (main_menu, test_menu); + + test_item = menuitem_create_action ("", NULL, "Action", false, false); + menu_add_item (test_menu, test_item); + test_item = menuitem_create_action ("", NULL, "Action,closing", true, false); + menu_add_item (test_menu, test_item); + test_item = menuitem_create_action ("", NULL, "Action,quiting", true, true); + menu_add_item (test_menu, test_item); + + test_item = menuitem_create_checkbox ("", NULL, "Checkbox", false, false); + menu_add_item (test_menu, test_item); + test_item = menuitem_create_checkbox ("", NULL, "Checkbox, gray", true, false); + menu_add_item (test_menu, test_item); + + test_item = menuitem_create_ring ("", NULL, "Ring", "ABC\tDEF\t01234567890\tOr a very long string that will not fit on any display", 1); + menu_add_item (test_menu, test_item); + + test_item = menuitem_create_slider ("", NULL, "Slider", "mintext", "maxtext", -20, 20, 1, 0); + menu_add_item (test_menu, test_item); + test_item = menuitem_create_slider ("", NULL, "Slider,step=5", "mintext", "maxtext", -20, 20, 5, 0); + menu_add_item (test_menu, test_item); + + test_item = menuitem_create_numeric ("", NULL, "Numeric", 1, 365, 15); + menu_add_item (test_menu, test_item); + test_item = menuitem_create_numeric ("", NULL, "Numeric,signed", -20, +20, 15); + menu_add_item (test_menu, test_item); + + test_item = menuitem_create_alpha ("", NULL, "Alpha", 0, 3, 12, true, true, true, ".-+@", "LCDproc-v0.5"); + menu_add_item (test_menu, test_item); + test_item = menuitem_create_alpha ("", NULL, "Alpha, caps only", 0, 3, 12, true, false, false, "-", "LCDPROC"); + menu_add_item (test_menu, test_item); +} + +MenuEventFunc (menuscreen_heartbeat) +{ + debug (RPT_DEBUG, "%s( item=%s, event=%d )", __FUNCTION__, item->id, event); + + if (event == MENUEVENT_UPDATE) { + /* Set heartbeat setting */ + heartbeat = item->data.checkbox.value; + report (RPT_INFO, "Menu: set heartbeat to %d", + item->data.checkbox.value); + } + return 0; +} + +MenuEventFunc (menuscreen_backlight) +{ + debug (RPT_DEBUG, "%s( item=%s, event=%d )", __FUNCTION__, item->id, event); + + if (event == MENUEVENT_UPDATE) + { + /* Set backlight setting */ + backlight = item->data.checkbox.value; + report (RPT_INFO, "Menu: set backlight to %d", + item->data.checkbox.value); + } + return 0; +} + +MenuEventFunc (menuscreen_contrast) +{ + debug (RPT_DEBUG, "%s( item=%s, event=%d )", __FUNCTION__, item->id, event); + + /* This function can be called by one of several drivers that + * support contrast ! + * We need to check the menu association to see which driver. */ + if (event == MENUEVENT_MINUS || event == MENUEVENT_PLUS) { + + /* Determinte the driver */ + Driver * driver = item->parent->data.menu.association; + + driver->set_contrast (driver, item->data.slider.value); + //item->data.slider.value = driver->get_contrast (driver); + report (RPT_INFO, "Menu: set contrast of [%.40s] to %d", + item->data.checkbox.value); + } + return 0; +} + +MenuEventFunc (menuscreen_brightness) +{ + debug (RPT_DEBUG, "%s( item=%s, event=%d )", __FUNCTION__, item->id, event); + + /* This function can be called by one of several drivers that + * support contrast ! + * We need to check the menu association to see which driver. */ + if (event == MENUEVENT_MINUS || event == MENUEVENT_PLUS) { + + /* Determinte the driver */ + Driver * driver = item->parent->data.menu.association; + + if ( strcmp (item->id, "onbrightness") == 0) { + driver->set_brightness (driver, BACKLIGHT_ON, item->data.slider.value); + } + else if ( strcmp (item->id, "offbrightness") == 0) { + driver->set_brightness (driver, BACKLIGHT_OFF, item->data.slider.value); + } + } + return 0; +} + +MenuEventFunc (menuscreen_screens) +{ + debug (RPT_DEBUG, "%s( item=%s, event=%d )", __FUNCTION__, item->id, event); + + if (event == MENUEVENT_ENTER) { + /* We are entering this menu */ + Screen * s; + Menu * m; + MenuItem * mi; + + /* Clean the screens menu */ + menu_destroy_all_items (screens_menu); + + /* Read list of screens */ + for (s = LL_GetFirst(screenlist_getlist()); s; s = LL_GetNext(screenlist_getlist())) { + + /* TODO: don't display screens that have _ at start of id */ + + m = menu_create (s->id, NULL, s->name?s->name:s->id, s); + menu_add_item (screens_menu, m); + + mi = menuitem_create_action ("", NULL, "To Front", true, true); + menu_add_item (m, mi); + + mi = menuitem_create_checkbox ("", NULL, "Visible", false, true); + menu_add_item (m, mi); + + mi = menuitem_create_numeric ("", NULL, "Duration", 2, 3600, s->duration); + menu_add_item (m, mi); + + mi = menuitem_create_numeric ("", NULL, "Priority", -1, 4, s->priority); + menu_add_item (m, mi); + } + } + return 0; +} diff --git a/server/menuscreens.h b/server/menuscreens.h index dac8f23..525acda 100644 --- a/server/menuscreens.h +++ b/server/menuscreens.h @@ -15,9 +15,20 @@ #define MENUSCREENS_H #include "menu.h" +#include "screen.h" -MenuEventFuncResult menuscreen_key_handler (); -/* This handler should be called by the real handler for the event */ +extern Screen * menuscreen; + +int init_menu(); + +bool is_menu_key (char * key); +/* This function indicates to the input part whether this key was the + * reserved menu key. + */ + +void menuscreen_key_handler (char *key); +/* This handler handles the keypresses for the menu. + */ #endif diff --git a/server/render.c b/server/render.c index 1180fbb..e17ef55 100644 --- a/server/render.c +++ b/server/render.c @@ -39,8 +39,9 @@ #include "render.h" int heartbeat = HEARTBEAT_OPEN; +int heartbeat_fallback = HEARTBEAT_ON; /* If no heartbeat setting has been set at all */ int backlight = BACKLIGHT_OPEN; -int backlight_state = BACKLIGHT_OPEN; +int backlight_fallback = BACKLIGHT_ON; /* If no backlight setting has been set at all */ int backlight_brightness = 255; int backlight_off_brightness = 0; int output_state = 0; @@ -55,9 +56,9 @@ int draw_screen (Screen * s, int timer) { static Screen * old_s = NULL; - int tmp = 0, tmp_state = 0; + int tmp_state = 0; - report(RPT_INFO, "draw_screen( screen=\"%.40s\", timer=%d ) ==== START RENDERING ====", s->name, timer ); + report(RPT_INFO, "draw_screen( screen=\"%.40s\", timer=%d ) ==== START RENDERING ====", s->id, timer ); reset = 1; @@ -73,52 +74,41 @@ draw_screen (Screen * s, int timer) /* FIXME drivers_backlight -- * - * This should be in a separate function altogether. - * Perhaps several: drivers_backlight_off, drivers_backlight_on, - * drivers_backlight_brightness, drivers_backlight_flash ... - * - * If the screen's backlight_state isn't set (default) then we + * If the screen's backlight isn't set (default) then we * inherit the backlight state from the parent client. This allows * the client to override it's childrens settings. + * The server can also override the clients and screens settings. */ - if (s->backlight == BACKLIGHT_OPEN) { - if (s->client) tmp_state = s->client->backlight; - } else { + if (backlight != BACKLIGHT_OPEN) { + tmp_state = backlight; + } else if (s->client && s->client->backlight != BACKLIGHT_OPEN) { + tmp_state = s->client->backlight; + } else if (s->backlight != BACKLIGHT_OPEN) { tmp_state = s->backlight; + } else { + tmp_state = backlight_fallback; } /* Set up backlight to the correct state... */ /* NOTE: dirty stripping of other options... */ - switch (tmp_state & 1) { - case BACKLIGHT_OFF: - drivers_backlight (BACKLIGHT_OFF); - break; - /* Backlight on (easy) */ - case BACKLIGHT_ON: - drivers_backlight (BACKLIGHT_ON); - break; - default: - /* Backlight flash: check timer and flip backlight as appropriate */ - if (tmp_state & BACKLIGHT_FLASH) { - tmp = (!((timer & 7) == 7)); - if (tmp_state & 1) - drivers_backlight (tmp ? backlight_brightness : backlight_off_brightness); - /*drivers_backlight(backlight_brightness * (!((timer&7) == 7))); */ - else - drivers_backlight (!tmp ? backlight_brightness : backlight_off_brightness); - /*drivers_backlight(backlight_brightness * ((timer&7) == 7)); */ - - /* Backlight blink: check timer and flip backlight as appropriate */ - } else if (tmp_state & BACKLIGHT_BLINK) { - tmp = (!((timer & 14) == 14)); - if (tmp_state & 1) - drivers_backlight (tmp ? backlight_brightness : backlight_off_brightness); - /*drivers_backlight(backlight_brightness * (!((timer&14) == 14))); */ - else - drivers_backlight (!tmp ? backlight_brightness : backlight_off_brightness); - /*drivers_backlight(backlight_brightness * ((timer&14) == 14)); */ - } - break; + /* Backlight flash: check timer and flip backlight as appropriate */ + if (tmp_state & BACKLIGHT_FLASH) { + drivers_backlight ( + ( + (tmp_state & BACKLIGHT_ON) + ^ ((timer & 7) == 7) + ) ? BACKLIGHT_ON : BACKLIGHT_OFF ); + /* Backlight blink: check timer and flip backlight as appropriate */ + } + else if (tmp_state & BACKLIGHT_BLINK) { + drivers_backlight ( + ( + (tmp_state & BACKLIGHT_ON) + ^ ((timer & 14) == 14) + ) ? BACKLIGHT_ON : BACKLIGHT_OFF ); + } else { + /* Simple: Only send lowest bit then...*/ + drivers_backlight (tmp_state & BACKLIGHT_ON); } /* Output ports from LCD - outputs depend on the current screen */ @@ -127,24 +117,21 @@ draw_screen (Screen * s, int timer) /* Draw a frame... */ draw_frame (s->widgetlist, 'v', 0, 0, display_props->width, display_props->height, s->width, s->height, (((s->duration / s->height) < 1) ? 1 : (s->duration / s->height)), timer); + /* Set the cursor */ + drivers_cursor (s->cursor_x, s->cursor_y, s->cursor); + /*debug(RPT_DEBUG, "draw_screen done"); */ - if (heartbeat) { - drivers_heartbeat(s->heartbeat); - /*if ((s->heartbeat == HEART_ON) || heartbeat == HEART_ON) { */ - /* Set this to pulsate like a real heart beat... */ - /* (binary is fun... :) */ - /* drivers_heartbeat (); */ - /*drivers_icon (!((timer + 4) & 5), 0); */ - /*drivers_chr (display_props->width, 1, 0); */ - /*} */ - /* else */ - /* This seems unnecessary... heartbeat is nicer... */ - /* if ((s->heartbeat == HEART_OPEN) && heartbeat != HEART_OFF) { */ - /* char *phases = "-\\|/"; */ - /* drivers_chr (ldisplay_props->width, 1, phases[timer & 3]); */ - /* } */ + if (heartbeat != HEARTBEAT_OPEN) { + tmp_state = heartbeat; + } else if (s->client && s->client->heartbeat != HEARTBEAT_OPEN) { + tmp_state = s->client->heartbeat; + } else if (s->heartbeat != HEARTBEAT_OPEN) { + tmp_state = s->heartbeat; + } else { + tmp_state = heartbeat_fallback; } + drivers_heartbeat(tmp_state); /* flush display out, frame and all... */ drivers_flush (); @@ -307,7 +294,7 @@ draw_frame (LinkedList * list, break; case WID_ICON: drivers_icon (w->x, w->y, w->length); - + break; case WID_TITLE: /* FIXME: Doesn't work quite right in frames...*/ if (!w->text) @@ -316,14 +303,12 @@ draw_frame (LinkedList * list, break; drivers_icon (w->x + left, w->y + top, ICON_BLOCK_FILLED); - drivers_icon (w->x + 1 + left, w->y + top, ICON_BLOCK_FILLED); + drivers_icon (w->x + left + 1, w->y + top, ICON_BLOCK_FILLED); - str[0] = ' '; length = strlen (w->text); if (length <= vis_width - 6) { - memcpy (str + 1, w->text, length); - str[length + 1] = ' '; - x = length + 4; + strcpy (str, w->text); + x = length + 5; } else /* Scroll the title, if it doesn't fit...*/ { speed = 1; @@ -339,7 +324,6 @@ draw_frame (LinkedList * list, screenlist_action = 0; } x %= (length); - x -= 3; if (x < 0) x = 0; if (x > length - (vis_width - 6)) @@ -349,18 +333,17 @@ draw_frame (LinkedList * list, { x = (length - (vis_width - 6)) - x; } - strncpy (str + 1, w->text + x, (vis_width - 6)); - str[vis_width - 5] = ' '; - x = vis_width - 2; + strncpy (str, w->text + x, (vis_width - 6)); + x = vis_width - 1; + str[vis_width-6] = 0; } - str[vis_width-4] = 0; - drivers_string (w->x + 2 + left, w->y + top, str); + drivers_string (w->x + 3 + left, w->y + top, str); - for (; xx + x + left, w->y + top, ICON_BLOCK_FILLED); + for (; x<=vis_width; x++) { + drivers_icon (w->x + x - 1 + left, w->y + top, ICON_BLOCK_FILLED); } - + break; case WID_SCROLLER: /* FIXME: doesn't work in frames...*/ { diff --git a/server/render.h b/server/render.h index b5835eb..c793880 100644 --- a/server/render.h +++ b/server/render.h @@ -36,7 +36,6 @@ extern int heartbeat; extern int backlight; -extern int backlight_state; extern int backlight_brightness; extern int backlight_off_brightness; extern int output_state; diff --git a/server/screen.c b/server/screen.c index 2a70ae8..34bb3ba 100644 --- a/server/screen.c +++ b/server/screen.c @@ -65,6 +65,9 @@ screen_create (char * id, Client * client) s->timeout = default_timeout; /*ignored unless greater than 0.*/ s->backlight = BACKLIGHT_OPEN; /*Lets the screen do it's own*/ /*or do what the client says.*/ + s->cursor = CURSOR_OFF; + s->cursor_x = 1; + s->cursor_y = 1; s->widgetlist = LL_new (); if (!s->widgetlist) { diff --git a/server/serverscreens.c b/server/serverscreens.c index 7780c54..b935c98 100644 --- a/server/serverscreens.c +++ b/server/serverscreens.c @@ -36,8 +36,6 @@ char one[256] = ""; char two[256] = ""; char three[256] = ""; -#define WidgetString(w,a,b,t) {(w)->x=(a);(w)->y=(b);(w)->text=(t);} - int server_screen_init () { @@ -69,25 +67,23 @@ server_screen_init () } /* Now, initialize all the widgets...*/ - if ((w = screen_find_widget (server_screen, "title")) != NULL) { - w->text = title; - } else - report (RPT_ERR, "server_screen_init: Can't find title"); + w = screen_find_widget (server_screen, "title"); + w->text = strdup (title); - if ((w = screen_find_widget (server_screen, "one")) != NULL) - WidgetString(w,1,2,one) - else - report (RPT_ERR, "server_screen_init: Can't find widget one"); + w = screen_find_widget (server_screen, "one"); + w->x = 1; + w->y = 2; + w->text = strdup (one); - if ((w = screen_find_widget (server_screen, "two")) != NULL) - WidgetString(w,1,3,two) - else - report (RPT_ERR, "server_screen_init: Can't find widget two"); + w = screen_find_widget (server_screen, "two"); + w->x = 1; + w->y = 3; + w->text = strdup (two); - if ((w = screen_find_widget (server_screen, "three")) != NULL) - WidgetString(w,1,4,three) - else - report (RPT_ERR, "server_screen_init: Can't find widget three"); + w = screen_find_widget (server_screen, "three"); + w->x = 1; + w->y = 4; + w->text = strdup (three); /* And enqueue the screen*/ screenlist_add (server_screen); @@ -97,20 +93,6 @@ server_screen_init () return 0; } -static int -screen_count (Client * c) { - int n; - - n = 0; - LL_Rewind (c->screenlist); - do { - if (LL_Get (c->screenlist) != NULL) - n++; - } while (LL_Next (c->screenlist) == 0); - - return n; -} - int update_server_screen (int timer) { @@ -131,27 +113,6 @@ update_server_screen (int timer) num_screens += client_screen_count(c); } -/* - if (c) { - num_clients++; - num_screens += client_screen_count(c); - - num_screens += screen_count(c); -*/ - -/* LL_Rewind (c->data->screenlist); */ -/* do { */ -/* s = LL_Get (c->data->screenlist); */ -/* if (s) { */ -/* num_screens++; */ -/* } */ -/* } while (LL_Next (c->data->screenlist) == 0); */ - -/* - } - } -*/ - /* Format strings for the appropriate size display... */ if (display_props->height >= 3) { snprintf (one, sizeof(one), "Clients: %i", num_clients); diff --git a/server/sock.c b/server/sock.c index 31e9efd..779856a 100644 --- a/server/sock.c +++ b/server/sock.c @@ -269,6 +269,10 @@ sock_close_all () { int fd; + return 0; + /* Do we need to do this ? Clients_shutdown already closes all + client connections ... */ + report (RPT_INFO, "sock_close_all()"); for (fd = 0; fd < FD_SETSIZE; fd++) { diff --git a/server/widget.c b/server/widget.c index 77173db..a74e110 100644 --- a/server/widget.c +++ b/server/widget.c @@ -6,9 +6,10 @@ * COPYING file distributed with this package. * * Copyright (c) 1999, William Ferrell, Scott Scriven + * 2002, Joris Robijn * * - * Does widget management + * Does all actions on widgets * */ @@ -65,9 +66,6 @@ struct icontable { {0,NULL} }; -//static widget *widget_finder (LinkedList * list, char *id); -//static int widget_remover (LinkedList * list, widget * w); - Widget * widget_create (char *id, WidgetType type, Screen * screen) @@ -128,14 +126,8 @@ widget_destroy (Widget * w) if (w->id) free (w->id); - /*debug(RPT_DEBUG, "widget_destroy: id...");*/ if (w->text) free (w->text); - /*debug(RPT_DEBUG, "widget_destroy: text...");*/ - - if (w->type == WID_FRAME) { - /* TODO: create a screen for the frame widget */ - } /* Free subscreen of frame widget too */ if (w->type == WID_FRAME) { @@ -143,7 +135,6 @@ widget_destroy (Widget * w) } free (w); - /*debug(RPT_DEBUG, "widget_destroy: widget...");*/ return 0; } diff --git a/shared/LL.c b/shared/LL.c index 1f66fbd..3ef3bdc 100644 --- a/shared/LL.c +++ b/shared/LL.c @@ -743,6 +743,30 @@ LL_Find (LinkedList * list, int compare (void *, void *), void *value) return NULL; } +////////////////////////////////////////////////////////////////////// +// Array like... +// Goes to the nth list item, and returns the +// data found there. +// +void * +LL_GetByIndex (LinkedList * list, int index) +{ + LL_node *node; + int num = 0; + + if (!list) + return NULL; + if (index<0) + return NULL; + + for (node = list->head.next; node != &list->tail; node = node->next) { + if (num == index) + return node->data; + num ++; + } + return NULL; // got past the end +} + ////////////////////////////////////////////////////////////////////// // Sorts the list, then rewinds it... // diff --git a/shared/LL.h b/shared/LL.h index 67bdfe0..fa5a1cf 100644 --- a/shared/LL.h +++ b/shared/LL.h @@ -30,7 +30,7 @@ For errors, the general convention is that "0" means success, and a negative number means failure. Check LL.c to be sure, though. - + ******************************************************************* To change the data, try this: @@ -73,12 +73,12 @@ my_data = (my_data *)LL_Get(list); ... do something to it ... } while(LL_Next(list) == 0); - + ******************************************************************* - + You can also treat the list like a stack, or a queue. Just use the following functions: - + LL_Push() // Regular stack stuff: add, remove, peek, rotate LL_Pop() LL_Top() @@ -191,6 +191,9 @@ int LL_Length (LinkedList * list); // Returns # of nodes in entire list // Searching... void *LL_Find (LinkedList * list, int compare (void *, void *), void *value); +// Array operation... +void *LL_GetByIndex (LinkedList * list, int index); // gets the nth node, 0 being the first + // Sorts the list... int LL_Sort (LinkedList * list, int compare (void *, void *));