Graphics Library

This library provides GUI functions on Linux/X11, Android, Linux/Framebuffer and Windows.

Screens are designed with a layout editor, which compiles the design into bytecode. The resulting object is then used with a single-file library.

3.2 No more graphical editor.
3.1 First version with pure bytecode format.
3.0 Many improvements, including initalization and gfx_event function.
2.16 Code is now completely bytecode.
2.15 Initial remote display work.
2.14 Partial Windows port.
2.12 Flexible font system. Removed dependency on freetype.
2.11 Last version with FreeType.
2.5 Log widget, initial Android port and improved editor.
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.0 Initial.

Contents

Installation

The final product of building and installing the source code is a single executable called gfxlay. This works only on Linux. In order to build it, do the following. After unpacking the gfx-X package, change into the created directory and run
./build.sh
from there. The resulting binary will be in
  layout/gfxlay
Put this binary somewhere in $PATH and you're done. There is nothing else to install. You may now remove the source tree.

Using the Tool

gfxlay generates everything you need. It can generate library source and header, as well as GUI data related to your project. When used with no options, it starts up a graphical editor to edit your design file.

Let's look at its options.

gfxlay: unknown option -k                                                       
gfxlay: usage: gfxlay [options] input-file                                      
options:                                                                        
   -d <file>     Definitions output file (C header)                             
   -n            Suppress version checking in definitions output (C header)     
   -s            Suppress version definition in definitions output (C header)   
   -H <file>     Combined C header output file (lib+data)                       
   -e <file>     Definitions output file (CSV)                                  
   -a <file>     Array output file                                              
   -b <file>     Binary output file                                             
   -C <file>     Combined C source output file (lib+data)                       
   -c <file>     Library code output file                                       
   -h <file>     Library header output file                                     
   -u            Disassemble input                                              
   -U            Compile input and disassemble the result                       
   -t <int>      Run input binary file, with the given screen number            
   -r            Remove input binary file after exit (only for -t)              
   -T <screen>   Compile and run input file with given screen name              
   -v            Print version information and exit   
After editing your design file, you'll run gfxlay from the command line or from a makefile to generate the necessary output files. In normal usage, you'll need
  1. a library code file which contains the library itself
  2. a library header file to be able to call functions from the above
  3. a GUI data header which enumerates your screens, widgets and icons so that you can communicate with the library
  4. finally, a GUI data file which contains the bytecode describing the structure of your screens as well as some resources.
1 and 2 above can be obtained by using the -h and -c options.

The option -d creates a C header which contains enumerations naming screens, widgets and icons in your design file. You will use these names to talk to the library about what to do.

The option -a creates a C source file which contains the bytecode necessary to run your GUI. The data in there gets passed to the library automatically by the gfx_init function.

The above setup works quite well for developing a program. If you want to simplify your distributed source tree, you can use the -H and -C options to combine library code and GUI description into two files; one header and one source file.

Remaining options are normally for internal use by gfxlay or for developing the library. They are pretty self-explanatory I think.

Using the Library

In order to use the library, you need to first include the necessary headers. Here I use some generic names, how you name these is up to you.
#include "libgfx.h"
#include "guidata.h"
The first one contains definitions relating to the library, while the second defines screens and widgets in your GUI. All files generated by gfxlay are C files. You should wrap them in extern "C" blocks if you need C++.

While linking, you will need to compile in your GUI data file and bring in some system libraries:

program: main.o <some other objects> guidata.o
	$(CC) $(LFLAGS) -o $@ $^ $(LIBS) -lXext -lX11 -lm
For targets other than X11, only -lm is required.

Now, in order to initialize the GUI system, you need to call gfx_init(). This takes a single argument on Linux/X11 (display name) and Windows (HINSTANCE). Most of the time, you will pass NULL here.

For Android, it's a little more complicated, which I will describe further down.

In order to display something, you need to enter a screen. When you do so, widgets in it are created and laid out by the library according to your design. You don't need to call create_button or something.

