Graphics Library

This library provides GUI functions on Linux/X11, Android and Linux/Framebuffer. The 2.x series now provide a layout editor, which is exclusively used for creating widgets.

2.7 Bug fix in text entry widget.
2.7 Improved canvas widget.
2.6 Slider, file list, some cleanup.
2.5 Log widget, Android port and improved editor.
2.4 Bug fixes, timers and spinner widget.
2.3 Standalone code generator, independent of the library.
2.2 Layout editor is now written with the layout editor.
2.1 First version with the layout editor.
2.0 Transitioning from 1.4 to 2.x.
1.4 Bugfix release.
1.3 New widgets and slight modifications to event system.
GL-gfx A fork of 1.2 which can also use OpenGL.
1.2 Improvement of widget object system.
1.1 List widget implemented.
1.0 Initial.

Contents

Change Log

              -2.7-
20170115  Canvas zoom window.
20161228  Printing on canvas widget.
20161228  Canvas scrollbars can be disabled in the layout editor.
              -2.6-
20160802  File list widget.
20160802  Slider widget.
20160616  Double click for lists. User resources are now exported to the
          header so that they can be used for lists etc.
              -2.5-
20160529  Alignment function for the editor.
20160528  All widgets have their own attribute editors now.
20160527  Put the Android stuff back in. 
20160525  Implemented the log viewer widget.
20160525  When a widget is created, the attributes screen is shown
          automatically.
              -2.4-
20160511  Cleaned up a lot of unused variables remaining from older
          iterations.
20160511  Fixed a huge bug in canvas buffer setsize() function.
20160427  Fixed the subscreen handling code for the panel object.
20160426  In tab order determination, I should have a way of telling
          which widgets are visible. This is already done by the
          can_focus flag. When a textentry gets hidden, can_focus is
          set to zero, removing the widget from the list of available 
          widgets for focusing.
20160426  Automatic tab order for screens. Left-to-right, top-to-bottom.
20160426  Spinner widget is implemented.
20160426  Internal timers are implemented.
20160418  Dropdown and list widgets now support the clear() command.
              -2.3-
20160411  Old layout editor is removed now. gfxgen takes its place to
          generate the layout editor.
              -2.2-
20160406  The layout editor now can edit its own layout file and 
          generate the GUI code for itself. This will be the last 
          version with the old layout editor in it.
20160404  The save file should contain the name of the generated file,
          possibly encoded relative to the path of the save file. Also
          the working screen size should be stored in the save file.
            This is so now. 
20160404  The generated C file should include the header according to 
          its own name. Currently, it simply includes "gfx.h". This 
          should change according to the C file's name. i.e. "display.h"
          etc.
            The generated source and header files are now independent of
          each other. The source file already contains the text of the
          header at the appropriate place. This way, it doesn't have to
          include anything.
20160324  Canvas widget is implemented.
20160317  File selector is re-introduced, the list in the things to
          do section is still valid.
20160313  Toggle button widget is implemented, as part of the regular
          button widget.
          Fixed a bug in the screen dimension computation.
          Re-implemented the confirmation dialog.
              -2.1-
20151225  A layout editor is implemented.  Widgets are no longer
          directly created by the user, you simply specify them in the
          layout editor and the editor generates the necessary code. The
          generated code creates the screens and exports only the screen
          functions.
20151225  Pixgrid widget was implemented.
20151225  Android port is removed for now, but I have framebuffer port.
              -1.4-
20150915  Various bug fixes.
              -1.3-
20150515  CheckBox widget is implemented.
20150513  Radio widget is implemented.
20150628  List widget interface is updated. The tuple array stuff is 
          gone.  The new interface looks like the dropdown widget
          interface.
20150704  _show() functions were imported into the widget object. Now we
          only call gfx_show(no,show) for all widgets.
20150704  The focusable interface was dropped. Focus information is now
          carried thru widget objects. Event handling is done there too.
20150704  key_filter was implemented for the text entry widget.
20150712  File selector is implemented.
20150712  Toggle button is implemented.
20150712  Confirmation dialog is implemented.
              -1.2-
20150510  Dropdown widget is implemented.
20150425  Now we don't have the array text_entries. We store them in the
          normal widget list. Also, destruction is now supported for
          text entries. 
              -1.1-
20150408  Lists, with and without icons

API Documentation 2.x

Using the Code Generator

The source tree also includes a code generator without a GUI. This is called gfxgen. Here is the usage for it:
  gfxgen [-c source.c] [-h header.h] input.scr
The given source and header file paths override any paths stored inside the input file. If these paths aren't given in the command line or in the input file, then generic names are used. In this case, source file is called 'gfx.c', inside the directory of the input file. It's 'gfx.h' for the header file.

Using The Layout Editor

In the editor, when you're asked for widget coordinates, you can use the variables WIDTH and HEIGHT to refer to the dimensions of the screen.

