Next Previous Contents

11.2 Curses User Interface (technical documentation)

This documentation describes implementation of Curses User Interface. It's meant to be used by programmers who want to either extend Curses client or fix some bugs in it.


Implementation of Curses client is split into a few files. Each file contains functions of specific functionality and it may help you to orient in the code if you have basic idea of what is in each file:


contains implementation of communication with libsurprise and implementation of operations above libsurprise data structures.


contains implementation of generic environment functions - eg. windows, general components and such.


contains initialization functions.


contains main key-handling loop and implementation of all functions needed for proper work of main screen (eg. device lists, partition lists, information window).


contains functions for printing messages for user and error printing.


contains implementation of undo. Note that this is in fact never used but it is there and in future can be debugged and turned on.


contains things somewhere among envir.c, main.c and command.c. Most functions are implementing communication with user before we have enough information to call function from command.c.


contains generic functions which didn't fit anywhere else.

User interface

Curses client has one layer called environment built above the curses and panel libraries. This layer implemented mainly in envir.c provides basic components as buttons, edit fields, list components and so on.

Basic element of this user interface is called environment window (see definition of env_window_t in envir.h). Each window is implemented as a panel of libpanel. Each window has specified handlers for function keys, special keys (like tab, enter, escape and others) and also handler for other keys. Window also contains labels of F-keys which should be shown when window is active. Each window contains list of components it contains and pointer to active component.