Screens form a stack. After you enter a screen, you will leave it in order to return to the previous screen. This is accomplished by

  gfx_screen(<screen_id>);
  ...
  gfx_leave_screen();
screen_id is just an integer constant from your guidata.h. Once you display a screen, you can interact with the widgets on that screen by using the functions in libgfx.h. Look at the
widgets section for reference.

In order to read events from the user, the gfx_event function shall be used:

  gfx_event_t event;
  while(!quit)
  {
    if (gfx_event(&event))
       switch(event.type)
       {
       ...
       case GFXEV_QUIT: quit= 1; break;
       ...
       }
  }
This function returns non-zero if there was a reportable event. Otherwise, some other event was read and it needs to be restarted.

The event type is always an integer constant. This can be

User Timers

The following functions handle user timers:
  void gfx_timer_start(int ident, int ms);
  void gfx_timer_stop(int ident);
The ident is a non-negative integer, which is independent of anything else. It can be any value. When a timer expires, it generates an event with type GFXEV(ident,TIMEOUT). The timer then stays inactive until gfx_timer_start() is called again. You can optionally cancel it before it expires using the gfx_timer_stop() function. If you give a negative integer for the ident field, these functions do nothing. So, -1 is a good sentinel value for variables holding timer identifiers.

Input Events

The following input events are reported, when not handled by the graphics system. For instance, if a mouse button click is handled by a list widget, then you won't see that click. You won't need to handle any of these unless you're writing a custom widget.
GFXEV_BUTTON_PRESS, GFXEV_BUTTON_RELEASE, GFXEV_DOUBLE_CLICK, GFXEV_POINTER_MOVE
Mouse event.
GFXEV_KEY_PRESS, GFXEV_KEY_RELEASE
Keyboard event.
For mouse events, the relevant fields of the event structure are: For keyboard events, the following fields are filled in: Some input events are also reported by some widgets. For instance, canvas and pixgrid widgets report mouse events.

System Events

The following are generated by the system.
GFXEV_TIMEOUT
A timer expires, refer to User Timers.
GFXEV_JAVARESULT
Used on Android.
GFXEV_QUIT
Happens when the user tries to close the application window thru the window manager.
GFXEV_FOCUS
When the keyboard focus changes to a new widget.

File Monitor Events

The library has the ability to monitor file descriptors thru the select() UNIX interface. This will eventually be removed from the library once an event injection scheme is in place. It works on Linux/X11, Linux/FrameBuffer and Android targets.

The following functions are used to manage monitored file descriptors:

void gfx_monitor_file(int fd, int ident, int R,int W,int X);                    
void gfx_stop_monitor_file(int fd,int ident);                                   
The ident is a simple integer constant determined by you. The R, W and X arguments are flags which tell the system which events to monitor: reading, writing and exceptions.

When a monitored event occurs, a gfx event is generated with type:

The key field of the event contains the corresponding file descriptor while the wide field holds the identifier given in the gfx_monitor_file call.

Widgets

The library provides some generic functions to interact with widgets. Not all functions are applicable to all widgets, of course. Here is our first couple:
void gfx_set_state(int wno, int state);                                         
int  gfx_get_state(int wno);
These two functions manage the state of a widget such as a check-box, toggle button etc.
void gfx_set_selected(int wno,int sel);                                         
int  gfx_get_selected(int wno);  
These are used to manage the selected item in a list, dropdown etc.
void gfx_set_fgbg(int wno, int fg, int bg); 
Some widgets support changing colors at run time.
char* gfx_get_text(int wno);                                                    
void gfx_set_text(int wno,char *str); 
Things like text entries, labels, buttons respond to these kinds of requests.
void gfx_clear(int wno);                                                        
void gfx_insert(int wno,int pos, char *text,void *user,unsigned int* icon);     
void gfx_remove(int wno, int pos, void **Ruser);   
These are used to insert/remove items. Lists, dropdowns etc support these operations. The position is an index which starts at 0.

If pos is out of bounds for insertion (ie. too big or negative), the item is appended to the list. For removal, the last element is removed in this case.