When you're editing screen coordinates, the same variables refer to the dimensions of the application window.

Initialization - X11

You should initialize the library using the following function:
  int gfx_init(char *name, int width, int height, char *title);
Here, name is the X11 display name. width and height are in pixels. The given title is for the main window of the application.

Events

int gfx_event(unsigned int *msecs,gfx_event_t *Revent);
Will return non-zero if there is an event to be handled. If msecs is NULL, then the function will wait until an event arrives. Otherwise, it will wait for a maximum of (*msecs) milliseconds and will return zero if no event was recieved during this time. In any case, if msecs is not NULL, it contains the remaining wait time upon return.

Confirmation Dialog

int gfx_ask(char *title, char *buttons, char *fmt, ...);
The confirmation dialog asks the user a question and returns an answer. If the user tries to close the window, -1 is returned. Otherwise, the index of the clicked button is returned.

The button string consists of a series of button labels, seperated by a particular character. This character is the first byte in the button string. For example:

  "!OK!Cancel"
describes a two-button dialog. Return value 0 corresponds to the OK button and 1 corresponds to the Cancel button.

fmt is a printf format, and the rest of the arguments are printf arguments.

title can be NULL.

File Selection

  char* gfx_select_file(int mode,char *init,char **types);
The return value is a non-null, absolute file path if the dialog was not cancelled. Otherwise, it's null. mode is zero for loading files and 1 for saving files. init contains the initial value for the file name. This is interpreted relative to the current directory if it's not an absolute path.

types is an array of file types. Each entry looks like:

  "Name (.ext1 .ext2)"
Each of these strings will be displayed in a file type box and can be activated to filter the list of files according to the extensions given. The file types can also be listed hierarchially. If a file type string starts with a space, it's the child node of a previous entry with less number of leading spaces. For example:
  "Image Files",
  " JPEG Images (.jpg .jpeg)",
  " Portable Network Graphics (.png)",
Here, the JPEG and PNG entries are the children of the "Image Files" entry. The latter doesn't have any extensions associated with it. When the user selects the "Image Files" entry, files matching the extensions of any child node are shown. It's possible to nest these even deeply using more and more leading spaces, but I don't think more than two levels are very practical.

The dialog also checks for the existence of the selected file. If the mode is 0 and a non-existant file name is entered, then the dialog doesn't close. Also, if mode is 1 and an existing file is selected/entered, the dialog will require user confirmation for overwriting it.

Widgets

Canvas

The canvas widget reports mouse clicks. The event type is
   GFXEV(widgetnumber,BUTTON_PRESS) 
      or 
   GFXEV(widgetnumber,BUTTON_RELEASE)
The x and y coordinates of the event are relative to the canvas buffer origin. The rest of the event is the same as a normal button press/release event.

In addition to this, mouse movement reports can also be obtained. These look similar to the press and release events. The type is:

  GFXEV(widgetnumber,POINTER_MOVE)
The following functions should be used to start and stop mouse position reporting. While mouse position reporting for the canvas is active, there is no other event generated for mouse position changes.
void gfx_canvas_begin_report_mouse_position(int wno);
void gfx_canvas_end_report_mouse_position(int wno);
The reported coordinates are relative to the widget coordinates (not buffer coordinates, this should be fixed).

The widget includes its own buffer. You can set the size of this buffer using:

void gfx_canvas_setsize(int wno, int new_w, int new_h);
Contents of the old buffer (if any) is copied to the new buffer. Using this function is mandatory, without a buffer the widget doesn't display anything. The widget automatically adds/removes scrollbars depending on the size of the buffer and the widget window.

In the layout editor, horizontal and vertical scrollers can be disabled. This doesn't affect the buffer sizes to be used. You can still use larger buffers but the widget will only display the top left part of the buffer. If you call the following function before setting any buffer size, a buffer will be created which is as big as the widget itself.

int gfx_canvas_get_buffer
   (int wno, unsigned int *W, unsigned int *H, unsigned int **B);
If you call this function after you have set the buffer size, the function will simply give you back the set width and height, along with the created buffer.

When you get the buffer using the above function, the returned buffer is in RGB32 format. You can manipulate this buffer and then put your changes back using:

void gfx_canvas_update(int wno, int x, int y, int w, int h);

The widget also lets you make rectangular selections using the following functions:

void gfx_canvas_recsel_begin(int wno, gfx_event_t *ev);
void gfx_canvas_recsel_end(int wno,int *Rx1,int *Ry1,int *Rx2,int *Ry2);
The _begin function sets the first corner of the selection and starts tracking the mouse pointer position. The _end function returns this first corner and the second corner obtained from the mouse pointer position. The returned coordinates are sorted: x1 ≤ x2 and y1 ≤ y2.

Canvas widget can also print text on the buffer, using the builtin fonts.