Component (see definition of env_component_t in envir.c) contains some information about its position in window, proper environment window and also pointer to component subwindow (each component is in subwindow of parent environment window so that we don't have to care about line wrapping and so on). Each instance of component has its id. This id is for use of above layers and environment layer doesn't care about it. Component can have same set of key handlers as environment window. After the generic part of component structure is type-specific part.

For each component type there can be also specified functions which should be called when component is deleted (array free_component), when component is activated (array activate_component) or when component is deactivated (array deactivate_component).

Currently these component types are implemented:


This component (see definition of struct comp_edit) allows editing of some string. You can also specify type of string it should contain. Currently there are only three types - number, string, unknown. When type is number component will let user leave it only when it contains number. When type is string there are no restrictions on contents. When type is unknown user specified checker is called to verify contents of string.


This component (struct comp_selection) is shown as list of entries from which user should choose one.


Choice component (struct comp_choice) is component which is similar to selection component. When inactive actual choice is shown. When component is activated and user press Enter window with selection component is shown and user can choose other entry. Actually structure of choice component contains only pointers to window with proper selection component and to current choice. Window with selection contains special component struct comp_bindchoice which is not shown and which contains only pointer back to proper struct comp_choice.


This component (struct comp_text) is used for displaying plain text.


This component (struct comp_button) implements ordinary button.


This component (struct comp_progressbar) implements progress bar.

Implementation of components

Edit component is implemented by following functions:


called when component is activated. It only makes cursor visible.


called when component is deactivated. It calls checker to check string contents and if checker agrees with deactivation cursor is made invisible.


called when component is deleted only makes cursor invisible.


called when some unhandled key is pressed. Prints it to edited string.


moves cursor in string to given position.


moves cursor to the left.


moves cursor to the right.


moves cursor to the beginning of line.


moves cursor to the end of line.


deletes character on given position.


deletes character under cursor.


deletes character before cursor.


creates new edit component.

Selection component is implemented by these functions:


creates new selection component. Note that choices are copied to allocated memory so that after component is created caller can free array given as a parameter. Callback specified as a parameter is called whenever any change of active choice happens.


changes active choice to given one.


moves active choice up.


moves active choice down.


moves active choice one page up.


moves active choice one page down.


moves to first choice.


moves to last choice.


is called when component is activated. It draws active choice in inverse color.


is called when component is deactivated. It redraws active choice in normal color.

Choice component is implemented by these functions:

cc_active(), cc_deactivate()

called on component activation/deactivation. Only redraws component in inverse/normal color.


is called when user chooses new choice. It hides window with choices and activates previous window (this activation will also activate current component and so new choice will be shown in our component). Then next component in window is activated.


is called when user wants to change choice. Function shows window with selection and let user choose.


is called when component should be destroyed. It closes window with selection.


creates new choice component.

Text component provides following functions:


move cursor in text component.


print character to component window (if it is control character like backspace, end of line or so it moves cursor appropriately).


print string to text component window. This function is implemented as calling ct_addch() at each character of the string.


move cursor to given place and print string (implemented in envir.h).


print formatted string to text component. It first formats string through sprintf() and then calls ct_addstr().


clears text component.


creates new text component.

Implementation of this functions is quite straightforward.

Button component has these functions:


It draws button on given position.

cu_activate(), cu_deactivate()

with obvious functionality.


creates new button component.

Progressbar component has these functions:


creates new component with progress bar.


updates viewed progress.

Generic window and component functions


This function creates new environment window. You can specify whether window should be centered (NEW_CENTER_FL), have a border (NEW_BORDER_FL), have a nonstandard attributes set (NEW_ATTR_FL) - first variable argument is taken as an attribute number, have a title (NEW_TITLE_FL) - in that case next variable argument is taken as a title.


This function returns curses window corresponding to given environment window.


Converts key code of special key returned by curses to our key numbering.


This function destroys given environment window and all components in it.


This function closes active environment window and makes active window under it.


This function will find component with give id in given window.


This function activates given window (puts it to the beginning of window list), draws labels for F-keys and activates component active in the window.


refreshes window with given component in case component isn't hidden.


just refresh all components in the window.


This macro returns active window.


This function allocates space for component with given amount of memory reserved for type-specific part of component and initializes generic component part of structure (including creating component window).


destroys component (calls specified callback for freeing).


activates component (calls specifies callback for activating). It includes redrawing of F-key labels appropriately to component F-key handlers.


deactivates component (deletes F-key labels induced by deactivated component).


activates next component in the current window.


activates previous component in the current window.


makes component hidden (currently unused).


makes component visible (currently unused).


This macro returns type-specific part of component typed to given type.


Checks whether active component in given window has given id.


places hardware cursor the place of software cursor in active window in active component.


makes all changes buffered by curses visible, places cursor on proper place.

Key handling

Key handling system of Curses client recognizes three types of keys:

Function keys

These are function keys from F1 to F20 (keys with higher number can be accessed by pressing Shift and F key).

Special keys

These are keys like Enter, Tab, Backspace and others (see definition of enum env_special_keys_e in envir.h for complete list).

Other keys

These are keys which are neither function nor special.

For each function key and each special key can be specified key handler. For other keys there is one handler for all keys. Each window and each component has its own set of handlers. When key is pressed handling function looks whether active component in active window has this key handled. If so, handler is called and handling is finished. If component doesn't have handler for pressed key handler of window is called (if present). In case neither window has key handled key is ignored and client beeps.

Error reporting and message printing

Curses client keeps list (curs_errlist) of errors to report. When some function returns any errors they are appended to the error list. In main loop if we find that there are errors on error list we show them. Error list is handled by three functions:


This function appends given list of errors to clients error list.


This function gets first error from error list and displays it through showerror() function.


This function shows all errors on the list. It doesn't use general key handling function and is used generally at times user interface is not properly initialized.

Reporting does function showmessage() which shows given text in message window (it just creates window of proper size and text component with given string in it). This function is also used by showerror() function which just makes string from struct user_error and prints it through showmessage().

Utility functions

This section describes various utility functions used in Curses client.


This function is called through die() macro. It will print error message along with file name and line number and exits the client.


Error handled version of malloc(). Exits when allocation fails.


Safe version realloc(). Exits when reallocation fails.


Safe version (macro) of alloca. Exits when allocation fails.


strncpy() which guarantees nullterminated result.


strncat() which guarantees nullterminated result.


Gets last component of file name.


This function gets text and maximal possible width and returns number of columns really used by printed text and number of lines used by the text.


This function gets struct user_parameter and returns its printable form.


This function returns number of colums needed by given parameter for showing and editing.


Counts maximum of maxparamvallen() over given list of parameters.


Returns maximal length of name of parameter.


This macro returns whether given parameter can be changed by user.


Returns number of parameters on list which can be changed.


Returns index of parameter with given id in an array of parameters.


Returns index of filesystem of given type in diskinfo array.

round_part_start(), round_part_end(), round_part_len()

Macros for rounding partition start, end and length.


Curses client has simple system for undoing operations and automatic verification of modifications. Currently this functionality is not accessible to the users and system even isn't debugged very much but it's there and it shouldn't be much work to finish it.

Undo system has a stack (undo_buf - it is initialized in undo_init()) in which it stores all planned state of disks (that means all array of struct disk_info). On the stack operate these functions:


This function gets entry on stack and frees all struct disk_info attached to it. So it in fact frees entry in undo buffer.


This function gets current planned state and duplicates it to undo buffer.


This function frees planned state in given struct final and puts state from given undo buffer on its place.


This function frees entry in undo stack if it is full and then duplicates current state to undo buffer and updates top of the stack.


This function replaces current planned state with the one from top of the stack and updates the top of the stack.

In automatic verification it is needed to restore previous state if last step was not correct. For that case special undo buffer saved_state is created. Before any action is done current state is saved to this buffer then action is done. If verification succeeds state stored in saved_state is moved to undo stack. If verification fails stored state is restored. For automatic verification are used these functions:


This function duplicates current state to saved_state buffer.


This function restores state from saved_state.


This function moves state saved in saved_state to undo stack (freeing oldest entry in case stack is full).


Curses client has also support of a few options. These options can be set from command line when running client or from dialog window (choice Client settings in menu). Values of options are stored in global variables with prefix cco_. Currently supported options are:


When this option is set length and partition start are automagically aligned to values supplied by libsurprise.


When this option is set verification is performed after each step and when it fails previous state is restored.


This option specifies minimal partition size.


This option specifies whether messages from libsurprise should be printed to stderr.


This option specifies whether warning message should be printed when starting client.


This option specifies whether length should be printed in cleverly chosen units in dialog boxes.


Main function of all Curses client is function cursesclient(). It first calls init_cursclient() for initialization and then loops in a key processing loop (see key processing for details about key processing).

Initialization has a few steps. At first libsurprise error system is initialized and warning message printed. When warning is accepted screen is initialized through function init_screen() (it configures curses flags and so on). Then information about disk subsystem is loaded by function curs_get_info() (this function is implemented in command.c). Then undo system is initialized by undo_init(), main window and window with original states of disks is created including lists of devices and partitions and information components.

Basic information about disk subsystem (struct resources - see resources description) is kept in global variable diskinfo, information about current and planned state (struct final) in variable diskstate. Formatted information about partitions (and also free space between partitions) along with pointer to corresponding array of struct disk_info is kept in array pinfo (see definition of struct complete_part_info in main.h). In one element of pinfo is kept information about planned state of partitions and in the other one is kept information about original state of partitions.

Array i_vinfo in pinfo is built for each disk from struct disk_info by function create_partlist(). This function just copies information about partitions from given structure, sorts partition by their beginning and inserts entries for free space if free space is large enough to be of any interrest.

Note that most handler functions are just wrappers for functions which does the thing. It's not because I like so much structured programming but mainly because we need to call those functions from menu and when key is pressed. Also most functions called from handlers does just some basic sanity checks and then pass values to functions in usercomm.c which let user edit parameters and confirm the action. Refreshing of information shown on the screen is responsibily of functions in usercomm.c and only if no such function is called function in main.c has to refresh the screen (this is because we usually need to leave the key handler and ask user for more information and so we end up in some handler in usercomm.c).

Implementation of function menu

There are three menus with functions in Curses client. When F9 is pressed menu appropriate to active component is shown. So we have menu for disk selection and menu for partition selection. There are also similar menus for window with original states of disks. Implementation of function menu consists of a few handlers:


Called whenever user closes menu. Function closes window with menu.


Called when user chooses function in menu for disk selection. Function appropriate to chosen action is called (as if F key was pressed).


Called when user chooses function in menu for partition selection. Function appropriate to chosen action is called (as if F key was pressed).


Similar to main_menu_disk_accept().


Similar to main_menu_part_accept().

Handlers of main window

These are handlers of keys in main window:


Called when menu with functions should be shown. It just creates menu appropriate to active component.


Called through main_f_quit() when user wants to quit client. Sets state variable curs_state to CST_QUIT state.


Wrapper for main_do_quit().


Called through main_f_gpars() when user wants to edit global parameters. Just calls ask_global_pars().


Wrapper for main_do_gpars().


Called through main_f_dpars() when disk parameters should be edited. Just calls ask_disk_exppars().


Wrapper for main_do_dpars().


Called when user wants to commit changes. Calls curs_commit() to commit changes, displays message appropriate to result of commit and calls recreate_all_infos() to recreate all informations appropriately to new state of disk.


Wrapper for main_do_commit().


Called when user wants to verify planned state. Calls curs_verify() and displays message about success if verification was ok.


Wrapper for main_do_verify().


Called on creation of new partition (in case disk selection was active). It finds first free space on current disk asks user for confirmation through ask_new_part().


Wrapper for main_do_new_part().


Called when user wants to change client settings. It just calls ask_settings().


Activates next component in main window. We can't use generic activation functions from envir.c because we only want to switch between disk and partition selection and we want to update information window appropriately to current active component.


Activates previous component in main window (see above for sense of this function).


Activates either window with original state or with planned state (in short the one which is currently inactive).


Wrapper for main_do_other_window(). It just deactivates active component and calls main_do_other_window().

Disk selection functions

Following functions are operating on disk selection:


Creates selection component with devices from list of devices in diskinfo.


Redraws base information about partitions shown next to partition selection.


Prints extended information about disk to information window.


Updates base information about partition, information about disk and partition selection appropriately to new disk (This function is used as callback of device selection component).


This function deletes all partition selection and disk selection components and creates new with updated data. It tries to preserve position of active entry in selections. This function is used after commit when anything may change.

Partition selection functions

Following functions are operation on partition selection:


This function shows all information about partition and filesystem on it in information window. It formats filesystem parameters that way that in one column should be parameter names and in second column parameter values.


This callback called from partition selection component updates information about partition in information window appropriately to new active partition.


This function creates selection component from struct complete_part_info corresponding to given window.


This function creates new array i_vinfo from struct disk_info and recreates component with list of partitions for given disk.


This function updates (through functions show_disk_info() or show_part_info()) contents of information window according to active disk or partition.


This function calls show_base_disk_info() on current partition.


This function calls recreate_part_sel() on given disk or on all disks if -1 is passed as disk number.


This function recreates all information about disk and redisplays all information shown about it (recreate list of partitions and selection component, redisplays disk or partition information and base partition information if shown).

Now follows key handling functions for partition selection:


Called (through psel_del_part()) when user wants to delete partition. Calls curs_del_part() on current partition and then recreates all information through disk_changed().


Wrapper for psel_do_del_part().


Called (through psel_new_part()) when user wants to create partition. Calls ask_new_part() for user confirmation and partition creation.


Wrapper for psel_do_new_part().


Called (through psel_conv_part()) on partition conversion. Real functionality is in called ask_conv_part().


Wrapper for psel_do_conv_part().


Called (through psel_copy_part()) when data from partition should be copied. Just calls ask_copy_part().


Wrapper for psel_do_copy_part().


Called (through psel_moveresize_part()) when partition should be moved or resized. Just calls ask_moveresize_part().


Wrapper for psel_do_moveresize_part().

Window with original state

In the window with original state are following special key handlers:


Called (through main_orig_f_dpars()) when imported parameters of disk should be changed. Just calls ask_disk_imppars().


Wrapper for main_orig_do_dpars().


Shows menu with available operations.


Called (through psel_fs_ipars) when imported parameters should be changed. Just calls ask_import_fs_pars().


Wrapper for psel_do_fs_ipars().

Interacting with user (asking for parameters)

For communication with user were created and implemented two compound components. They are

dialog window

This component (created through newenvdialog()) is a window with two buttons (OK and Cancel). On creation caller specify callbacks which should be called when dialog is accepted (either through pressing F2 or through pushing OK button) or canceled (F10, Cancel button or Escape).

selection window

This component (created through newenvselection()) is a window with selection component. Caller can specify callbacks to be called on accept (F2 or Enter) and on cancel (F10 or Escape).

There are also generic helper functions for filesystem parameter editing:


This is callback function used in edit components for checking contents of component on deactivation. It checks whether component contains number in given interval.


Callback used for checking whether string in edit component matches given regular expression.


Callback used for checking whether given string in edit component is correct length specifier (ie. it's number of sectors or size in KB, MB or GB).


Function creates on given position component used for editing given parameter (choice component for discreete numbers, strings and boolean values, edit component for interval and string). Created component has assigned id corresponding to id of parameter so that we know which component belongs to which parameter.


This function counts size of dialog needed for parameters and then creates (using create_par_component()) components for all changable parameters. This function also takes argument whether given parameters are imported or exported because changability of parameter depends on this.


This function updates given parameter appropriately to value stored in the given component.


This function gets all components with parameters in given window and creates list of parameters with appropriate values from them.


This function gets list of parameters and updates values of parameters whose components are present in given window.

Creating partition

First function called on partition creation is ask_new_part(). This function creates dialog window with partition number, partition start, partition length and filesystem type for new partition. When user cancels the creation, callback np_cancel is called which just closes the dialog. On accept callback np_create_accept is called. This function gathers data from components, stores them in global variables (I don't like this solution much but I don't know cleaner and reasonably easy), closes dialog and creates new dialog with filesystem parameters. When parameters are eddited and accepted callback np_fspars_accept() is called. It gathers the filesystem parameters, closes dialog and calls curs_new_part() to create new partition.

Converting filesystem

First converting function is ask_conv_part(). This function creates selection window in which user can choose destination filesystem type. When user chooses filesystem type (canceling leads to calling cv_cancel() which just closes the selection window) cv_fstype_accept() is called. It stores filesystem type in global variable and closes the selection window. If destination filesystem type is same as the current one window with current filesystem parameters is created and user can edit them. If destination fstype differ window with filesystem parameters with default values is created (ask_fs_pars() does the work). Function cv_fspars_mod_accept() is called when user modified and accepted parameters for current filesystem (fstype didn't change). The function stores current planned state of disks (see undo system for more information), calls gather_fs_pars_modify() to alter parameters, closes window and plans conversion through curs_conv_part. If everything succeeds, disk_changed() is called to update viewed information. cv_fspars_new_accept() called when user modified parameters and accepted conversion to other fstype works almost the same way as cv_fspars_modify_accept(). It only uses gather_fs_pars_create() for gathering modified parameters.

Copying partition

Copying of partition data starts in function ask_copy_part(). This function creates dialog with components for editing of destination device and partition number. When user edits values, cp_pars_accept() is called (on cancel is called cp_pars_cancel() which closes the dialog). cp_pars_accept() extracts user specified values from dialog and closes it. Then curs_copy_part() is called. This function checks whether destination partition exists and whether source and destination partitions are different. If all checks succeed structure is updated to reflect copying of data.

Moving and resizing partition

Moving and resizing of partition starts in function ask_moveresize_part(). This function creates dialog with components for editing of device, partition number, partition start and length. When user modifies values he/she wants, callback mr_pars_accept() is called (on cancel mr_pars_cancel() which just closes the dialog is called). mr_pars_accept gathers new values from components in dialog, closes it and calls curs_moveresize_part() to alter proper structures. When operation succeeds viewed information for source and evetually destination disk are updated (this has sence in case we would have more main windows).

Editing global and disk parameters

Editing of global parameters is quite simple. In the beginning function ask_global_pars() is called. This function creates dialog with all global parameters (function ask_fs_pars() is used). On accept callback gp_pars_accept is called. It saves current planned state (see undo system for more information), alters global parameters by gather_fs_pars_modify(), closes dialog and calls curs_set_pars() to set global parameters.

Setting of disk parameters starts in function ask_disk_imppars() or ask_disk_exppars depending on type of parameters to edit. Called function creates dialog with asked parameter set. When user wanted to edit imported parameters callback idp_pars_accept() is filled in as a callback for accept otherwise edp_pars_accept() is used. idp_pars_accept() just stores current state to undo buffer, alter update imported parameters in gather_fs_pars_modify() and closes environment window. edp_pars_accept() does the same as idp_pars_accept() but it also calls curs_set_pars() to verify correctness of set parameters.

Editing imported filesystem parameters

Parameter editing starts in ask_import_fs_pars(). This function creates dialog (by calling ask_fs_pars()) with filesystem imported parameters. When parameters are modified and dialog accepted ifs_pars_accept() is called. It stores current state in undo buffer, alter current parameters, closes the dialog and updates info panels.

Editing client parameters

Editing of Curses Client parameters starts in function ask_settings(). This function copies data from global variables (those with cco_ prefix) to parameter structures used for editing and then calls ask_fs_pars() to create dialog with proper components. When parameters are edited and dialog accepted set_pars_accept() is called. This function just calls gather_fs_pars_modify() to copy data from components to parameter structures then closes the dialog and copies data from parameter structures back to global variables. In case dialog is canceled pars_cancel() is called to close dialog window.

Altering libsurprise structures and commiting changes

Progress indicator and aborting

When commiting changes or verifying Curses client shows progress indicator. It is handled by following functions:


This function creates window with progress bar.


This function closes window with progress bar.


This is callback called from libsurprise when progress bar should change. It redraws progress bar according to given progress.

Because it's not wise to stop libsurprise in the middle of converting when converting special handler abort_handler() is attached to SIGINT. When SIGINT is caught global variable abort_requested is set and function show_progress() then returns to libsurprise that abort was requested.

Creating structures

In the beginning and after commit curs_get_info() is called to get information about disk subsystem from libsurprise. This function first calls get_disk_info() which is in fact wrapper to get_resources() of libsurprise. Then create_api_structs() is called to create struct final. create_api_structs() calls get_final() to create desired struct final. When everything is ok, add_missing_exp_pars() is called for each partition to add missing changable exported parameters to the partition.

Verifying and commiting changes

Verification and commit are quite similar operations. At first progress indicator is created and SIGINT handler installed. Then commit_disks() from libsurprise is called. After it returns SIGINT handler is restored and progress bar hidden. In case there were any errors they are added to error list (by add_report_errors()). In case we are commiting changes we free all information about disks and partitions and then call curs_get_info() to get new disk state from libsurprise.

Operations on structure

All operations on struct final at first backup current state with save_state() then do the operation (function has usually prefix do_). Then if verification of every step is required curs_verify() is called to verify the step. If verification succeeds saved state is moved to undo buffer. If verification fails old state is restored and error returned.

Operations on structure are following:

deleting partition

This does function do_curs_delete_part() which frees data structures associated with partition and then puts some other partition on its place in structures and shrinks the array of partitions.

creating partition

This is done by function do_curs_new_part(). This function first checks whether wanted partition number is used (if so error is returned). Then aligns partition start and lenght (if option cco_align is set) and checks whether resulting values are reasonable (ie. positive, before end of disk and so). If all checks are ok space for new partition is allocated and structure filled in.

converting partition

This is implemented in function curs_conv_part() (this is a bit exceptional function as current state is already saved when this function is called and so this function doesn't have usual wrapper). If we are converting to other filesystem current filesystem parameters are freed and filesystem type is altered. Otherwise we have nothing to do (parameters were already altered).

moving and resizing partition

This is implemented in function do_curs_moveresize_part(). Function first checks whether we are moving to other disk and in that case moves structure for partition to proper disk. Then if partition number or disk changed it checks whether new partition number is unused and if so it changes it in partition structure. Then if partition start changed we align it (in case option cco_align is set) update structure of partition appropriately. Also if partition length changed we align the length (if option cco_align is set) and set length in partition structure.

setting parameters

When global or disk exported parameters are set curs_set_pars() is called. It just calls curs_verify() if verification after each step is required and eventually restores previous state.

Next Previous Contents