The widgets can also store some user data along with the item, which is stored and retrieved as shown. There is no way to modify an item once it's inserted. A removal and re-insertion is necessary.

FIXME: document the icon parameter.

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 buffer origin.

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.

Starting from 2.8, you can also have sprites in a canvas widget. These are ARGB32 images which can move around without affecting buffer contents. The following can be used for manipulating them:

  int gfx_sprite_new(int wno,int func, int w,int h, uint32_t *data);
  void gfx_sprite_move(int wno,int sp, int x,int y);
  void gfx_sprite_show(int wno,int sp,int sho);
  void gfx_sprite_remove(int wno, int sp);
In _new(), the return value is a sprite number valid for only the given widget. func is the function to be performed on the sprite and buffer data when it's displayed: The data is copied to the sprite, you may reuse or free it after _new(). Please note that each sprite has a copy of its own data, nothing is shared there. Also, having a couple of hundred sprites shouldn't hurt much, but thousands will definitely do. The whole is list traversed linearly, doing it otherwise will necessiate some complex data structures. I won't do that, because I basicly made this to have some sort of a shaped cursor.

When you move the sprite, the coordinates refer to the top left of the given image data. You can move them partially or completely off the screen, it won't do any damage.

Dropdown List

To insert items, use the common function:
void gfx_insert
    (int wno,int pos, char *text,
     void *user,unsigned int* icon);
If pos is -1, then the item is appended to the list. The icon is not used and should be NULL. The user pointer is returned along with the clicked item's position when a selection is made.

When the user chooses an option, the widget returns an event where the event's type equals the widget number. The key field contains the index of the clicked item. The usr field contains the user pointer passed along in the gfx_insert() call.

You can get the index of the selected item later on, using the function

  int  gfx_get_selected(int wno);
The result is -1 if there is no selection.

You can select an item programmatically using:

  void gfx_set_selected(int wno,int sel);
If sel is -1, no item will be selected and the box will be empty.

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.

Icon List

The icon list widget has a different _insert() function because it doesn't take an icon as an argument, it takes a rgb32 image. It goes like this:
void gfx_iconlist_insert
   (int wno, int pos, char *text, 
    unsigned int w, unsigned int h, unsigned int *data,
    int ival, void *pval);
As always, if pos<0, then the item is inserted at the very beginning. data must not be NULL. It should be a valid rgb32 image with the given width and height. The given ival and pval values are associated with the item and are reported whenever the item gets selected thru a mouse click.

The widget emits an event with the following fields, when an item is clicked and gets selected:

If you're going to insert/delete a lot of items, you can call:
  void gfx_iconlist_updates(int wno, int upd);
with upd==0. After doing your modifications in bulk, you should call the same function with upd==1. This way, the display isn't updated during the bulk operations, but delayed until upd==1.

You can use the regular gfx_remove, gfx_clear, gfx_set_selected and gfx_get_selected functions for other operations.

Now we have multi-select capability for icon lists. The following functions can be used to manipulate the selection:

void gfx_iconlist_get_selection(int wno,int *Rn, int **Rs);
void gfx_iconlist_clear_selection(int wno);
For get(), both output arguments are optional. The array in (*Rs) is terminated with a -1.

You can also permute the contents as follows:

void gfx_iconlist_permute(int wno, int N, int *P);
N should be equal to the number of items in the list. It's used as a crosscheck to make sure that the user has the correct number of items.

The function

void gfx_iconlist_area(int wno, int *Rw, int *Rh);
can be used to decide on an icon size. The returned dimensions are the size of the whole widget. A vertical scrollbar may be subtracted from the area later on.

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:

Log Viewer

In order to print something into this widget, you should use:
   gfx_logview_printf(WIDGET, fmt, ...);
You can use the following function to make it easier to write such print calls:
  void gfx_set_log(int widget);
After this call, you can use loprintf/loprintv function as follows:
  void loprintf(const char *fmt, ...);
  void loprintv(const char *fmt, va_list ap);
All such calls will be displayed on the widget given to the _set_log() call. If the widget given there is -1, then loprintf() doesn't print anything.