void gfx_canvas_print(int wno, int font, int color,
                      int x, int y, char *str);
The text is blended onto the buffer contents, using the given foreground color.

The canvas can optionally show a zoom sub-window. The parameters are set using:

void gfx_canvas_set_zoom_params
  (int wno, int zf, int grid, unsigned int gridcolor,int w,int h);
zf is the zoom factor. grid indicates the thickness of the grid lines. Give -1 here if you don't want grid lines. w and h give the dimensions of the zoom window.
void gfx_canvas_zoom(int wno,int x,int y);
This function shows the zoom window. x and y are the coordinates of the main buffer pixel which will be displayed in the center of the zoom window. The position of the zoom window is adjusted automatically.
void gfx_canvas_hide_zoom(int wno);
hides the zoom window. Call canvas_zoom() again to show it back.

File List

char *gfx_filelist_get_directory(int wno);

char** gfx_filelist_get_files(int wno);
  Returns all the files currently listed in the widget.
  The returned pointer can be freed in one go because
  the whole thing is in one block. The list is null
  terminated.

  Each entry has a prefix character indicating its type:
  'D' (directory), 'F' (file) and 'U' (unknown). 

void gfx_filelist_set_directory(int wno,char *dir);

void gfx_filelist_set_types(int wno, char **types);
  Sets the list of extensions to look for as explained in
  the file selector dialog section.
When a directory is clicked, the list shows that directory. For other entries, an event is returned. The event type is the widget ident for a single click and GFXEV(ident,DOUBLE_CLICK) for a double click.

The 'string' field of the event contains the absolute path of the clicked file. This string doesn't contain a type prefix. This field must be freed by the user. WARNING Not handling click or double-click events will cause a memory leak from this field.

List

The list widget emits an event with its own identifier when an item is clicked. The following fields are set within the event object:

Radio Button

When you want to set a radio button to ON, you simply call
   gfx_set_state(WIDGET_NUMBER, 1);
This will set all the other radio buttons to OFF. If you want to make sure that nothing is set within the group, you can follow the above call with
   gfx_set_state(WIDGET_NUMBER, 0);
Initially, when a radio widget is shown, it's in OFF state. You can get the widget number of which radio button is selected using the function
  R= gfx_get_selected(WIDGET_NUMBER);
where the widget number is that of any radio button within the intended group. R is -1 if no button is active.

Bugs

Things to Do

Roadmap

Things to Do

Things I Won't Do

Resources

There are two kinds of resources: user and library. Library resources define how the library drawn things look. For example, the folder icon in a file selector is a library resource. An icon to be displayed inside a button is a user resource. User resources are injected into the gfx.c source file using a marker.

There are library resources with the following types:

The layout editor can't edit library resources, but it should be possible to do so later. All library resources are listed using macros like RES_INT, RES_COLOR etc.

Later on, the layout editor will be able to merge .scr files and override resources in one file from another.

Coding Style

Externally visible names should start with gfx_. Internal names with gui_.

The shown/notshown flag in a widget should be called 'enabled'.

Some Notes About Tablet and Desktop Implementations

Differences between desktop and tablet programs:

- D: I specify screen size
  T: The whole screen is used and its size is constant
- T: There are some events such as pause,resume, low memory etc.
  D: There is nothing like that
- D: There is a keyboard attached at all times
  T: A soft keyboard is needed. A hardware keyboard might be present
     but I don't know how to activate it if there is one.
- D: Scrollbars and tiny controls make sense
  T: They don't.
- T: It's possible to have multiple pointers due to multi-touch
  D: Only one pointer is present
- T: The screen could be rotated, resulting in resize events
  D: Resize events are normally disabled. If we enable them, we
     get arbitrarily sized windows.
- T: There is no command line, init state is used.
  D: Command line arguments are used, there is no init state.
- D: No polling is necessary to make things move.
  T: We have to poll the system in order to get events like
     WindowCreated etc. Otherwise, the whole thing hangs up.
 
In order to solve the polling problem, I could do the following:
  - Make all callbacks return pretty much immediately, without
    waiting for anyone to acknowledge.
  - Do the actual work in these callbacks, but protect shared variables
    using locks and such.
  - Spin a new thread for events. 
  - Have the event and callback threads put some events on the queue.
  - Make the main thread read from this queue only.

API Documentation

The following information relates to the 1.x series. In that series, each widget was accessible thru its own functions. i.e. you said gfx_text_entry_set_text() etc instead of gfx_set_text().

dropdown Widget

