Overview of writing code using the menu system
----------------------------------------------
This file contains implementation and developer documentation.
For simple cases, you should start by using simple.c as a template.
complex.c illustrates most of the features available in the menu system.
Menu Features currently supported are:
* menu items,
* submenus,
* disabled items,
* checkboxes,
* invisible items (useful for dynamic menus), and
* Radio menus,
* Context sensitive help
* Authenticated users
The keys used are:
* Arrow Keys, PgUp, PgDn, Home, End Keys
* Space to switch state of a checkbox
* Enter to choose the item
* Escape to exit from it
* Shortcut keys
1. Overview
-----------
The code usually consists of many stages.
* Configuring the menusytem
* Installing global handlers [optional]
* Populating the menusystem
* Executing the menusystem
* Processing the result
1.1 Configuring the menusystem
------------------------------
This includes setting the window the menu system should use,
the choice of colors, the title of the menu etc. In most functions
calls, a value of -1 indicates that the default value be used.
For details about what the arguments are look at function
declarations in menu.h
// Choose the default title and setup default values for all attributes....
init_menusystem(NULL);
set_window_size(1,1,23,78); // Leave one row/col border all around
// Choose the default values for all attributes and char's
// -1 means choose defaults (Actually the next 4 lines are not needed)
set_normal_attr (-1,-1,-1,-1);
set_status_info (-1,-1);
set_title_info (-1,-1);
set_misc_info(-1,-1,-1,-1);
1.2 Populating the menusystem
-----------------------------
This involves adding a menu to the system, and the options which
should appear in the menu. An example is given below.
MAINMENU = add_menu(" Menu Title ",-1);
CHECKED = 1;
add_item("option1","Status 1",OPT_RUN,"kernel1 arg1=val1",0);
add_item("selfloop","Status 2",OPT_SUBMENU,NULL,MAINMENU);
add_item("othermenu","Status 3",OPT_SUBMENU,"menuname",0);
add_sep();
add_item("checkbox,"Checkbox Info",OPT_CHECKBOX,NULL,CHECKED);
add_item("Exit ","Status String",OPT_EXITMENU,NULL,0);
The call to add_menu has two arguments, the first being the title of
the menu and the second an upper bound on the number of items in the menu.
Putting a -1, will use the default (see MENUSIZE in menu.h). If you try
to add more items than specified, the extra items will not appear in
the menu. The accuracy of this number affects the memory required
to run the system.
If you do not want to keep track of the return values, you can also use
the following variant of add_menu
add_named_menu("main"," Menu Title ",-1)
This creates a new menu as before and gives it a name "main". When using named
menus, you get an alternate way for adding submenu's. See below for details.
The call to add_item has five arguments.
The first argument is the text which appears in the menu itself.
The second argument is the text displayed in the status line.
The third argument indicates the type of this menuitem. It is one of
the following
* OPT_RUN : executable content
* OPT_EXITMENU : exits menu to parent
* OPT_SUBMENU : if selected, displays a submenu
* OPT_CHECKBOX : associates a boolean with this item which can be toggled
* OPT_RADIOMENU: associates this with a radio menu.
After execution, the data field of this item will point
to the option selected.
* OPT_SEP : A menu seperator (visually divide menu into parts)
* OPT_RADIOITEM: this item is one of the options in a RADIOMENU
* OPT_INACTIVE : A disabled item (user cannot select this)
* OPT_INVISIBLE: This item will not be displayed.
The fourth argument is the value of the data field always a string.
Usually this string is just copied and nothing is done with it. Two
cases, where it is used.
In case of a radiomenu the input string is ignored and the "data" field
points to the menuitem chosen (Dont forget to typecast this pointer to
(t_menuitem *) when reading this info).
In case of a submenu, this string if non-trivial is interpreted as the
name of the submenu which should be linked there. This interpretation
happens when the menu is first run and not when the menu system is being
created. This allows the user to create the menusystem in an arbitrary
order.
The fifth argument is a number whose meaning depends on the type of the
item. For a CHECKBOX it should be 0/1 setting the initial state of the
checkbox. For a SUBMENU it should be the index of the menu which should
be displayed if this option is chosen. Incase the data field is non-trivial,
this number is ignored and computed later. For a RADIOMENU it should be the
index of the menu which contains all the options (All items in that menu
not of type RADIOITEM are ignored). For all other types, this
argument has no meaning at all.
A call to add_sep is a convenient shorthand for calling add_item
with the type set to OPT_SEP.
1.3 Executing the menusystem
----------------------------
This is the simplest of all. Just call showmenus, with the index
of the main menu as its argument. It returns a pointer to the menu
item which was selected by the user.
choice = showmenus(MAIN); // Initial menu is the one with index MAIN
// or choice = showmenus(find_menu_num("main")); // Initial menu is the one named "main"
1.4 Processing the result
-------------------------
This pointer will either be NULL (user hit Escape) or always point
to a menuitem which can be "executed", i.e. it will be of type OPT_RUN.
Usually at this point, all we need to do is to ask syslinux to run
the command associated with this menuitem. The following code executes
the command stored in choice->data (there is no other use for the data
field, except for radiomenu's)
if (choice)
{
if (choice->action == OPT_RUN)
{
if (syslinux) runcommand(choice->data);
else csprint(choice->data,0x07);
return 1;
}
csprint("Error in programming!",0x07);
}
2. Advanced features
--------------------
Everycall to add_item actually returns a pointer to the menuitem
created. This can be useful when using any of the advanced features.
2.1 extra_data
--------------
For example, every menuitem has an "extra_data" field (a pointer)
which the user can use to point any data he/she pleases. The menusystem
itself does not use this field in anyway.
2.2 helpid
----------
Every item also has a field called "helpid". It is meant to hold some
kind of identifier which can be referenced and used to generate
a context sensitive help system. This can be set after a call to
add_item as follows
add_item("selfloop","Status 2",OPT_SUBMENU,NULL,MAINMENU);
set_item_options('A',4516);
The first is the shortcut key for this entry. You can put -1 to ensure
that the shortcut key is not reset. The second is some unsigned integer.
If this value is 0xFFFF, then the helpid is not changed.
2.3 Installing global handlers
------------------------------
It is possible to register handlers for the menu system. These are
user functions which are called by the menusystem in certain
situations. Usually the handlers get a pointer to the menusystem
datastructure as well as a pointer to the current item selected.
Some handlers may get additional information. Some handlers are
required to return values while others are not required to do so.
Currently the menusystem support three types of global handlers
* timeout handler
* screen handler
* keys handler
2.3.1 timeout handler
---------------------
This is installed using a call to "reg_ontimeout(fn,numsteps,stepsize)"
function. fn is a pointer to a function which takes no arguments and
returns one of CODE_WAIT, CODE_ENTER, CODE_ESCAPE. This function is
called when numsteps*stepsize Centiseconds have gone by without any
user input. If the function returns CODE_WAIT then the menusystem
waits for user input (for another numsteps*stepsize Centiseconds). If
CODE_ENTER or CODE_ESCAPE is returned, then the system pretends that
the user hit ENTER or ESCAPE on the keyboard and acts accordingly.
2.3.2 Screen handler
--------------------
This is installed using a call to "reg_handler(HDLR_SCREEN,fn)". fn is
a pointer to a function which takes a pointer to the menusystem
datastructure and the current item selected and returns nothing.
This is called everytime a menu is drawn (i.e. everytime user changes
the current selection). This is meant for displaying any additional
information which reflects the current state of the system.
2.3.3 Keys handler
------------------
This is installed using a call to "reg_handler(HDLR_KEYS,fn)". fn is
a pointer to a function which takes a pointer to the menusystem
datastructure, the current item and the scan code of a key and returns
nothing. This function is called when the user presses a key which
the menusystem does not know to dealwith. In any case, when this call
returns the screen should not have changed in any way. Usually,
one can change the active page and display any output needed and
reset the active page when you return from this call.
complex.c implements a key_handler, which implements a simple
context sensitive help system, by displaying the contents of a
file whose name is based on the helpid of the active item.
Also, complex.c's handler allows certain users to make changes
to edit the commands associated with a menu item.
2.4 Installing item level handlers
----------------------------------
In addition to global handlers, one can also install handlers for each
individual item. A handler for an individual item is a function which
takes a pointer to the menusystem datastructure and a pointer to the
current item and return a structure of type t_handler_return. Currently
it has two bit fields "valid" and "refresh".
This handler is called when the user hits "enter" on a RUN item, or
changes the status of a CHECKBOX, or called *after* a radio menu choice
has been set. In all other cases, installing a handler has no effect.
The handler can change any of the internal datastructures it pleases.
For e.g. in a radiomenu handler, one can change the text displayed
on the menuitem depending on which choice was selected (see complex.c
for an example). The return values are ignored for RADIOMENU's.
In case of RUN items: the return values are used as follows. If the
return value of "valid" was false, then this user choice is ignored.
This is useful if the handler has useful side effects. For e.g.
complex.c has a Login item, whose handler always return INVALID. It
sets a global variable to the name of the user logged in, and enables
some menu items, and makes some invisible items visible.
* If the handler does not change the visibility status of any items,
the handler should set "refresh" to 0.
* If the handler changes the visibility status of items in the current
menu set "refresh" to 1.
* If you are changing the visibility status of items in menu's currently
not displayed, then you can set "refresh" to 0.
* Changing the visibility status of items in another menu
which is currently displayed, is not supported. If you do it,
the screen contents may not reflect the change until you get to the
menu which was changed. When you do get to that menu, you may notice
pieces of the old menu still on the screen.
In case of CHECKBOXES: the return value of "valid" is ignored. Because,
the handler can change the value of checkbox if the user selected value
is not appropriate. only the value of "refresh" is honored. In this case
all the caveats in the previous paragraph apply.
menu.h defines two instances of t_handler_return
ACTION_VALID and ACTION_INVALID for common use. These set the valid flag
to 1 and 0 respectively and the refresh flag to 0.
3. Things to look out for
-------------------------
When you define the menu system, always declare it in the opposite
order, i.e. all lower level menu's should be defined before the higher
level menus. This is because in order to define the MAINMENU, you need
to know the index assigned to all its submenus.
4. Additional Modules
---------------------
You can make use of the following additional modules, in writing your
handlers.
* Passwords
* Help
4.1 Passwords
-------------
This module was written by Th. Gebhardt. This is basically a modification
of the DES crypt function obtained by removing the dependence of the
original crypt function on C libraries. The following functions are
defined
init_passwords(PWDFILE)
// Read in the password database from the file
authenticate_user(user,pwd)
// Checks if user,pwd is valid
isallowed(user,perm)
// Checks if the user has a specified permission
close_passwords()
// Unloads password database from memory
See the sample password file for more details about the file format
and the implementation of permissions.
See complex.c for a example of how to use this.
4.2 Help
--------
This can be used to set up a context sensitive help system. The following
functions are defined
init_help(HELPBASEDIR)
// Initialises the help system. All help files will be loaded
// from the directory specified.
runhelpsystem(context)
// Displays the contents of HELPBASEDIR/hlp.txt
In order to have a functioning help system, you just need to create
the hlp.txt files and initialize the help system by specifying
the base directory.
The first line of this file assumed to be the title of the help screen.
You can use ^N and ^O to change attributes absolutely and relatively,
i.e. [^O]46 (i.e. Ctrl-O followed by chars 4 and 6) will set the
attribute to 46, while [^N]08 will XOR the current attribute with
specified number, thus in this case the first [^N]08 will turn on
highlighting and the second one will turn it off.