PixGrid

This widget provides a simple way of drawing on the screen. Unlike the canvas, it doesn't provide any buffers and doesn't do scrolling. There are two ways to paint on it. First, you can draw on your own buffer and then tell the pixgrid to display it. Second method is to draw on the window buffer directly.
void gfx_pixgrid_attribs
      (int pg, int *Rshown, unsigned int *Rw,
       unsigned int *Rh);
void gfx_pixgrid_put
      (int pg, int x,int y,
       unsigned int *img, unsigned int iW,unsigned int iH,
       int src_x, int src_y, 
       int copy_w, int copy_h);
void gfx_pixgrid_putsimple(int pg, unsigned int *img);
_attribs() is pretty self explanatory. *Rshown is nonzero iff the pixgrid is shown on the screen and is not obscured by a sub-window.

_put() displays the given portion of the supplied buffer. The buffer is in XRGB format. Bits 31-24: don't care, bits 23-16: red etc.

_putsimple() does the same thing as _put(), but with fixed arguments

It basicly updates the whole widget. For the second method, we have the following:
int gfx_pixgrid_buffer
    (int pg, int *Rbpl, int *Rbpp, int *Rorder,
     unsigned char **Rbuffer);
void gfx_pixgrid_update
    (int pg, int x,int y,int w, int h);
_buffer() returns bytes per line, bytes per pixel and pixel order in addition to part of the window buffer where the pixgrid starts. Rorder is a 3-byte array with the following values upon return: After writing to the given buffer, just call _update() to put your stuff on the screen. Note that this doesn't do any clipping etc. Be careful to not write outside the area of the pixgrid.

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 in the same group 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 the selected radio button in a group by 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.

Dialogs

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.

Change Log

              -3.1-
20210724  The output is now completely bytecode, helped by special
          instructions to identify parts of the binary description.
20210724  User fonts can be added to the output file, but can only
          be used in a canvas. Later on, all text-based widgets 
          shall be able to use whatever font we need.
20210724  gfxgen is no more. A gfxlay.nox is generated which is 
          meant to be used only for developing gfx itself. All other 
          functions are collected into gfxlay.
20210724  Code and data are now completely separated.
20210724  Internal improvements, getting rid of many output buffer
          types.
20210724  The generate screen is now defunct. Code/data generation 
          can only be done from the command line.
              -3.0-
20191112  Removed the timeout parameter from gfx_event.
20191110  Fixed a long-standing bug in alignment functionality
          in the layout editor.
20191110  Resource save/load code removed from save.c
20191110  Window width/height/title is part of the GUI design 
          file.
20191110  Now there is a 'generate' screen. This gives me 
          options to generate a GUI in release/debug settings.
              -2.16-
20191107  Removed library/. Everything is now in libsrc/
20191107  Incorporated lodepng. No longer dependent on libpng. 
20191107  Library code is now stored compressed inside the 
          layout and gen binaries, using the incorporated miniz
          code.
20191107  Icons for buttons now work. Multi-dpi icons.
20191107  Resource related code almost completely removed. 
          Just need to remove it from save.c
20191107  Icons are now part of the GUI description module.
20191107  Drastically reduced size of the source tarballs.
              -2.15-
20191102  Generate bytecode instead of C.
20191102  gfxlay/gen no longer print out library code. Just the
          bytecode necessary to use the library.
              -2.14-
20191027  Added partial windows support.
20191027  Removed all t2c code. Now it's all shell scripts.
              -2.13-
20190704  A new bug was introduced to the logviewer widget 
          along with the new font system. Now fixed.
20190704  User timers are now implemented on Linux.
              -2.12-
20190614  Made a layout editor screen for custom fonts. Code 
          still needs to be written.
20190614  Combined the library into one U file. All that remains
          is to modify the build scripts to use the new file 
          layout.
          After that, I can simply make them straight C+H files.
20190614  On Android GFX_SAVE_STATE event is removed. Instead,
          you now set a function pointer which is used to handle
          this situation.
20190529  Now fonts are used dynamically, scaled according to 
          the DPI of the device.
              -2.11-