int  gfx_dropdown(int x,int y,int w,int h,int action);
void gfx_dropdown_insert(int wno, int pos, const char *str, void *usr);
int  gfx_dropdown_remove(int wno, int pos, void **Rusr);
void gfx_dropdown_select(int wno, int pos);
int  gfx_dropdown_selected(int wno);
int  gfx_dropdown_get(int wno, int pos, char **Rstr, void **Rusr);
Typical usage is as follows:
  int dd;
                            // just a made-up integer for reporting
                            // selection change events.
  dd= gfx_dropdown(x,y,w,h, EVENT_DROPDOWN1);
                         // if pos is -1, the item is appended to the 
                         // list otherwise it's inserted at the given
                         // position.
  gfx_dropdown_insert(dd, -1, "An item", itemdata);
                              // String to be shown. This string is 
                              // copied into the widget.
                                         // Data associated with the
                                         // string. When the given 
                                         // string is selected, this
                                         // pointer will be stored in
                                         // the 'usr' field of the
                                         // returned event.
  gfx_show(dd, 1);
              // The widget is initially hidden.
  while(1)
    if (gfx_event(NULL, &event))
     switch(event.type)
     {
     case EVENT_DROPDOWN1:
          // event.key is the position of the selected item
          // event.usr is the data associated with the item
     }
Currently, there is only one value for _show(). The widget can be shown, but not hidden. I'll do that later.

The _remove() function returns 1 if the given position was valid. If Rusr isn't NULL, then user data associted with the item is stored there. Here is a way of getting rid of temporary user data:

                               // -1 means the end of the list. using 
                               // this is much more efficient than using
                               // another position as there is no 
                               // copying.
  while(gfx_dropdown_remove(dd, -1, &ptr))
    free(ptr);
The _get() function returns 0 if the position is outside the bounds of the list. Otherwise, the string and user data are stored in the respective pointers if not null.

The _selected() function returns -1 if there is no selected item. Otherwise, the position of the selected item is returned.

radio Widget

      // If group is -1, then this radio is in a group by itself.
      // Otherwise, group should be the widget number of another
      // radio widget. When this is the case, the two radio buttons
      // are in the same group.
      //
      // action is an event number as always. ident is an arbitrary
      // number which is used to identify the widget within its group.
int gfx_radio(int x,int y, int w,int h, const char *text,
              int action, int ident, int group);


               // Selects the given widget and clears the others in the group
void gfx_radio_set(int wno);

               // Returns the 'ident' of the selected widget within the group.
               // Returns -1 if nothing is selected yet.
int gfx_radio_get(int wno);

checkbox Widget

int gfx_checkbox(int x,int y, int w,int h, const char *text, int action);
void gfx_checkbox_set(int wno,int value);
int gfx_checkbox_get(int wno);
_get() returns -1 if the given widget can't be found.

If action is non-zero in the constructor, whenever the checkbox is clicked, an event will be generated. The type field of the event will be equal to the value of 'action' given here. The 'key' field of the event will be equal to the state of the checkbox.

text_entry Widget

int gfx_text_entry(int x, int y, int w, int h, char *initstr);
char *gfx_text_entry_get(int no);
void gfx_text_entry_set(int flags, int no, char *str);
void gfx_text_entry_filter(int wno, void *arg, 
                           int (*f)(void *,void*, gfx_event_t*));
The widget is initially hidden, use gfx_show() to enable it. The string returned by _get() is a copy of the text, you should free() it. The string passed to _set() is copied along, you don't need to keep the reference after the call.

The _filter function installs a filter for the entry. The filter has three arguments, the argument given as 'arg', a dummy argument which should be ignored, and the event. If you return 0 from this function, the event is processed as usual. If you return 1, then the event shouldn't be processed any further.

list Widget

int gfx_list(int flags, int x,int y,int w,int h, int click_action);
flags can contain GFX_NOICON and GFX_MULTISELECT. The former makes the list never show any icons even if they are given along with element data.

click_action is an event number. When an element is clicked, an event with this type is returned to the user. The following elements of the event record is set:

string
The text shown for the item.
usr
The user pointer associated with the item.
key
Index of the clicked item within the list
void gfx_list_insert
      (int widget, int pos, char *str, void *usr, 
      unsigned int *icon, unsigned int icon_w, unsigned int icon_h);
void gfx_list_delete(int widget, int pos, int N);
If pos is out of list bounds, the given item is inserted to or deleted from the end of the list.

FIXME: [001] insert() should also specify whether the item is to be selected or not.

int gfx_list_selected(int wno);
int *gfx_list_selected_all(int wno);
void gfx_list_select(int wno, int pos,int sel);
The first one gets the 'first' selected item, to be used for single-select lists. The second one returns an array of integers, terminated by -1. Each integer in the array corresponds to the index of a selected item.

FIXME: [002] The returned array is malloc()ed each time. Maybe this should be a member of the list struct and free()d automatically when the widget is destroyed.

FIXME: [003] The list struct should contain a 'selected' field to record the currently selected item. This will provide O(1) access to the selected item for single-select lists. Currently, we're looping thru the list items even for single-select lists.