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
Most functions are implementing communication with user before we have enough information
to call function from
contains generic functions which didn't fit anywhere else.
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
envir.h). Each window is implemented as a panel of libpanel.
Each window has specified handlers for function
keys, special keys (like
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 (see definition of
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
activate_component) or when component is deactivated (array
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
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.
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:
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
ct_addch() at each character of the string.
move cursor to given place and print string (implemented in
print formatted string to text component. It first formats string through
sprintf() and then calls
clears text component.
creates new text component.
Button component has these functions:
It draws button on given position.
with obvious functionality.
creates new button component.
Progressbar component has these functions:
creates new component with progress bar.
updates viewed progress.
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 system of Curses client recognizes three types of keys:
These are function keys from F1 to F20 (keys with higher number can be accessed by pressing Shift and F key).
These are keys like Enter, Tab, Backspace and others
(see definition of
enum env_special_keys_e in
envir.h for complete list).
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.
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
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
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.
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
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
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
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
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
This function restores state from
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
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
for initialization and then loops in a key processing loop (see
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
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
about current and planned state (
struct final) in variable
information about partitions (and also free space between partitions) along with
pointer to corresponding array of
struct disk_info is kept in
pinfo (see definition of
struct complete_part_info in
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.
pinfo is built for each disk from
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
usercomm.c which let user edit parameters and confirm the action. Refreshing
of information shown on the screen is responsibily of functions in
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
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).
These are handlers of keys in main window:
Called when menu with functions should be shown. It just creates menu appropriate to active component.
main_f_quit() when user wants to quit client. Sets
main_f_gpars() when user wants to edit global
parameters. Just calls
main_f_dpars() when disk parameters should be edited.
Called when user wants to commit changes. Calls
commit changes, displays message appropriate to result of commit and calls
to recreate all informations appropriately to new state of disk.
Wrapper for main_do_commit().
Called when user wants to verify planned state. Calls
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
Wrapper for main_do_new_part().
Called when user wants to change client settings. It just calls
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).
main_do_other_window(). It just deactivates active
component and calls
Following functions are operating on disk selection:
Creates selection component with devices from list of devices in
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.
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
corresponding to given window.
This function creates new array
and recreates component with list of partitions for given disk.
This function updates (through functions
show_part_info()) contents of information window according to active disk or
This function calls
show_base_disk_info() on current
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:
psel_del_part()) when user wants to delete partition.
curs_del_part() on current partition and then recreates all information through
psel_new_part()) when user wants to create partition.
ask_new_part() for user confirmation and partition creation.
psel_conv_part()) on partition conversion. Real
functionality is in called
psel_copy_part()) when data from partition should be copied.
psel_moveresize_part()) when partition should
be moved or resized. Just calls
In the window with original state are following special key handlers:
main_orig_f_dpars()) when imported parameters
of disk should be changed. Just calls
Shows menu with available operations.
psel_fs_ipars) when imported parameters should be changed.
For communication with user were created and implemented two compound components. They are
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).
This component (created through
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
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.
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
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.
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 (
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
gather_fs_pars_modify() to alter parameters, closes window
and plans conversion through
curs_conv_part. If everything succeeds,
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
It only uses
gather_fs_pars_create() for gathering modified parameters.
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
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 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).
new values from components in dialog, closes it and calls
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 of global parameters is quite simple. In the beginning function
is called. This function creates dialog with all global parameters (function
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
closes dialog and calls
curs_set_pars() to set global parameters.
Setting of disk parameters starts in function
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.
just stores current state to undo buffer, alter update imported parameters in
gather_fs_pars_modify() and closes environment window.
does the same as
idp_pars_accept() but it also calls
verify correctness of set 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
ifs_pars_accept() is called. It stores current state in undo buffer,
alter current parameters, closes the dialog and updates info panels.
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
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
abort_handler() is attached to
SIGINT is caught
abort_requested is set and function
returns to libsurprise that abort was requested.
In the beginning and after commit
curs_get_info() is called to get
information about disk subsystem from libsurprise. This function first
get_disk_info() which is in fact wrapper to
create_api_structs() is called to create
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
Verification and commit are quite similar operations. At first progress indicator is
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
In case we are commiting changes we free all information about disks and partitions and
curs_get_info() to get new disk state from libsurprise.
All operations on
struct final at first backup current state with
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:
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.
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
This is implemented in function
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).
This is implemented in function
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
cco_align is set) and set length in partition structure.
When global or disk exported parameters are set
is called. It just calls
curs_verify() if verification after each step is required
and eventually restores previous state.