20190528  Fixed a bug regarding window creation/destruction
          on android.
20190528  Implemented monitor_file functionality for android.
20181015  Added the gfx_soft_keyboard() function for android.
20180515  Modified the pixgrid widget to provide access to the
          window buffer directly.
20180515  Added loprintv. 
              -2.10-
20180120  Various changes, I forgot to record them here. 
20180120  Android subsystem now can handle user-defined timers.
20180120  Android subsystem can receive user-generated messages
          thru the haveresult() native.
              -2.9-
20171104  Icon list widget is implemented.
              -2.8-
20171102  Sprites are implemented.
20171102  Bug fixed in canvas mouse position reporting.
              -2.7.3-
20171027  Progress bar widget is implemented.
              -2.7.2-
20171024  loprintf
20171024  Log viewer can update itself even if a screen is 
          entered on top of it.
20171024  When a new screen is pushed, the old contents are now 
          stored in the previous screen, not the newly pushed 
          screen. This enables widget update while the screen is 
          in the background.
20171024  New image types graypix_t and colorpix_t are 
          implemented.  These should be used instead of passing
          a ton of function arguments.
              -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

Things to Do

Roadmap

Things to Do

Things I Won't Do

Bugs

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.

Android Interface

Starting with 2.10, the gfx system is able to receive user-generated messages from the Java environment. It works as follows: you start an asynchronous task in the Java environment thru JNI and set up the java code such that native function
  void YourActivity::haveresult(int value);
gets called when it finishes. This native function is defined by the gfx library. It constructs an event with type==GFXEV(value,JAVARESULT). Upon receiving this event, you can query the fields of YourActivity to get more detailed results.

In order to activate this behaviour, you need to define the name of your activity in the compilation flags as follows:

  cc '-DGFX_ANDROID_ACTIVITY="YourActivity"' -DGFX_ANDROID gfx.c
The double quotes are part of the definition because the code looks up the code using this macro without any further processing, so it needs to be a string literal.

You also need to declare the haveresult function in your activity class:

  class YourActivity extends NativeActivity
  {
..
    static { System.loadLibrary("sonamewithoutsuffix"); }
    public static native void haveresult(int rcode);
..
  }
The first static code is necessary because you need to load the .so since you no longer use NativeActivity directly. The second line above declares haveresult() properly.

Do not forget to set hasCode="true" in AndroidManifest.xml and the code lives under src/packagename/YourActivity.java

Mandatory Functions to be Present in Your Android Activity Class

In order for the fonts to scale properly, we need the display DPI. This is retrieved from the java world via the following function. Put this in your activity class:
public int getDisplayDPI()
{
  DisplayMetrics dm = new DisplayMetrics();
  getWindowManager().getDefaultDisplay().getMetrics(dm);
  float xDpi = dm.xdpi;
  float yDpi = dm.ydpi;
  if (xDpi>yDpi) return (int) xDpi; else return (int) yDpi;
}

Writing New Widgets

You need to write a couple of mandatory functions: The constructor will return a [widget_t*]. In the constructor, you need to set the class identifier and the function pointers.

If the widget is supposed respond to click events, you need to install a click handler at this point. You can change the parameters of the click handler later on.

When the position of the widget changes, don't forget to update the click handlers.

The click handler function should return 1 if the event has been completely processed and shouldn't be propagated further. It should return 0 if the event is to be reported to the user. This is your chance to modify the event so that a click event is returned to the user as an item selection event, for example.

The prototype for a click handler is:

  int click_handler_func(void *arg,gfx_event_t *event);
arg is typically a pointer to the widget object.

Using Scroll Bars

If your widget uses scrollbars, you can set the scrollbar position using:
  scrollbar_set_all(scrollbar, double start, end, step);
Here, start and end refer to the position of the slider in the scrollbar, 1.0 is the very end of the document and 0.0 is the beginning. step is again a number between 0.0 and 1.0 and tells us how much the position should progress when the arrow buttons are pressed. A good value is viewport_h/document_h*0.8.

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.