diff options
author | Bernie Innocenti <bernie@codewiz.org> | 2010-05-03 21:53:47 (GMT) |
---|---|---|
committer | Bernie Innocenti <bernie@codewiz.org> | 2010-05-03 21:53:47 (GMT) |
commit | 1030dc837b10a03a02a85d5504cbeec168ce49e2 (patch) | |
tree | 698eefa87ac437deaf36a4141b326f8ce7986692 /doc/xaosdev.texinfo |
Import XaoS r489 (trunk after version 3.5)
Diffstat (limited to 'doc/xaosdev.texinfo')
-rw-r--r-- | doc/xaosdev.texinfo | 2842 |
1 files changed, 2842 insertions, 0 deletions
diff --git a/doc/xaosdev.texinfo b/doc/xaosdev.texinfo new file mode 100644 index 0000000..49203d8 --- /dev/null +++ b/doc/xaosdev.texinfo @@ -0,0 +1,2842 @@ +\input texinfo @c -*-texinfo-*- + +@c Use A4 paper - If you don't like that, remove the following 3 lines. +@iftex +@afourpaper +@end iftex + +@setfilename xaosdev.info +@settitle A fast real-time interactive fractal zoomer --- hacker's guide +@dircategory Graphics +@direntry +* XaoS: (xaosdev). The fast real-time interactive fractal zoomer + (developers documentation) +@end direntry + + +@ifinfo +@copyright{} 1997 Jan Hubicka + +Permission is granted to make and distribute verbatim +copies of this manual provided the copyright notice and +this permission notice are preserved on all copies. + +@end ifinfo + +@c %**end of header + +@set VERSION 3.1.1 +@set DATE Feb 5, 2005 + +@titlepage + +@title XaoS @value{VERSION} +@subtitle A fast real-time fractal zoomer --- hacker's guide + +@author Jan Hubi@v cka +@tex +Dukelsk\'ych bojovn\'\i ku 1944 +@end tex +@* +390 03 T@'abor @* +Czech Republic + +Email: @code{jh@@ucw.cz} + +@value{DATE} + +@page +@vskip 0pt plus 1filll +@vskip 0pt plus 1filll + +@copyright{} 1997 @tex Jan Hubi\v cka +@end tex + +Permission is granted to make and distribute verbatim +copies of this manual provided the copyright notice and +this permission notice are preserved on all copies. + +@end titlepage + +@c %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +@node Top, , (dir), (dir) + +@ifinfo +@top XaoS @value{VERSION} +@flushright 1.0 +A real-time interactive fractal zoomer +Hacker's guide +@value{DATE} +@end flushright + +This manual contains documentation for those who are interested in +studying and improving the XaoS sources or using them in other programs. +It includes a description of the algorithm and documentation of those parts +of XaoS that I think might be useful for others. +@end ifinfo + +@menu +* design:: Overview of the XaoS design +* driver:: Driver API description +* gui-driver:: Writing user interface driver +* eui:: Writing an external user interface +* ui-helper:: UI helper library +* xthreads:: XaoS thread library +* filters:: Filters +* algorithm:: Algorithm description +* timerlib:: The timer library +* registry:: XaoS function registry +* index:: Function command and variable index +@end menu + +@node design, driver ,Top ,Top +@chapter Overview of the XaoS design + +XaoS is divided into several ``libraries'' (some of them have been merged +together; nonetheless the conceptual divisions are there and they can be +separated easily). + +Understanding the design philosophy should help you to navigate in the sources. +I also expect that much of the lower level stuff may be useful in other +projects, since it is designed to be fairly generic. + +So here is an overview, from the lowest level stuff to the highest. + +@section Palette and image library + +@findex image +@findex palette +The sources for this are in the directory @code{src/filter}. +The aim of the palette library is to provide a relatively abstract interface to +the various visuals, and hide differences in the hardware and driver +implementations. Fixed color, pseudocolor, grayscale and truecolor visuals +should be handled in almost the same way. + +It provides the @code{palette} structure, containing a palette. You might +allocate new colors here (you give an RGB value and the corresponding pixel +is returned), interpolate colors where possible, cycle colors and so on. + +Every palette contains two parts --- the preallocated color cells and the actual +palette. For instance, this could be used to allow the GUI the possibility to +allocate static, unchanging colors for its texts and dialogs, while the rest of +the palette is under the control of different parts of XaoS. + +This library also contains a set of functions to allocate different palettes +used by other parts of XaOS. I expected that different parts of XaoS could share +the same palette. This hasn't happened yet, but the functions are kept here just +in case. + +The image library is built on top of the palette library, providing +functionality for handling actual image data. Each image is represented by one +or two frame-buffers (useful for double-buffering). One frame-buffer is +called `current' and the other `old'. They are flipped by a special function. +The program can draw into either of them. + +Frame-buffers are held as a set of pointers to scan-lines. This provides more +flexibility than more obvious representations, because tricks like sub-windows +and flipped bitmaps are possible. It's also fast, since you should avoid one +multiplication. + +The last significant information the image structure holds is of course the bpp +depth. It is counted in bytes, and ranges from 0--4 (where 0 is used for 1bit +bitmaps). + +@section Filter library +Sources are available in @code{src/filter}. +This library controls the process of image creation. It handles a queue of +filters, where each filter modifies the image in some way. The filters at the +beginning and end of the queue are special; the first filter is usually the +actual fractal engine which creates the image, and the last filter is usually +the user interface helper library. + +@section Xthread library +This library provides an interface to various multi-threading libraries; +currently the BeOS, plan9 and POSIX implementations are available. It allows the +running of various functions in parallel, and provides some synchronization +primitives (semaphores). It is simple, but has all the functionality required +for the XaoS engine. + +@section Fractal library +Sources are available in @code{src/engine/}, headers in @code{fractal.h}. +This library contains the actual fractal calculation routines. It is governed by +a fractal context, which contains information like the current formula, the seed +(for julia sets), the palette, and so on. + +Functions for calculating the various fractal types and various coloring modes +are also available here. + +@section Zooming engine and other filters. +Sources are available in @code{src/engine/}. +This is the actual zooming engine filter. It is fairly independent of the +fractal library, so it could possibly be used for zooming other stuff. (It has +already been used for zooming large scale images containing maps of Hungary). + +The file @code{ui_helper} contains the implementation of all filters not already +mentioned, and a structure containing all functions exported from the filter to +the user interface. One other terminal filter is implemented --- the Julia +morpher. Other filters add special effects (such as motion blur), or do +conversions (such as rotation or dithering). + +@section Timer library +This library provides many useful timing primitives (such as timers). +It is used by some other programs too. + +@section xio library +This library aims to provide a unified, portable interface to the file +system. Some strange systems (such as MacOS) have a very different filesystem +API to UNIX; perhaps they don't represent filenames as a string, use special +structures, or something; this library abstracts away from all that. + +@section xshl library +Xshl stands for XaoS simple hypertext library. It contains a fairly universal +engine parsing an `xshl' language, similar to HTML, with some additions and many +restrictions. It can render text for both proportional and monospaced fonts, in +various sizes. + +@section help library +This is built on top of the xshl and xio libraries. It can read help files +containing help chapters, can parse keywords in chapters, and so on. + +@section xmenu library +This is the XaoS function registry. All functions from UI-Helper library are +registered in the registry. From this registry the menus, dialogs, command +line options and scripting language are built. + +@section Catalog library +This is a library for handling message catalogs. It should read catalogs and +convert keywords into an actual message. + +@section PNG library +This library provides a function for saving an image from the Image library to a +file, in PNG format. Other formats could be added as well if required. + +@section UI-helper library +This library controls all the low-level stuff and provides a high-level +interface to it. It has functions for playing animations, zooming/un-zooming +and suchlike. It all the other libraries heavily. It doesn't implement +functions for handling menus and such, but could be helpful for such +implementations, because of the function registry database. + +@section Ugly interface +This is currently the only real user interface for XaoS (there is another, wich +is used for rendering animations, but it is not a user interface as users +understand the term). It is built above the UI-helper library and provides +functions for drawing menus, dialogs and so on. It has drivers for many +platforms, and can be easily ported to others. + +In the future, it should be quite easily extended to let drivers specify their +own menu/dialog handling code; so it should be possible to give it a ``native'' +look on each platform. + +It can also operate in a mode where the GUI drawing routines are disabled; the +function registry database is transferred through a pipe to an external program, +which should build the menus and act as an external user interface, sending back +commands in the scripting language (presumably representing things that the user +has done). This provides another way to give a native look to the ugly interface, +and could also be used to interface other scripting languages to XaoS. + +The ugly interface has one serious limitation --- for historical reasons, it is +coded to handle just one window (the rest of XaoS can probably do multiple +windows, but this is untested), so in windowed environments it is impossible to +open multiple menus with fractals. On the other hand, this limitation is not so +important once external GUIs enter the picture, as they could just start +multiple XaoS engines. This is also preferred because it brings extra +robustness, multitasking and some other advantages. That's why I don't plan to +remove this limitation yet. + +@node driver, gui-driver ,design ,Top +@chapter Driver API description + +To port XaoS successfully to some platform you need: + +@itemize @bullet +@item +An ISO C compatible optimizing compiler. Note that an optimizing compiler is +really required, since XaoS is coded to be a good target for optimizations and +doesn't have many routines coded in assembly; so if you will use a bad compiler, +you will notice drastic slowdowns (a slowdown of 10x or more). Some compilers +have serious problems compiling XaoS; this applies to most DOS compilers (Watcom +C, Borland C, Microsoft C) for instance. They generate incorrect code or crash +during compilation. I highly recommend using the GNU C compiler, but even some +versions of GNU C have difficulty. Please read @code{compilers.txt} for more +information. +@item +A fast way to avoid division by zero/overflow and other floating point +exceptions. XaoS is carefully coded to not crash in this case, but doesn't have +any tests to avoid such situations; expect random results in such cases. Many +platforms provide a means to switch coprocessor into a mode where 1/0 produces +Inf and so on. If there is no such means, try to use some kind of signal handler +that will ignore such exceptions. + +The ``normal'' solution --- add @code{if}s to avoid division by zero --- is +almost impossible. Division is quite easy to check for, but other cases (such as +overflows) are much harder. I don't think it is possible to avoid all crashes +just by adding @code{if}s. + +XaoS doesn't depend on IEEE arithmetic. The result in the sort of edge cases +where IEEE makes a difference is mostly undefined. XaoS usually works well with +compiler switches for inexact math enabled (such as @code{-ffast-math} in GCC), +but, of course, there are no guarantees. For example on Alphas this is not true, +since they usually generate exceptions in that case. +@item +A text or graphics output device. If you only have a text output device you may +use the AA driver, which renders fractals into high quality ASCII art. In this +case you might want to skip this chapter, download AA-lib +(http://www.ta.jcu.cz/aa) and read the porting chapter of AAlib manual. A +graphics device must be one of: + +@findex C256 +@findex FIXEDCOLOR +@findex GRAYSCALE +@findex TRUECOLOR +@findex TRUECOLOR24 +@findex TRUECOLOR32 +@itemize @bullet +@item 8 bits per pixel with user definable palette @code{C256}, static palette @code{FIXEDCOLOR}, or static grayscale @code{GRAYSCALE} +@item 16 bits per pixel with arbitrary bits per color @code{TRUECOLOR} +@item 24 bits per pixel with 8 bits per color, arbitrary order @code{TRUECOLOR24} +@item 32 bits per pixel with arbitrary order of colors, where each color fits in exactly one byte @code{TRUECOLOR} +@item 1 bit per pixel bitmap, either least or most significant bit first +@end itemize + +Please contact me if you have a different kind of device. Some modes (like +mis-ordered truecolor modes) could be added really easily if required. Note +that mono/4/16 colors devices will probably never be supported internally by +XaoS, since I expect they will be slower than 8bpp, so XaoS will internally work +in 8bpp and then convert to 1/2/4bpp. Contact me if you want to write such a +converter. (For bitmap one already exists --- see @code{dither.c}.) +@item +Some way to save images. By default XaoS uses @code{libpng}, which is ported to +many platforms, but there are many platforms where it isn't ported yet. If your +system has some standard image format which is easier to handle than +@code{.png}, contact me and I will show you how to add such support to XaoS (see +@code{png.c}). +@item +Stdio compatible library (this is a problem on the Mac and BeOS). XaoS has an +abstract layer above stdio, so it can use other input/output libraries too. You +could write another implementation of this layer, called @code{xio}. See +@code{xio.h}, @code{xstdio.c}, and @code{xstdio_mac.h} for an example. +@end itemize + +The ugly interface is designed to make writing new drivers as easy as possible. +Use the file @code{ui_template} when starting to write a new driver from +scratch. You need to write only a few functions, filling out the following +table: +@findex ui_driver +@example +struct ui_driver @{ + char *name; + int (*init)(void); /*initializing function. returns 0 if fail*/ + void (*getsize)(int *,int *); /*get current size..in full-screen versions + i.e svga and dos asks user for it*/ + void (*processevents)(int,int *,int *,int *,int *); + /*processevents..calls ui_resize,ui_key + also returns positions of mouse.. + waits for event if first parameter is + 1*/ + void (*getmouse)(int *,int *,int *); + /*returns current mouse positions*/ + void (*uninit)(); /*called before exit*/ + int (*set_color)(int,int,int,int); + /*alloc palette color and returns number*/ + int (*set_range)(ui_palette *palette,int start,int end) + /*Set palette range*/ + void (*print)(int,int,char *);/*prints text*/ + void (*display)(); /*displays bitmap*/ + int (*alloc_buffers)(char **buffer1,char **buffer2);/*makes buffers*/ + void (*free_buffers)(char *buffer1,char *buffer2);/*frees buffers*/ + void (*flip_buffers)(void); /*prints text*/ + void (*mousetype) (int type); /*Change mouse cursor*/ + void (*flush) (void); /*Flush current state to screen*/ + int textwidth; /*width of text*/ + int textheight; /*height of text*/ + struct params *params; /*command line parameters*/ + int flags; + float width,height; + int maxwidth,maxheight; + int imagetype; + int palettestart,paletteend,maxentries; + int rmask, gmask, bmask; + struct gui_driver gui_driver; +@}; +@end example + +@section Functions + +@code{ui} uses the following functions to communicate with a driver: + +@defun init + Initializes the driver and returns 1 on success and 0 on failure. +@end defun +@defun getsize (int *@var{width}, int *@var{height}) + Returns the size of screen (or window) in x and y dimensions. +@end defun +@defun processevents (int @var{wait}, int *@var{x},int *@var{y}, int *@var{buttonmask}, int &@var{keys}) + Gets new keyboard/mouse events. Parameters are: + @table @var + @item wait + If 1, the function can block, waiting indefinitely for the next event, + otherwise it just determines if something's already arrived. This is + useful on multi-tasked OSes, where eating unnecessary CPU time on + busy-waiting is discouraged. + @item *x,*y + Returns the current mouse coordinates. + @item *b + Returns a mouse button bitmask of @code{BUTTON1}, @code{BUTTON2}, and @code{BUTTON3}. + @item *k + Returns a cursor key bitmask: + @table @code + @item 1 + @key{left} + @item 2 + @key{right} + @item 4 + @key{up} + @item 8 + @key{down} + @end table + @end table + The function calls @code{ui_key} (ASCII character) and @code{ui_resize} + if required. For special keys use @code{UIKEY_UP}, @code{UIKEY_DOWN}, etc. + See @code{ui.h} for a complete list of these constants. + + In case of problems freeing/allocating inside @code{processevents} you may + call @code{ui_call_resize}, which calls resize later outside this function. +@end defun + +@defun uninit + Uninitialises driver---called before exit. +@end defun + +@defun set_range (ui_palette *@var{palette}, int @var{start}, int @var{end}) +This is the preferred way to set the palette (the other way is with +@code{set_color}). When @var{imagetype} is @code{UI_C256} (256 color with +palette) one of these two functions @emph{must} be used. In truecolor modes they +are unused. If direct palette access is possible on your platform, define this +function. The function is expected to set all color cells between @var{start} +and @var{end} to colors defined in @var{palette}. @var{ui_palette} is array of +@var{ui_rgb} elements. @var{palette[0]} is the color of entry number +@var{start}. @var{ui_rgb} is an array of @code{char}. @var{palette[0][0]} is +the red field of entry number @var{start}, @var{palette[0][1]} is the green and +@var{palette[0][2]} is the blue. @code{0} means black and @code{255} means full +intensity. Use @code{NULL} if your driver doesn't support this call. +@end defun + +@defun set_color (int @var{r}, int @var{g}, int @var{b}, int @var{init}) + This is a secondary way to set the palette, that should be used at platforms + w/o direct palette access (like X11 or static color schemes). It receives the + RGB value of the color, and returns the index of the color cell with this + color, or -1 if no more color cells are available. The @var{init} parameter is + set to 1 when this function is called for the first time on a given + palette; @code{set_color} is then expected to free all color entries + previously allocated. Use @code{NULL} if your driver doesn't support + this call. +@end defun + +@defun print (int @var{x},int @var{y}, char *@var{text}) + Prints text to screen at position x/y. This function is a bit archaic + (XaoS now uses its own functions drawing directly to the buffer in most + cases), but in some cases --- initialization messages or calculation --- + the functions are unusable, so we still need this primitive. In the + @code{C256} mode you can rely on the first allocated color always being + black and the second being white. +@end defun + +@defun display (void) + Displays current buffer on screen +@end defun + +@defun alloc_buffers (char **@var{buffer1},char **@var{buffer2}) + Allocates two buffers that can hold a bitmap the size of the screen. + Also sets the current buffer to @var{buffer1}. Since version 2.1, this + returns the scan-line size in bytes (usually the screen width) or 0 + on failure. This is useful on systems that allocate a bitmap bigger + than the window/screen (divisible by 4 or thereabouts). +@end defun + +@defun free_buffers (char *@var{buffer1}, char *@var{buffer2}) + Frees allocated buffers. +@end defun + +@defun flip_buffer (void) + Flips buffers --- sets the current buffer to the other one. +@end defun + +@defun flush (void) + This function should be used by drivers with buffered output to flush + those buffers. Other drivers should set it to @code{NULL}. +@end defun + +@defun mousetype (int type) + This function is used to change the mouse cursor. @var{type} has the + following values: + @table @code + @item NORMALMOUSE + This pointer is that usually displayed, while the UI waits for user + input. + @item WAITMOUSE + This pointer is displayed when the UI is busy (perhaps the famous + hourglass) or you may use that defined in ui_dos --- a Mandelbrot + set. + @item REPLAYMOUSE + This pointer is displayed during replay. There should be none in + fullscreen drivers, since a blinking mouse cursor during replay + looks ugly. On windowed systems, disabling the mouse looks ugly, + so it should be some funny cursor. + @end table + You should use @code{NULL} if your driver doesn't support this. +@end defun + +@section Other information +Some additional variables are also used to inform the ui library about the +driver. All these values can be changed by init functions in case they were +unknown beforehand. + +@table @var +@item textheight, textwidth + Width and height of your font +@item palettestart, paletteend + First and last palette entry that can be changed. You should use this + to avoid changing entries reserved for the window system, UI objects, + mouse, etc. +@item rmask, gmask, bmask + These fields are used in truecolor modes to specify where each color is + defined. +@item maxentries; + Number of allocatable entries. Normally should be + @var{palettestart}-@var{paletteend} +@item imagetype + Defines the type of image. Should be one of the following values: +@findex UI_C256 +@findex UI_FIXEDCOLOR +@findex UI_GRAYSCALE +@findex UI_TRUECOLOR +@findex UI_TRUECOLOR24 +@findex UI_TRUECOLOR32 + @table @code + @item UI_C256 + The classical 256 color with palette scheme used by most older graphics + adapters. You should also use it for static-color schemes, but they are + not well supported in current versions of XaoS. + @item UI_TRUECOLOR + 32bpp truecolor mode + @item UI_TRUECOLOR24 + 24bpp truecolor mode + @item UI_TRUECOLOR16 + 16bpp truecolor mode + @end table +@end table + +What follows is @emph{not required} to get the driver working at first, +so you may skip to @emph{registering driver} on the first read and return +here later. + +@table @var + +@item params + You may define command line options for your driver. + + They are defined using a @var{params} structure like: +@findex params + @example + static struct params params[]=@{ + @{"-mode",P_NUMBER,&defmode, + "Select graphics mode(same number as in interactive menu)"@}, + @{NULL,0,NULL,NULL@} /*this MUST be last option field*/ + @}; + @end example + + where every line is one parameter. The list ends with @code{@{NULL,0,NULL,NULL@}}. + The first field is the option name. + The second field is the parameter type: +@findex P_SWITCH +@findex P_NUMBER +@findex P_STRING +@findex P_FLOAT + @table @code + @item P_SWITCH + No parameter --- variable is just set to 1 if the option is supplied. + @item P_NUMBER + Integer number + @item P_STRING + String + @item P_FLOAT + Floating point number (variable is float) + @end table + The third element is a pointer to a variable that is changed if the option is + supplied. (For instance, it is @code{int*} for @code{P_NUMBER} or + @code{P_SWITCH}.) + The last element is help text displayed by @code{ui -h}. + + +@item width,height + See @var{flags}. May be set to @code{0.0, 0.0} at the start. + +@item maxwidth,maxheight + See @var{flags}. May be set to 0,0 at the start. + +@item @var{flags} + This variable says more about your driver. A good starting value is 0. + But for the final version it is recommended to read the following carefully. + + Flags are uppercase constants and should be set as follows: + + @code{ASYNC_PALETTE | RANDOM_PALETTE_SIZE} + + The following flag constants are supported: + + @table @code +@findex RANDOM_PALETTE_SIZE +@findex UPDATE_AFTER_RESIZE +@findex RESIZE_COMMAND + @item RANDOM_PALETTE_SIZE + States that the palette is randomly sized. This is used in X where the + palette is shared between many programs. By default XaoS allocates all + available colors up to 256. This is not very nice to other applications in + X, so this flag states that that a random number of colors (in the range + 8--256) are allocated. + + When this variable is off XaoS expects that the same number of colors is + always available. + @item UPDATE_AFTER_RESIZE + Causes the screen to be recalculated and redrawn upon a resize, even if + its size has not changed, in case the resize procedure destroys data in + XaoS' buffers. + @item RESIZE_COMMAND + Some drivers (mainly the fullscreen ones) may ask the user for the size + and color depth to use, in the function @code{get_size}. It would be nice + to let the user change this parameter at runtime and force XaoS to + reinitialize all its images. This is done with the @code{ui_resize} + call + + In windowed drivers, this is called by external window-system events, but in + fullscreen drivers you'll need a key or menu item for this. You could add + this function directly into XaoS's function registry (see for example the + GGI driver) --- it is useful mainly when you want to use a size selection + dialog that is standard for your environment, or let XaoS use its default + one. For example, see the SVGAlib or DOS driver. + @end table + + Screen/window size information: + + Xaos needs to know the exact physical size of displayed images. This is + required for random dot stereograms and also for keeping the aspect ratio + of fractals correct (do not make them wide in 640x200 resolution etc.) + At least one of the following values should be defined (given in the order I + prefer) +@findex SCREENSIZE +@findex PIXELSIZE +@findex FULLSCREEN +@findex RESOLUTION + @table @code + @item SCREENSIZE + The @var{width}/@var{height} values specify the exact size of the + screen/window in centimeters + @item PIXELSIZE + The @var{width}/@var{height} values specify the exact size of one pixel in + centimeters. This is better for windowed environments, where the window + size is often changed. + @item FULLSCREEN + The driver runs fullscreen. XaoS automatically uses the default screen size + (29.0cm x 21.5cm) + @item RESOLUTION + The driver does not know the exact screen size, but knows the resolution + used (in the @var{width}/@var{height} variables); XaoS automatically + calculates pixel width using width = 29.0cm/maxwidth and height = + 21.5/maxheight. + @end table + Of course the default width and height can be changed by command line + options. You may also use combinations like: + + @table @code + @item SCREENSIZE | FULLSCREEN + The best for fullscreen drivers + @item PIXELSIZE | RESOLUTION + The best for windowed drivers + @item FULLSCREEN + For fullscreen drivers that have no idea about screen size... + @end table + + Do not forget to set the @var{width}, @var{height}, @var{maxwidth}, + @var{maxheight} fields if required. + +@item gui_driver + See the next section for description. +@end table + + +@section Registering the driver + +Once you've done the above, you just register the driver in @code{drivers.c} and +you may compile :) You can use @code{ui_template.c} as a driver template. + +You may also look at the xthreads library description if you are porting XaoS to +some SMP platform. + +Please let me know if you want to start to code a driver. + +@node gui-driver, eui ,driver ,Top +@chapter Writing a GUI driver + +XaoS has a builtin GUI. Many operating systems have native GUI toolkits and XaoS +default GUI might look strange there. To avoid this problem, you might write an +external GUI program (see eui section) or write mappings from the XaoS GUI +functions to the native ones. The advantage of an external GUI process is +multitasking; XaoS is not thread safe and the GUI must be synchronous with +calculation. Also, the ugly interface code currently doesn't support multiple +windows (although this should be solved in the future). This solution is +suitable mainly for those systems where two cooperating programs sharing one +window is a problem (like Windows). + +To write a GUI driver you need to fill out the following structure: +@example +struct gui_driver +@{ + void (*setrootmenu)(struct uih_context *c, char *name); + void (*enabledisable)(struct uih_context *c, char *name); + void (*menu)(struct uih_context *c, char *name); + void (*dialog)(struct uih_context *c, char *name); + void (*help)(struct uih_context *c, char *name); +@}; +@end example + +All functions have a @code{uih_context} parameter. You don't need to worry about +its contents; just pass it to the functions that require it. This parameter is +for multiple window support, which is not implemented yet. + +The @code{setrootmenu} function draws the root menu according to the menu called +@code{name}. To get the menu fields you might use the following piece of code: +@example +#include <ui.h> +#include <xmenu.h> + +.... + +int i; +menuitem *item; +for (i = 0; (item = menu_item (name, i)) != NULL; i++) + @{ + if (item->type == MENU_SUBMENU) @{ + /* This field is a submenu. You might call a function to + construct the submenu here. item->shortname contains + the name for the submenu. */ + @} + /* Add menu field here. + + You might check flags here: + item->flags&MENUFLAG_CHECKBOX + field has a checkbox + item->flags&MENUFLAG_RADIO + field is part of a radio button group. In the current + implementation, there is one radio button group per menu. + + in both cases you can call menu_enabled(uih, item) to see + if the item is checked or not. + + item->name contains the field's text + + item->key contains a hotkey (a one letter string in the + current implementation) + @} +@end example +Once the field is selected, call the function @code{ui_menuactivate(item, NULL)} +where @code{item} is a pointer to the @code{menuitem} record of the selected +field. + +The @code{enabledisable} function is called when the checkbox or radiobutton +state is changed. The @code{name} parameter is the same as the +@code{item->shortname} of the changed field, so you need to search all the +menus, compare @code{name} to @code{item->shortname}, and if it matches, call +@code{menu_enabled} to get the new state. For radiobuttons, only enable events +are noticed. Your code is expected to automatically disable all other +radiobuttons in the same submenu. + +The @code{menu} function works similarly to @code{setrootmenu}, but displays a +popup menu. + +The @code{dialog} function is called for dialogs. The function should look like: +@example +menuitem *item = menu_findcommand(name); +menudialog *dialog = menu_getdialog(uih, item); +int i; +for(i=0; dialog[i].question; i++) +@{ + /* Construct dialog, where the left side contains labels with + dialog[i].question, and the right side contains input entities based on the + dialog[i].type. Dialog[i].type is one of the following: + + DIALOG_INT: integer value input. The default value is: dialog[i].defint + DIALOG_FLOAT: floating point input value (long double, where available). + Default value is dialog: dialog[i].deffloat + DIALOG_COORD: complex value floating point input (two floats), default + values are dialog[i].deffloat and dialog[i].deffloat2 + DIALOG_STRING: string input. Default value is dialog[i].defstr + DIALOG_IFILE: input file + DIALOG_OFILE: output file + default mask is dialog[i].defstr + DIALOG_CHOICE: choice between various strings. + cast dialog[i].defstr to char ** to get a pointer to a NULL + terminated array of choices. +@} +@end example +Once the dialog is filled by the user, @code{gui_driver} is expected to allocate +an array of union @code{dialogparam}: +@example +dialogparam *p = calloc (sizeof (*p), nitems); +@end example +and fill in the selected values. @code{p[i].dint} is used to pass an integer +value, or the number of a DIALOG_CHOICE selection, @code{p[i].number} is used +for floating point numbers, @code{p[i].dstring} for strings and filenames, +@code{p[i].dcoord[0]} and @code{p[i].dcoord[1]} for complex values. + +The string values are expected to be in separately @code{malloc}ed chunks. Once +the array is filled, call @code{ui_menuactivate(item, p)}. + +The function @code{help} is used to display help about a given topic. To +implement it you could either convert XaoS help file to some native format, or +use the xshl library to render the help page for you. To render an xshl page +use: +@example +#include <xshl.h> +xshl_line *lines; +int getwidth (void *data, int flags, char *text) +@{ + return width of text with given flags + flags is mask of the following: + XSHL_BIG - large text + XSHL_EMPH - emphasized text + XSHL_MONOSPACE - monospaced text (typewriter) + XSHL_LINK - line (should be underlined or suchlike) + XSHL_WHITE + XSHL_RED + XSHL_BLACK - color of text (not very meaningful here) + XSHL_RIGHTALIGN + XSHL_CENTERALIGN - alignment of text + +@} +lines = help_make (name, getwidth, textheight, largetextheight); +if (lines == NULL) + lines = help_make ("main", getwidth, textheight, largetextheight); + +@end example +Now you might use @code{lines} to draw the help. It is a pointer to the array of +structures: +@example +struct xshl_line @{ + int y; + struct xshl_item *first; +@}; +@end example +@var{y} is the position of the line from the beginning of the text, and +@var{first} is a pointer to the blocks of text on the line. The last line +contains a @code{NULL} pointer in the first section. + +@code{first} is a linked list of @code{xshl_item} structures: +@example +struct xshl_item @{ + struct xshl_context c; + char *text; + int x; + int width; + struct xshl_item *next; +@}; +@end example + +You can draw @code{text} at position @code{x} (and @code{y} from the line +record) using the text style described by @code{xshl_context}: +@example +struct xshl_context @{ + int flags; + char *linktext; +@}; +@end example +@code{flags} has the same meaning as in the @code{getwidth} +section. @code{linktext} is the name of the next help page when the field has +the @code{XSHL_LINK} atribute. + +For an example of @code{gui_driver}, see the win32 driver code. + + + + +@node eui, ui-helper ,gui-driver ,Top +@chapter Writing an external user interface + +This part describes how to make an external user interface --- that is, a +separate program which constructs a window with all menus and dialogs. It uses +the XaoS engine to calculate the fractal as a separate process. This design +brings many advantages --- the external GUI implementation could have a ``native +look'' for given platform and could contain many extensions (perhaps multiple +windows). Also all calculation is done while multitasking, so the user interface +is usable even when the engine is busy. + +The X Window System provides a way for programs to draw into others' windows --- +the ``master'' program creates window the sub-window where it wants to put the +fractal, then calls the engine with @code{-windowid} @var{number_of_window} +parameters. Instead of creating a new window, the engine uses uses the specified +window. The most famous example of such cooperation is probably +ghostscript/ghostview. + +Other windowed environments probably provide similar means for for +cooperation. In most others it will probably be implemented using shared memory, +so it should work on most platforms, I expect. + +Of course, you might also design the UI as a separate button box in another +window, like most animation players, or ImageMagick do. In fact, the external +GUI could be very similar in style to ImageMagick... + +@section Basic concept + +The UI implementation has a function to disable its GUI functions. Because of +the function registry, all its menus and dialogs are described in a fairly +simple database, which is also mapped to the Scheme-like scripting language. +The external UI implementation can just translate the user's actions into this +scripting language and send that through the pipe. + +The commands, menus, and dialogs should be created automatically from the +database, so the UI doesn't need to have special code for each XaoS feature. At +the beginning it should use XaoS' @code{(print_menus)} command to force it to +send information about the database, then build menus using this information. + +For this you only need some equivalent to UNIX pipes, so again I expect it is +doable on most platforms. + +@section Starting XaoS as a slave process + +One of the first things the engine needs to do is to initialize XaoS in the +right mode to work as a slave process. For this you need to do several things: + + +@itemize @bullet +@item Open the pipe +@item Disable the builtin GUI +@item Read the menu hierarchy (this is optional --- GUI can also have all menus +coded into it. But it is not recommended, since it will cause problems with +adding new features in future) +@end itemize + +Opening pipe is done via the @code{-pipe} option. It takes one parameter, the +name of the FIFO you want to use. If you specify ``@code{-}'', XaoS will read +input from stdin. + +To disable the XaoS GUI use @code{-nogui}. This will disable all menus, dialogs +and help text. + +To read the menu hierarchy, just add @code{-print_menus} parameter and then +parse XaoS's output. This will print the whole menu hierarchy. If you are +building menus at the time they are selected, you might prefer to use the +@code{print_menu} command, which prints just one menu without its submenus; its +output could be immediately used for building a menu. The command takes one +string parameter, the name the menu you want to print; i.e., to print the root +menu use @code{-print_menu root}. + +Under the X Window System you also need to specify the @code{-windowid}; also +the @code{-shared} option is quite recommended. Otherwise in pseudocolor visuals +XaoS will create its own colormap, wich will likely collide with the UI's +colormap, and either XaoS or the UI will have wrong colors. If you have any +idea how to avoid this, let me know. + +You might also let the user specify some extra parameters from the command line, +by simply adding those options to the end of the command line. The @code{-nogui} +and @code{-print_menus} commands must be first for a simple reason: XaoS parses +its command line early in initialization. Some commands (like +@code{-print_menus}) should be processed at this time, while others (like +@code{-loadpos} need a working engine. Such commands are queued and processed +later, once the engine is initialized. XaoS never reorders commands; if an +option that requires queuing is located before @code{-print_menus} on the +command line, it will queue @code{-print_menus} too. This will cause the menus +to be printed much later, slowing startup. + +So the proper calling sequence for the user interface under X should look like: + + +@example +xaos -nogui -print_menus -windowid <id> -share -pipe - @var{[other options]} +@end example + +@section Parsing the menu structure + +The structure is printed menu by menu. Each menu contains a header, some +entries, then @code{endmenu}. The listing from @code{print_menus} is terminated +by @code{endmenus}. + +The header starts with @code{menu} and then contains a menu identifier, and the +menu's full name, e.g. + +@example +menu "fractal" "Fractal" +@end example + +Each entry which follows has its own line. It starts with a menu type, which +should be @code{submenu} or @code{menuentry}. + +@code{submenu} has a similar format to the header --- the full menu name, and an +identifier. + +The next few fields are @code{menuentry}-related. It has an entry type, which +could be @code{normal}, @code{radio} or @code{checkbox}. @code{radio} and +@code{checkbox} are followed by @code{on} or @code{off} specifying whether it is +enabled or disabled by default. (The radio-buttons don't have explicit +information about which group they belong to. For now I expect that each menu +contains just one such group, so it is clear in any case.) + +A set of flags should follow. Currently two flags are defined: @code{dialog}, +wich specifies that the function has a dialog box, and +@code{dialogatdisable}. By default, dialogs for check-boxed functions are +displayed only when the checkboxes are enabled. The second flag reverses this +behaviour. It is currently used for the @code{mandelbrot} function, which +behaves in this way; when you disable it, the user is prompted for the Julia +seed. + +So a specification should look something like this: + +@example +menu fractal "Fractal" +submenu "formulae" "mformula" +submenu "Incoloring mode" "mincoloring" +submenu "Outcoloring mode" "moutcoloring" +submenu "Plane" "mplane" +submenu "Palette" "palette" +menuentry "Mandelbrot mode" "uimandelbrot" checkbox off dialogatdisable dialog +menuentry "Perturbation" "uiperturbation" checkbox off dialog +menuentry "View" "uiview" normal dialog +menuentry "Reset to defaults" "initstate" normal +endmenu +@end example + +@section Activating functions and dialogs + +Once the menu structure is built and the user selects some item, it should be +activated. This is done by a simple command: @code{(@var{name})}. Once +``@code{)}'' is sent, the command is executed by XaoS. + +Check-boxed functions has one extra parameter --- @code{#t} to enable them and +@code{#f} to disable. So if you want to enable the @code{autopilot} item, send: +@code{autopilot #t} + +Radio-buttons don't have any such parameter, because disabling radio-buttons +makes no sense. + +If the item has a dialog enabled, the engine expects that the UI will make the +dialog first, ask the user for values and then pass back a function with +parameters. But first, the UI needs to know what parameters the function +expects. This is done by sending the command @code{(print_dialog +"@var{name}")}. XaoS replies with a dialog specification very similar to the +menu specification. + +This has the header @code{dialog} followed by the name of the function being +described. Then one dialog entry per line is sent, started by +@code{dialogentry}, followed by the question the UI should display. Next is a +type, which should be one of the following: +@table @code +@item integer +Integer number such as @code{123} +@item float +Floating point number such as @code{123.123} +@item string +String such as @code{"ahoj"} +@item keyword +String such as @code{'ahoj}. Keywords are mostly similar to strings, except that +they can not contain spaces. They are used, for example, for specifying types of +formulae. Strings are used for printing text and so on. +@item inputfile +@item outputfile +Here the UI should display a file selection dialogs. With @code{outputfile} it +is also a good idea to check whether the file exists and ask the user if he +wants to overwrite it. +@item onoff +Boolean value (@code{#f}, or @code{#t}) +@item complex +Complex value --- two floating point numbers such as @code{123.123 123.123} +@item choice +Choice between some keywords. The keywords are sent after @code{choice}, +enclosed in @code{@{} @code{@}}. +@end table +The last information on the line is the default value, in the same format as the +examples above. For files, the default value is in the format +@code{"@var{[prefix]}*@var{[extension]}"}. + +Some examples: + +@example +customdialog "uiview" +dialogentry "center:" complex 0.000000 0.000000 +dialogentry "Radius:" float 0.000000 +dialogentry "Angle:" float 0.000000 +enddialog + +dialog "load" +dialogentry "Filename:" inputfile "fract*.xpf" +enddialog + +customdialog "color" +dialogentry "Color" choice @{white black red @}white +enddialog + +@end example + +To activate a function, send a command consisting of the function name followed +by a sequence of parameters, mapped one-to-one to dialog box fields, in the same +order as those fields. The parameters have the same format as in the above +examples; checkbox fields return @code{#t} and @code{#f}. For instance: + +@example +(uiview 0 0 0.5 0) +(load "text.xpf") +(color 'white) +@end example + +@section Synchronization + +In some cases, XaoS can change radio-box and check-box values itself (when user +presses a key, or loads some file, for instance). All such changes are sent to +the GUI so that it can update what the user sees correspondingly. They are sent +to standard output in the following format: + +@example +checkbox "name" on/off +radio "name" on/off +@end example + +Your GUI code should parse this and change its menus when necessary. + +XaoS's menus can contain multiple distinct trees. In some cases (like when +animation replay is active) the root of the menu structure should change. To +indicate this to the GUI, XaoS sends a command: + +@example +root "name" +@end example + +Also, the user can press keys which normally display menus, dialogs or +help. If XaoS has the keyboard focus, it will receive these instead of the +GUI. XaoS sends commands to indicate this: + +@example +menu "name" +dialog "name" +help "topic" +@end example + +All these commands should be taken into account by the GUI, or could be ignored +(not recommended!) + +@section help + +XaoS's help is in a simple hypertext language. In order to simplify its parsing, +I've made xshl and help libraries. Making a help window with these libraries +should be quite easy; just call the help function: +@deftypefn Function struct xshl_line *help_make (char *@var{command}, int @var{getwidth} (void *, int @var{flags}, char *@var{text}), int @var{width}, int @var{smallheight}, int @var{bigheight}); +@end deftypefn +and you will receive a stream of text with coordinates describing where to +display the text into the shared window. + +The @code{command} parameter is the help topic. The @code{getwidth} function +returns the width of a given piece of text. @code{width} is the width of the +window, @code{smallheight} is the height of the small font, and @code{bigheight} +is the height of the big font. + +Please ask me for more details if necessary. + +And thats all. Good luck with coding. + +@node ui-helper, xthreads ,eui ,Top +@chapter UI-helper library + +UI helper library takes care of all of XaoS' engine functions and features and +provides a higher level API which is quite easy to understand. If you want to +write a completely new user interface (a replacement for the ugly interface --- +not just new bindings for native menus or external user interfaces) or you want +to use the XaoS engine in your program, you will probably want to use this +library. + +Its API has many calls and features. This section gives a brief overview of its +calls. Please ask me for details. + +@section initialization + +To initialize the UI helper library, you need to prepare a palette and image. +The palette is created using the palette library call +@code{createpalette}. Creating a truecolor palette should look like this: + +@example + struct palette *pal = createpalette (0, 0, TRUECOLOR, 0, 0, NULL, + NULL, NULL, NULL); +@end example + +For details about creating palettes see @code{ui.c} or ask me. + +To create an image, call: + +@example + struct *image img = create_image_mem (width, height, 2, pal, + pixelwidth, pixelheight); +@end example + +This creates an image in memory. If you want to create it in your own buffers, +you might use @code{create_image_cont} or @code{create_image} calls. Again see +@code{ui.c}. + +Then it is time to fire up the main library: + +@example + struct uih_context *uih = uih_mkcontext (0, img, passfunc, + NULL, NULL); +@end example + +The @code{passfunc} is called when the engine is calculating. It might process +external events and display progress information. It should look like this: + +@example +static int +ui_passfunc (struct uih_context *c, int display, char *text, float percent) +@{ + /*process events */ + if (uih->display) + @{ + uih_drawwindows (uih); + /*display */ + @} + if (display) + @{ + if (percent) + sprintf (str, "%s %3.2f%% ", text, (double) percent); + else + sprintf (str, "%s ", text); + /*display it */ + @} +@} +@end example +It can set @code{uih->interrupt} if it wants to interrupt the current +calculation (whereupon the main calculation loop will return to its caller). + +You can also load the catalog file in order to make tutorials work: + +@example + uih_loadcatalog (uih, "english"); +@end example + +Once this is done, the ui_helper library is fully functional and you can enter +the main loop. + +@section main loop +The UI helper library does not have any timing primitives; so it expects a +standard form of main loop. It asks it caller to redisplay a changed image when +necessary. The library also uses the generic timerlib library for its timing, +for which see elsewhere in this document. + +The main loop should look like this: + +@example +while (1) + @{ + if (uih->display) + @{ + uih_prepare_image (uih); + uih_drawwindows(uih); + /*display current image buffer*/ + @} + uih_update (uih, mousex, mousey, buttons); + if ((time = tl_process_group (syncgroup, NULL)) != -1 && + !uih->inanimation) @{ + /*relax for the given time in usec - wait for events etc..*/ + @} + /*and repeat*/ + @} +@end example + +@section Calling functions + +The UI helper library has many functions declared in @code{ui_helper.h} for +various actions. There are too many of them to describe here, but their names +are quite informative, so I hope you will not have problems. + +(You could also use the XaoS function registry, which does all this stuff for +you; you will just draw menus and dialogs based on this registry and all +features will be automatically made available. If you are writing an ordinary +user interface, this is the preferred way.) + +Note that the @code{ui_helper} library is not reentrant, so you can't call most +of these functions from the @code{passfunc}. If you are using the registry, the +activating function handles this automatically and queues functions when +necessary. To process them you need to flush the queue in the main loop as +follows: + +@example +static void +processbuffer (void) +@{ + menuitem *item; + dialogparam *d; + if (uih->incalculation) + return; + while ((item = menu_delqueue (&d)) != NULL) + @{ + menu_menuactivate (item, d); + @} +@} +@end example + +@section closing library + +This is done using: + +@example + uih_freecontext (uih); +@end example + +One user of this library is the ugly interface code in XaoS; see the +@code{src/ui} directory. Another, much simpler user is +@code{src/ui-hlp/render.c}, which does animation rendering. + +@node xthreads, filters ,ui-helper ,Top +@chapter XaoS thread library + +This description should be useful for those who want to port XaoS to +multiprocessor platforms, and those who want to implement a filter or other +relatively computationally expensive code. Note that the thread library uses +nothread calls as a degenerate case when only one thread is used, when the host +does not allow multi-threading or it is not an SMP architecture (since this +library is used only to distribute calculation into other CPUs). + +XaoS thread library is a simple map of a few functions required by XaoS to the +system's library for threads. + +It has the following variables: + +@deffn Variable ethreads +This is set to 1 in the case that threads are enabled +@end deffn + +@deffn Variable nthreads +Number of threads +@end deffn + +It provides the following functions: + +@deftypefn Function {void} xth_init (int @var{threads}) +This function initializes the threading library (starts threads, sets +@var{ethread} to @code{1} and @var{nthreads} to @var{n}. @var{threads} parameter +should be set to 0 for auto-detection, or to the number of threads the user +wants. If threads is set to 1, the threading library is disabled and the +following functions are mapped to the @code{nothread_} equivalents defined in +@code{xthread.h}. + +Note that all threads are not interchangeable --- there is a main thread (the +one that called @code{xth_init}) that communicates with drivers, controls +calculation, and so on, and there are child threads that are waiting for orders +from the main thread. The latter threads can't use any functions from the +xthread library. +@end deftypefn + +@deftypefn Function {void} xth_uninit (void) +This function un-initializes the thread library --- kills child threads and sets +@var{ethread} to 0 and @var{nthreads} to 1. +@end deftypefn + +@deftypefn Function {void} xth_function (xfunction *@var{function}, void *data, int @var{range}) +This function is used when the engine wants to perform some operation on the +image in parallel. It is expected to wait until all threads are ready, then +start @var{function} on all threads, including the control one, with the +following parameters: @var{data} --- the same as @var{data} passed to +@code{xth_function}, @var{taskinfo} --- pointer to a platform-dependent +@code{taskinfo} structure (defined in @code{xthread.h}), but must have at least +a field @code{n}, that holds the thread number (where the control thread is +numbered 0 and other threads are numbered in the range 1 -- @var{nthreads}). +The next two parameters are the range of images across which the function is +expected to work. @code{xth_function} is expected to divide @var{range} into +@var{nthreads} equal pieces and pass to each thread the start of a piece and the +start of the next piece (@var{range} for the last one). The function does not +wait for other threads to finish at the end, but returns immediately to main +thread after @var{function} returns. + +This function is called approx. 5--10 times per frame. +@end deftypefn + +@deftypefn Function {void} xth_sync (void) +This function waits until all threads are ready for the next order from the main +task. + +This function is called approx 5--10 times per frame. +@end deftypefn + +@deftypefn Function {void} xth_bgjob (xfunction *@var{function}, void *@var{data}) +This function is expected to behave as follows: if there are any threads waiting +for orders, ask one of them to call @var{function} with similar conventions as +in @code{xth_function} except that the @var{range} parameters are set to 0. +Otherwise it starts function in the foreground, as usual. + +This function is called once per frame. +@end deftypefn + +@deftypefn Function {void} xth_nthread (struct taskinfo *@var{s}) +This function should be used to determine the current thread number. Do not use +@code{taskinfo->n} instead, since if threads are disabled this will be defined +to 0 to allow the optimizer to perform better optimizations. This function can +be called by all threads. +@end deftypefn + +@deftypefn Function {void} xth_lock (int @var{n}) +@end deftypefn +@deftypefn Function {void} xth_unlock (int @var{n}) +Lock/unlock lock number @var{n}. At least @code{MAXSEMAPHORS} locks must be +available. +@findex MAXSEMAPHORS + +Note that locks are used for very short fragments of code, so they need to be +fast; so spinlocks may be better than classical Dijkstra semaphores (although +this is untested). They are called once per calculated line/row during zoom and +once per approx 10 pixels during calculation of a new image. +@end deftypefn + +@findex MAXCONDS +@deftypefn Function {void} xth_sleep (int @var{n}, int @var{l}) +Expected to atomically unlock lock @var{l} and sleep in queue @var{n}. At least +@code{MAXCONDS} queues must be available. After the function is woken up, lock +@var{l} again. This mechanism is used by the new image calculation algorithm, +but it is designed to minimize its calls, so I expect it should be called once +or twice. +@end deftypefn + +@deftypefn Function {void} xth_wakeup (int @var{n}) +Wake up some thread from queue @var{n}. The lock used by sleep calls is locked +in this case. The function should wake up all threads if a single-thread awaken +is not supported by the host API. + +With luck, this function will not be called at all; it will be called by the new +image calculation routines when the queue is empty. This happens when there are +50 threads or thereabouts, but happens rarely at two or eight threads in my +tests. +@end deftypefn + +@deftypefn Function {void} xth_wakeall (int @var{n}) +Similar to wakeup but wake up all threads. +@end deftypefn + + +@node filters, algorithm ,xthreads ,Top +@chapter Filters + +This is a brief description of the filter system used internally by XaoS. +Filters in XaoS provide an object oriented interface to every part of the XaoS +engine. The main filters are the user interface implemented in ui_helper.c and +the zooming engine implemented in zoom.c. Active filters are kept in a queue --- +in the beginnning there are just two filters here (zoom and ui), but at any +later time additional filters (stereogram generation, and so on) can be inserted +into the middle of the queue. + +When calculating, every filter should use data calculated by the filter +immediately before it in the queue, which that filter placed into the image it +passes to its child. For example, the stereogram filter should take the fractal +generated by the zooming engine and create a stereogram from it (assuming that +the zooming engine is immediately after the zooming engine in the filter queue). + +This makes XaoS's code more flexible and makes future enhancements easy (perhaps +a different zooming engine, or image rotation, other special effects, plug-ins +or some other funny stuff) since the enhancements are forced to be decoupled by +the filter library, and since each filter has a degree of control over filters +that follow it in the queue. For instance, the stereogram filter should change +the palette, force the zooming engine to change the depth, width and height of +the calculated image to fit its needs, and so on. + +This document mainly describes the creation of a filter like the stereogram +generator --- i.e. a filter placed into the middle of the queue --- since I +don't expect there will be many people creating ``terminal'' filters (zooming +engines/user interface layers). Note that different user interfaces are +possible, since the user interface layer is not the real user interface, just a +set of high level functions that should be called by the main application, like +@code{set_view}. So if you want to use XaoS as a calculation engine in your +program this document is probably not for you. + +Each filter is defined by a @var{filteraction} structure, as follows:: +@findex filteraction +@example +struct filteraction @{ + char *name; + char *shortname; + int flags; + struct filter *(*getinstance)(struct filteraction *a); + void (*destroyinstance)(struct filter *f); + int (*doit)(struct filter *f,int flags,int time); + int (*requirement)(struct filter *f,struct requirements *r); + int (*initialize)(struct filter *f,struct initdata *i); + void (*convertup)(struct filter *f,int *x,int *y); + void (*convertdown)(struct filter *f,int *x,int *y); + void (*removefilter)(struct filter *f); +@}; +@end example +This structure describes unchanging parameters to the filter (like its name) and +a basic set of methods required for communication with the rest of XaoS. The +@var{name} field is a comparatively long description of the filter's name, such +as ``A random dot stereogram generator''. @var{name} is displayed by the ugly +interface in the Filters menu, so it is expected to be descriptive (but shorter +than 30 characters). The short name is a one word long name for the filter, like +``stereogram''. This name is used in save files and command line parameters; +everywhere that the user might need to write it, so writing a long descriptive +name would just be wasteful of time and disk space. + +The flags field is reserved for future enhancements and is expected to be 0 for +now. + +@section Instance creation / destruction + +Functions @code{getinstance} and @code{destroyinstance} are equivalent to the +constructor and destructor in object-oriented languages. @code{getinstance} is +expected to create and fill out the following structure: + +@findex filter +@example +struct filter @{ + struct filter *next,*previous; + struct queue *queue; + struct filteraction *action; + struct image *image,*childimage; + struct requirements req; + struct fractal_context *fractalc; + void *data; + char *name; + int flags; + void (*wait_function) (struct filter *f); + /*stuff for wait_function*/ + int pos,max,incalculation,readyforinterrupt,interrupt; + char *pass; +@}; +@end example +Although this structure seems to be long and complex, most of the fields are +currently unused, and the rest of them are filled out automatically by a helper +function: + +@deftypefn Function {struct filter *} createfilter (struct filteraction *@var{fa}); + +This function should be used to do the dirty work of instance creation and fill +out the @var{filter} structure. The only possibly interesting field is +@var{data}, a pointer reserved for the filter's internal use; it can be a +pointer to the filter's internal variables if required. This is what a +@code{getinstance} implementation that allocates such an additional structure +might look like: + +@findex getinstance +@example +static struct filter *getinstance(struct filteraction *a) +@{ + struct filter *f = createfilter(a); /*create filter structure*/ + struct stereogramdata *i=calloc(sizeof(*i),1); + /*allocate internal variables*/ + /*initialize your variables here*/ + f->data=i; /*add pointer to internal data*/ + return (f); +@} +@end example + +If nothing similar is required you can simply put @code{creatfilter} into the +@var{getinstance} field. + +@end deftypefn + +@code{destroyinstance} is expected to free the memory used by the filter +structure and all the filter's internal data. To free the filter structure use +the normal @code{free} call. An implementation of such function should look +something like +: +@example +static void destroyinstance(struct filter *f) +@{ + destroyinheredimage(f); + free(f->data); + free(f); +@} +@end example +The meaning of @code{destroyinheredimage} will be described later. + + +@section Initialization + +During the initialization phase, each filter says to its parent what kind of +images it supports (which should depend on the images that it's child has said +it supported), the parent chooses the best supported image format for its +purpose and gives that to the child (while passing that information on up the +queue of filters). Initialization is done in two passes: + +The first pass starts at the lowest filter in the queue (zoom, by default); each +filter passes a @var{requirements} structure to its parent. + +The second pass starts at the highest filter (the ui filter), and each filter +passes to its child an image and some other stuff. Then calculation should +begin. + +The queue needs to be reinitialized after creating, resizing, adding or removing +a filter, and similar operations. + +The first pass is implemented using the @code{require} function. This function +is expected to look at the child's requirements it received as a parameter, fill +out its own @var{requirements} structure, and call the @code{require} function +of its parent filter. +@findex requirements +@example +struct requirements @{ + int nimages; + int supportedmask; + int flags; +@}; +@end example +The @var{nimages} field should be set to 1 or 2. When it is 2, the parent filter +@emph{must} pass the image in two buffers (double-buffered). Note that if it is +1, the parent @emph{should} pass the image in two buffers, but is not required +to. + +@var{supportedmask} is a mask giving the image types supported by the +filter. Valid image types are: +@findex C256 +@findex FIXEDCOLOR +@findex GRAYSCALE +@findex TRUECOLOR +@findex TRUECOLOR24 +@findex TRUECOLOR32 +@table @code +@item C256 + A normal 8bpp image with palette +@item TRUECOLOR24 + A 24bpp truecolor image with 8 bits for each color. +@item TRUECOLOR16 + A 16bpp truecolor image +@item TRUECOLOR + A 32bpp truecolor image with 8 bits for each color. +@item LARGEITER + A 16bpp image, but without colors. The pixels are expected to hold an + iteration count; it could also be thought of as a 16bpp grayscale image. +@item SMALLITER + Similar to @code{LARGEITER}, but 8bpp. +@end table + +@findex MASK1BPP +If you don't want to worry about palettes, color allocations and so on, but just +want to do some non-display operation with a bitmap, you probably only care +about the image depth and not the precise meaning of the pixels; in that case, +you can use one of the bitmasks @code{MASK1BPP} for 8 bit images, +@code{MASK2BPP} for 16 bit and so on. + +The final field in the @var{requirements} structure is @var{flags}. It's a mask +composed from the following constants: + +@table @code +@item IMAGEDATA + Set this if your filter requires the data from previous frame untouched. When + this is not set, filters can reuse your image and change it. But some + filters, like motion blur or the zooming engine, require data from the + previous frame to construct the new one; for such filters, this flag should be + set. +@end table +There are no more flags supported at the moment. The @code{require} function +should also save the child's @code{requirements} structure into +@var{filter->req} for later use by the initialize pass. The code for a +@code{requirement} function might look like +@example +static int requirement(struct filter *f,struct requirements *r) +@{ + f->req=*r; /*Save an child's requirements*/ + r->nimages=1; /*Just one image is required*/ + r->flags&=~IMAGEDATA;/*unset the imagedata field*/ + r->supportedmask=C256|TRUECOLOR|HICOLOR|REALCOLOR; + /*mask of all supported image types*/ + return (f->next->action->requirement(f->next, r)); + /*call parent*/ +@} +@end example +The next pass is the main initialization pass. It goes in the opposite order +(from parent to child, from the top of the queue to the bottom, in the same +direction as image flow), and the child receives some stuff from the parent +(such as images). The @code{initialize} function receives an @var{initdata} +structure: +@findex initdata +@example +struct initdata @{ + void (*wait_function) (struct filter *f); + struct image *image; + struct fractal_context *fractalc; + int flags; +@}; +@end example +@var{wait_function} points to a function called by the filter during calculation +that lets the parent filter (usually the user interface layer) inform the user +of calculation progress. @var{image} is an image expected to be filled with an +image in the calculation phase. @var{fractalc} is a pointer to a structure that +will contain information about the fractal itself during calculation (formula +type and so on). @var{flags} is a mask of the following constants: +@table @code +@item DATALOST + This is set if the data in the previous image was lost (if the image was + cleared or resized or freshly allocated). Filters that use data from previous + frames should pay attention to this flag. The zooming engine, for example, + recalculates the whole image if this flag is set, since all pixels from + the previous frame were lost. + Note that data will also be lost if the filter receives a different + @var{image} than in the previous initialization (since some filter before it in + the queue was removed). +@end table +Inheritance is carried out using these functions: + +@deftypefn Function void inhermisc (struct filter *@var{f},struct initdata *@var{i}); + +This function sets fields in the filter structure like @var{fractalc} or +@var{wait_func}. Inheritance of images is quite complex, since the new image +needs to be prepared for the child filter. In order to save memory it is highly +recommended to use the same image --- or at least the same memory --- for data +when passing to the child, but this is not allays possible. The following +function implements a heuristic to reuse the image where possible: +@end deftypefn +@deftypefn Function int inherimage (struct filter *@var{f},struct initdata *@var{data}, int @var{flags}, int @var{width}, int @var{height}, struct palette *@var{palette}, float @var{pixelwidth}, float @var{pixelheight}) +You should call this function in your @code{initialize} pass. It fills out +@var{image} and @var{childimage} in the @var{filter} structure, and prepares +@var{initdata} and @var{image} for the child. Note that in some cases it may +fail and return 0. In this case the filter is expected to interrupt +initialization and return 0 too. + +The @var{flags} parameter is a mask of the following constants: +@table @code +@item IMAGEDATA + Set if your filter requires data from the previous frame. +@item TOUCHDATA + Set if your filter touches data in the output image. This is the usual case, + but some filters, like interlace or subwindow, don't touch the image data at + all. +@item NEWIMAGE + Set if your filter cannot use the same image for output as it uses for input + (that is, if the two images must be distinct blocks of memory). +@end table +@var{width} and @var{height} are the width and height of the image you want to +pass to the child; it should be set to 0 if you want the same width/height as in +the parent image. @var{palette} is the palette of the image you want to pass; +set to @code{NULL} if the palette should be inherited from the parent's image +(as is usual). @var{pixelwidth} and @var{pixelheight} give the physical size of +a pixel in centimeters; if set to 0 they are inherit from the parent's image. +@end deftypefn + +If you use the @code{inherimage} mechanism, you must also call +@code{destroyinheredimage} in the @code{destroyinstance} function and +@code{updateinheredimage} at the beginning of the @code{calculate} function. + +Example implementation: +@example +static int initialize(struct filter *f,struct initdata *i) +@{struct stereogramdata *s=f->data; + inhermisc(f,i); + if(!inherimage(f,i,TOUCHIMAGE,0,0,NULL,0,0) return 0; + /*initialize here*/ + return(f->previous->action->initialize(f->previous,i)); +@} +@end example +Also note that the fractal context holds a pointer to the fractal's palette. If +you don't change the image's palette everything is OK; but if the child's image +differs from the parent's, there should be two behaviors --- the fractal's +palette is the child's one (this is common in color conversion filters, going +from 8bpp to TrueColor and suchlike), or the fractal's palette is the parent's +one (like in the edge detection filter). By default the fractal's palette is set +to the parent's one, because this is most likely to be generally useful; +anything else requires explicit work from the parent to set up the child's new +palette. + +This can be changed by the @code{setfractalpalette} call, which has two +parameters --- the @var{filter} structure, and the new palette. When you pass +the child's palette as @var{palette}, the fractal's palette will be changed to +the child's. If you pass @code{NULL}, changing the palette will be disabled (as +in the motion blur filter in 8bpp mode). This is only changeable if you still +have access to the fractal's palette; some parent might have already redirected +the palette beforehand, in which case this function does nothing. + + +@section Calculation + +The calculation is done using the @code{doit} function: + +@deftypefn Function int (*doit)(struct filter *f,int flags,int time) + +This function is expected to call the child's calculation function when +required, and apply its filter to the child's output. + +The @var{flags} are mostly undefined; only @code{INTERRUPTIBLE} is defined for +now, and @emph{that} is mainly for the zooming engine so I do not describe it +here. Nonetheless, the filter is expected to pass the @var{flags} to its +child. Finally, @var{time} is the time in milliseconds that expired since the +last @code{doit} call. It can be used to calculate the animation speed, perhaps +in an attempt to keep the speed constant. + +@end deftypefn + +Calculation loops return a bitmask composed of the following flags: +@findex ANIMATION +@findex CHANGED +@findex INEXACT +@table @code +@item ANIMATION + Set if the filter performs some animation, and expects that its calculation + function will be called again soon. +@item CHANGED + Set if something changed in the output image (the usual case). +@item INEXACT + This is enabled by the zooming engine in @code{INTERRUPTIBLE} mode in case the + @var{time} was exceeded. +@end table + +Most @code{doit} functions change the image. The @var{image} structure contains +following fields that might be significant to filters: +@findex image +@table @code +@item bytesperpixel + Number of bytes per pixel (image depth). +@item palette + Palette of image. +@item currlines + Array of pointers to the beginning of each scanline in the image. +@item oldlines + Like @var{currlines}, but for the previous image, when double-buffering is + enabled. +@item nimages + Set to 2 when double-buffering is active. +@item flipimage + Pointer to a function that flips @var{oldlines} and @var{currlines}. +@end table + +The @var{palette} structure contains the following significant fields: + +@table @code +@item type + Type of palette/image (@code{C256}, @code{TRUECOLOR} etc...) +@item size + The number of allocated entries in the palette. +@item pixels + The array of allocated entries; a conversion table mapping from the iteration + number to a pixel value. +@item rgb + @sc{RGB} values for pixels (@code{NULL} for @code{TRUECOLOR}, @code{HICOLOR} + and similar paletteless types) +@end table + +To make writing calculation loops for different bit-depths easier, +@code{pixel8_t}, @code{pixel16_t} and @code{pixel32_t} are predefined. You also +can use preprocessor magic as the edge detection filter does; this lets you +write calculation loops just once, using @code{cpixel_t}, and the code will be +compiled for every bitmap depth. See the edge detection filter +(@code{src/engine/edge.c} and @code{src/engine/edged.c}) for implementation +details. + + +@section Coordinate conversion + +The @code{convertup} and @code{convertdown} functions are used for converting +screen coordinates to a position in the fractal and back. @code{convertup} +receives coordinates in the child's image, and is expected to convert them into +coordinates in the parent's image and call the parent's @code{convertup} +function. + +@code{convertdown} is the reverse of @code{convertup} (going from parent to +child). + +If coordinates correspond 1:1 you should use @code{convertupgeneric} and +@code{convertdowngeneric}; otherwise, the implementation should look something +like this: + +@example +static void convertup(struct filter *f,int *x,int *y) +@{ + *y*=2; + *x*=2; + if(f->next!=NULL) f->next->action->convertup(f->next,x,y); +@} +static void convertdown(struct filter *f,int *x,int *y) +@{ + *y/=2; + *x/=2; + if(f->previous!=NULL) f->previous->action->convertdown(f->previous,x,y); +@} +@end example + +@section Filter removal + +Before the filter is removed from the queue, the @code{removefilter} function is +called. It is expected to clean up anything that the filter changed. + +In most cases, it should be left at @code{NULL}. + +@section Filter registration + +Once the @var{filteraction} structure is filled, the filter is ready, and you +should try to enable it. To enable it in the user interface you need to edit +@code{src/ui-hlp/ui_helper.c}, add the filter to the @var{uih_filters} +structure, and increase @var{uih_nfilters}. Note that the order of filters in +@var{uih_filter} defines the order of the filters in the filter queue. + +Then it is high time to start experimenting. + +Good luck! +@node algorithm, timerlib, filters ,Top +@chapter Algorithm description +The main idea behind XaoS is that it is not necessary to calculate the whole +image in every frame; most pixels were already calculated by the previous +frames. You usually don't have exactly the pixels you want, but all within a +range lower than a step between pixels are acceptable. That is why the image +flickers a bit and why points do not blink randomly as in recalculated +animations. + + This document describes some of the most important algorithms in XaoS: + @itemize @bullet + @item Saving Previous Pixels + @item Approximation Algorithm + @item Moving Pixels to New Positions + @item Calculating New Pixels + @item Symmetry + @item Calculation of Mandelbrot Set + @item Dynamic Resolution + @item Autopilot + @end itemize + +@section Saving Previous Pixels + +Ideally, all recalculated points should be saved and used for +building successive frames. I could not figure out a practical way to +implement this. To save all frames for half an hour would require 24 +Mb of memory, and searching the saved frames would be more +computationally expensive than recalculating an entirely new frame. + +One way was later used by the program Frang. It remembers all pixels as +triplets of (x,y,value), and when it builds a new image, it draws all +the pixels that it remembers to that image and then browses the image +and fills it with new pixels. (Possibly an @sc{rle} encoding should be +used for calculated pixels to conserve memory.) Frang actually uses an +algorithm that takes away pixels from the screen, so it behaves in +exactly the same way as the algorithm described here. On the other hand, +this method seems to require much more memory than XaoS' algorithm, and +drawing pixels/browsing the image costs quite a lot, so the algorithm +described here seems to be faster, since it never requires examining the +whole image, and the new image is constructed using block move +operations. + +For this reason, only the last generated frame is used as a reference. +This way the memory requirements are proportional to @math{xsize * +ysize}. It can be shown that this method is only about 2--5% slower +during zooming. (Of course unzooming back to once browsed areas is much +slower.) + +Because only the previous frame is used, another optimization can be +performed: The imaginary and real parts of the calculated image are not +precise, since they are the result of successive iterations of the +algorithm. In order to prevent errors from being propagated to the +following frames, their exact coordinates need to be known. +Fortunately, it isn't necessary to save their values since it is known +that all real components in a row and all imaginary components in a +column are equal. Thus, the only things that must be saved are the real +components for every row and the imaginary components for every column. + +This allows for a substantial speed-up in approximation because the +calculation requires less data. Of course, some rows and columns fall +out of the threshold and new ones need to be calculated to fill in the +gaps in the frame. + +Obviously, much less work is done than in a brute-force calculation: +there are only @math{xsize + ysize} calculations instead of +@math{xsize * ysize}. So the main loop in XaoS looks like this: + @itemize @bullet + @item Make approximations for rows + @item Make approximations for columns + @item Move old pixels to their new positions + @item Calculate pixels for which there is no good approximation for + their row + @item Calculate pixels for which there is no good approximation for + their column, but there is one for their row + @end itemize + +@section Approximation Algorithm + + @unnumberedsubsec Introduction to problem + +You can see that the approximation algorithm is central to the +implementation of XaoS. If a guess is incorrect the image will look +strange, boundaries will not be smooth and the zoom will flicker. On the +other hand, if it adds more new rows or columns than required, zooming +will become much slower. Also, if doubling should happen (i.e., using an +old row or column more than once) the resolution will lower and the +image will look jagged. It is important to keep the increasing imaginary +and real components in the correct order. If a row and column of complex +coordinates follows one with higher coordinate values, an improved +approximation can be attained by swapping their values. + +The algorithm needs to be relatively fast. It is only used for @math{xsize + ysize} +values, but if its speed is proportional to @math{O(n^2)}, it can be +slower than a whole recalculation of the image. Speeds of @math{O(n)} or @math{O(n * log(n))} are acceptable. + + @unnumberedsubsec Some simple algorithms to solve it + +Initially, a very simple algorithm was used: + + Find the old row/column nearest the row/column that needs to be + regenerated. If the difference between them is less than one step + (@math{step = (end - beginning) / resolution}) then use it. Otherwise, + recalculate a new one. + +Finding the nearest row/column pair is very simple since it is always +greater than or equal to the pair needing to be generated. + +Surprisingly, this simple algorithm has almost all the problems +described above. Doubling was fixed by lowering the limit to @math{step / 2.} +This caused a considerable slowdown so the limit was returned to @math{step}. +Instead, the algorithm was changed to search for only row/column pairs +that are greater than the previous frame's row/column pairs. This is +the algorithm that was used in version 1.0. + +This algorithm still added too many new rows and columns, and did not +generate smooth boundaries. For version 1.1 a heuristic was added that +preferred approximating rows/columns with lower values. This way it +did not occupy possible rows/columns for the next approximation. The +result was a speedup by a magnitude of four. In versions 1.1 to 2.0 +many improvements were made to the heuristic to give it added +performance. The following example tries to explain how complicated +the problem is (O is the old coordinates and X is the values to be +approximated): +@example + X1 X2 X3 X4 X5 X6 X7 +O1 O2 O3 O4 O5 O6 O7 O8 +@end example + +The normal algorithm will aproximate X1 by O2, X3 by O4 but nothing +more. For the algorithm with threshold step instead of @math{step / 2}: + +@example + O2 to X1 + O3 to X2 + O4 to X3 + O5 to X4 + O6 to X5 + O8 to X6 +@end example + +But this will fail with X7. The second algorithm which relies on lower +values will do the following: + +@example + O1 to X1 + O3 to X2 + O4 to X3 + O5 to X4 + O6 to X5 + O7 to X6 + O8 to X7 +@end example + +O1 to X1 is wrong. And there is many and many other situations that +may occur. But you may see that the normal algorithm will calculate 4 +new rows/columns but the heuristic saves all of these calculations. + + @unnumberedsubsec Current algorithms used + +In version 2.1 work on this heuristic was disabled after I discovered a +surprisingly simple algorithm that solves all these problems. First I +decided to exactly define the ``best approximation''. This should be +done by defining a price for every approximation and choose the +approximation with the lowest price. Prices are defined as such: + + Approximating row/column x by y costs @math{dist(x, y) ^ 2}. + +This prefers two smaller approximation errors before a single larger +error and describes my goal quite well. + +The cost for adding a new row/column specifies when it is better to do +a bad approximation and when to add a new row/column. I use @math{(4 * step) +* (4 * step)}. This means that the approximation is acceptable when +@math{dist(x, y) < 4 * step}. Otherwise, adding a new row/column costs less. +Now the best approximation is known. All that is required is a fast +algorithm to do this. Surprisingly, this is possible in linear time +using a relatively simple dynamic algorithm. It uses approximations of +@math{length < n} to make a guess at the length of @math{n}. It can start by +approximating one row/column and then again for two, three up to +xsize/ysize rows/columns. + +The algorithm starts by calculating prices for all possible new +positions for old row/column 1. Because of the pricing there are +maximally 8 new positions. (Other ones must cost more than adding new +row/column). Of course it is possible that there are no new positions. + + +For calculating the price of approximations for row/column 2 I may use +the previous column: Try new position n. Calculate the price and add the +best approximation for the previous (row/column 1) that uses a new +position lower than n (thus prohibiting doubling or swapping). This +should be one of 8 positions or (eventually) adding a new one and not +using row/column 1 at all. + +The same method can be used for the rest of the rows/columns. At the +end the best price may be found for the last row/column and return by +the way it was calculated. (For this I need the saved ``calculated +using'' values.) At this step the best approximation has been +determined. + +To fill the table, @math{9 * n} steps are required and n steps to +backtrack to the best approximation. The only problem is that this +algorithm is still a little slow (chiefly because of slow memory access +on the Intel architectures). But, with some optimizing, it works well. + +This algorithm is almost perfect except that it occasionally adds new +rows/columns to the wrong locations --- it does not prefer to add new +rows/columns into holes --- but it does not seem that this is a real +problem. The last optimization made was based upon the fact that added +rows/columns do not have the exact real and imaginary components +calculated by (@math{beginning + x * step}) but lie at the average of +their left and right neighbors. This makes the boundaries smooth and +distributes coordinates better. It also has the added benefit of making +the input better for future approximations. + +Another danger during implementation of this algorithm is that adding new +rows/columns into their ideal positions could cause misordered results, +since some rows/columns could be off more than the distance between +them. To avoid this, I use an algorithm that always examines the start +and end of a block of new rows/columns and linearly interpolates the +value between them. Special care needs to be taken with the blocks that +start at the beginning or finish at the end. + +Implementation should be much faster using custom fixed-point +routines --- first recalculate values such that 0 means start of image +and 65536 means end. Than calculation is much cleaner. Values <0 and +>65536 are off screen, calculation is independent of scale, and many +things should be recalculated --- like tables for calculating price from +distance. Also dividing the main loops into many specialized parts and +avoiding filling unnecessary parts of tables helps. So current algorithm +in XaoS is about 5 or 6 times faster than first naive implementation. + +@section Moving Pixels to New Positions + +Since XaoS is using the approximation algorithm the following table is +filled for every row/column: + @itemize @bullet + @item calculate + @item oldpoint + @item position + @end itemize + +calculate is 1 if the current row/column is new and needs to be +calculated or 0 if no old pixels need to be moved. oldpoint is a +pointer to the old row/column that corresponds to the new one. This +pixel needs to be copied to the new location. position is the real and +imaginary components of the coordinates used for future +approximations. Because almost all points will be moved, the solution +seems to be simple: for every new point look at the row and column +table; copy it if required. + +There is the problem that this minimally needs three memory reads for +every pixel (read calculate, oldpoint and index of old point). This is +too slow, so a small optimization is performed. Instead of rewriting the +piece of code in assembly, normal memcpy is used to move blocks of pixels +to their new locations. This minimizes the internal loop and access can +be done more quickly since memcpy is usually optimized for each +architecture. + +Using the row table, a list of blocks to move for every row is +created. With this new table all the pixels can be moved quickly. This +increased the speed of XaoS by about four times and made this function +so fast that it is no longer a problem. (In fact, it takes much less +time than all other parts of XaoS.) + +@section Calculating New Pixels + +The above optimizations make XaoS very fast, but another 30% increase in +speed is acquired by using a clever method for calculating the new +pixels. Many methods are known for saving calculations during the +generation of fractal images. The most powerful is boundary detection. +It relies on the fact that the Mandelbrot Set is connected with lakes. +You need only one pixel at the boundary, then can traverse the whole set +and fill the solid area inside. This method saves many calculations but +is too complex for adding just one line. Many claim that it does not +introduce any errors, but this is not true. It is possible for a +connected part of the lake to be so small that it is not visible in +smaller resolutions. In this case, boundary detection misses the whole +area. This algorithm is actually used just for calculating of new images +(i.e. at the startup). + +XaoS uses modification of method known as solid guessing. The pixels +at the boundaries of a rectangle are calculated. If they are all the +same you may assume that this rectangle does not does not contain +anything and fill it. + +This algorithm is further modified to operate on added lines. For this +it is at least as good as boundary detection and produces more +tangible errors. When adding a single line, the upper and lower line +may be examined for the nearest three pixels. If they are all the same +then it is assumed that 9x9 pixels are the same. This disables all +calculations inside solid areas and calculates as many points as +boundary detection. The only possibility of creating a larger error +with this method as opposed to boundary detection is in the instance +that the shape of the set is so sharp that it does not set any of the +tested points but comes from the right (i.e., uncalculated) location. +This situation is not very common. + +Later, rules were added for new rows and columns that crossed each +other. In this instance you can test only four pixels. This situation +is very rare. It is hoped that it does not introduce many errors. + +If multiple blocks of new lines need to be calculated there are no +reference pixels to use for solid guessing. Interlacing does the +trick. By calculating the odd lines without any guessing, the guessing +algorithm is now possible for the remaining uncalculated lines. This +simple trick saves about 30% of the calculation of the main Mandelbrot +image. + +A similar approximation can also be done for the X coordinate. This +makes it possible to improve solid guessing at even pixels because all +surrounding pixels are available, further reducing errors. + +@section Symmetry + +Many fractals are horizontally or vertically symmetrical. This is +implemented in the approximation code. When there is no good +approximation available, try to mirror the opposite side if the line +is available. + +This method primarily speeds up the initial image. + +@section Calculation of the Mandelbrot Set + +The internal Mandelbrot calculation loop is unrolled --- it calculates +the first 8 iterations using the normal method, and then it expects that +number of iterations will probably be large, so it switches into a mode +where it calculates iterations in blocks of 8 with one bailout test at +the end. When the bailout is attained, saved values from previous +iterations are restored and the last 8 iterations are recalculated slowly +to get exact values. This especially helps on the Pentium, where +conditionals in floating point code are slow. + +Another stuff is periodicity checking. XaoS has loops with and without +periodicity checks. In most cases it uses the no-periodicity-checking +version. The periodicity check version is used just in the case where +some inside-set pixel has been found during the solid guessing +phase. This is done mainly because the periodicity checking version of +the loop is significantly slower. + +@section Dynamic Resolution + +The above optimizations often do not help enough and image calculation +is still too slow. One option was to reduce the framerate, but a +framerate lower than 5 frames per second is unbearable. Another option +is simply to calculate only the details that can be determined within +a time interval. + +Rows/columns not calculated are simply approximated by referencing the +nearest row/column. The result is an image with larger pixels. One +problem is the fact that the order of calculating the rows/columns is +significant. Previous versions of XaoS simply calculated all rows from +top to bottom and then columns from left to right. Using the dynamic +resolution code with this algorithm would result in distorted +images. This was solved by adding a priority to every row/column and +calculating the high priority row/column first. The algorithm for adding +these priorities is as follows: +@itemize @bullet + @item Find middle row/column of uncalculated block. Priority is the size + of the block (in floating point coordinates) + @item Start function for left block and right block +@end itemize + +This function produces quite good results. It tends to make same-sized +rectangles on the whole image and does not depend on resolution. + +Another interesting optimization is that during the zoom it is more +advantageous to calculate rows/columns in the center of the zoom +instead of the borders since these will be in the viewport longer and +the user is usually focusing on the center of the zoom anyhow. + +This is done by simply adding to the calculated priority +@math{normal_priority / (abs(newposition - oldposition) / step + 1)}. This +prefers rows/columns that do not move a great deal. (Of course, +unzooming uses the reverse of this formula.) + +The last variable to consider is the time interval for one frame. +Setting it too low makes the calculation slow. Setting it too high makes +the framerate too low. So the amount of time spent in other parts of the +program is calculated and multiplied by 5 to determine the interval. If +this indicates a framerate lower than 15FPS, 15FPS is used instead, since +slower animations are unacceptable. On the other hand, if it is higher +than 35FPS, it is set to 35FPS, since a higher framerate than that is +just wasting computer resources. When the image is not animating, this +value is changed, so a framerate between 5FPS and 15FPS is selected. This +ensures that images are calculated quickly after zooming stops. + +@section Autopilot + +Another interesting algorithm controls the autopilot. It is actually +quite simple. Interesting parts are found at the boundaries of the +set. It randomly looks around and zooms to the first area containing +both outside and inside set points. Some fractals (such as the Newton) +do not have points inside the set at all. In this case it selects a +point where many (more than 2) different colors are around. (i.e., It +zooms into noisy areas.) + +In the instance that there are no such areas, the autopilot will +unzoom. It also detects oscillations / vacillations and breaks them. + +The current implementation also does detection of out of range numbers; +randomly chosen points are chosen near the old one, to avoid frequent +changes of direction. + +@section SMP support +Since version 3.0 XaoS supports SMP. This is done using threads. Most of +XaoS routines should be threaded easily --- for example +@code{moveoldpoints} just divides image into @math{n} equal parts and +each part is computed by one processor. The only unthreaded part is the +realloc table calculation routines. I don't see any way to paralellize it +except for calculating both @math{x} and @math{y} approximations +simultaneously (using two processors). Another interesting algorithm to +parallelize is boundary trace; see the comments in +@code{src/engine/btrace.c} for discussion of the current implementation. +The only problem I see in the current implementation is the possibility +that calculation is divided into too many parts (realloc tables, move +points, calculate, symmetries, dynamic resolution) causing too much +synchronization between each part. So this may be too slow on a real SMP +box. + +@node timerlib, registry,algorithm ,Top +@chapter The timer library + +Timer library is a library originally written for timing in XaoS; but I +found it useful in many other programs (like demonstrations, games, +animation players and other stuff that needs to be timed). So you should +read this description and possibly use it in your application and save +some coding time. + +There are many ways to design a timed application (such as a game) + +@enumerate +@item +Read user input, move baddies, display and loop again. + +This way has one disadvantage; the speed of game depends on the speed of +the computer. This was acceptable in olden times where the only +processor was the Z80 :) but now with a wide variety of hardware with +widely differing speeds such a loop is unacceptable. + +@item +Read user input, measure time since last loop and calculate step for +baddies, move baddies for set step, display and loop again. + +This way fixes the problem with speed. But moving baddies just for +calculated step, that should differ a much is quite complex, usually +introduces complex calculation, floating/fixedpoint math and other +unnecessary stuff that makes program long and introduces many bugs. + +@item +Set a fixed framerate that is high enough to make the game smooth but low +enough to do the whole internal loop in time. The internal loop then +looks like this: read user input, move baddies, display, measure time +spent in loop, sleep until next frame. + +This is quite a popular scheme but has another disadvantage --- game can +not be designed to use the whole CPU power, since on slower computers +internal loop would longer than is available for one frame, so the game +will run slowly again. + +@item +To take away disadvantage of previous method, many games time just the +moving of baddies and user input. Other stuff like displaying should be +done in the `untimed' rest of the time. In DOS games moving and user +input is often done in an asynchronous interrupt and drawing runs as the +main loop. This solves the case where the drawing of the game takes a +significantly longer time than the moving of baddies. This is quite +common so this scheme works well. + +@item +The previous scheme still has one problem --- since the timer interrupt +works asynchronously, there could be many race conditions: if moving +takes a longer time than the time reserved for it, the computer can +crash. So this scheme should be enhanced into a synchronous one with +exactly the same result but avoiding the problem with race conditions: + +Read user input, measure the time spent by the loop and calculate how +many simulated frame interrupts were activated since last activation: if +zero, sleep until simulated interrupt, move baddies as many times as +required, display, and loop again. + +This is a combination of 4 and 3 and seems to be the most comfortable way +for writing games, but since the main loop is now quite complex many +games don't do that. + +@item +there is still one small problem. Method 5 expects that moving takes a +significantly smaller time than displaying. This may not be the truth. A +simple work around is to write a moving routine that should move for +@math{x} moves in a faster way than calling move @math{x} times. This is +often possible, and makes extension to scheme 5 easy. This scheme allows +you to use a very large maximal framerate (say 100FPS) and to have the +same results as method 2 (which is the maximally exact method) +@end enumerate + +As you can see, designing the main loop isn't so easy. This is just a +very simple example: a more advanced application might want to move one +set of baddies at one framerate and another at a different +framerate. This requires two such timings. Another complication is that +there are many different ways to measure time exactly on different +platforms. Under Linux you can measure using @code{gettimeofday}, but +under DOS this is exact to just 1/18 of second and thats too low for +smooth animation, and so on. + +That's why I decided to design a portable easy to use timer library, that +makes it easy to implement all methods described above, combinations of +them, and much more. During the design I took care of the following +things: quality of timing, how easy to use it is, speed, portability and +to minimise unexpected situations (like race conditions in asynchronous +interrupts and so on) + +@section The name of the game + +The timer library operates with @dfn{timers}. They should be created, you can +measure time since last reset, pause them or set @dfn{handler} and +@dfn{interval}. But handler is not yet activated at the given interval. Since +timer library is not asynchronous, you must activate them yourself. + +For activating @dfn{groups} are used. You should process a group at some place +in your program, whereupon all timers in the group are checked and their +handlers activated if required. When the time spent since last activation is +higher than the interval, the handler is activated more than once. Also, the +interval to next invocation is calculated to keep frequency. Simple scheduling +is performed for handler --- handler is activated just once and then all other +timers are checked before it is activated again. You can also define a +multihandler --- a handler that is activated just once and receives as an +argument a count of intervals. + +There are two special groups --- @code{asyncgroup}. Timers in this group are +activated asynchronously (as though they were called from interrupts). Thisis +not recommended, since the asynchronicity brings many problems and usually isn't +required. Also it does not work on many platforms. @code{syncgroup} is the +default group. The program is expected to process it quite often. If you don't +need to use more than one group, you should use this one. + +Time in timerlib is bit strange, since it does not flow continuously but +jumps. It is updated every time you call @code{tl_updatetime}. I did this in +order to minimize context switches, but later I found this scheme very useful, +since you normally look up the timer, do something and then reset it and don't +want to worry about any time spent between lookup and reset. This helps to keep +frequency of timers exact w/o any errors caused by such situations. At the other +hand you need to call @code{tl_updatetime} at least once in your main loop. + +Maybe you don't know why you'd want to create more groups, but I found it quite +useful. For example, an autopilot in XaoS has such a special group --- I need +to call it approx. every 1/20 of second, but just at one place in the +program. Invocation of the autopilot when calculation is active would produce +incorrect results, so I have a special group for autopilot and process it in +just one place where I am sure it is safe. + +Timers can also be emulated. You can stop them and then control the flow of time +for given timer. This should be quite useful; for example, when you want to +precalculate animation at a given framerate. + +To control a group of timers, you can create @dfn{emulators}, which are just +other timers controlled by you. They are useful in cases where you want to +emulate fixed framerates (for animation rendering) or suchlike. + +@section Time functions + +@deftypefn Function void tl_update_time (void) +Update time used by timerlib. This must be called at least once in the main loop +otherwise time will not flow. See above. +@end deftypefn +@deftypefn Function void tl_sleep (int @var{time}) +Sleep for the given @var{time}. Similar to @code{usleep} in @sc{POSIX}. +@end deftypefn +@section Group functions + +@deftypefn Function tl_group* tl_create_group (void) +Allocate and initialize the group header. Returns @code{NULL} when @code{malloc} fails. +@end deftypefn +@deftypefn Function void tl_free_group (tl_group *@var{group}) +Free memory storage used by group structure. +@end deftypefn +@deftypefn Function int tl_process_group (tl_group *@var{group}, int *@var{activated}) +Process timers in @code{group} and activate their handlers. Returns time until +next invocation; the main loop should sleep for that long. The @var{activated} +parameter is either @code{NULL}, or a pointer to a variable that is set to +the number of activated handlers. +@end deftypefn +@section Timer functions + +@deftypefn Function tl_timer* tl_create_timer (void) +Create timer structure. +@end deftypefn +@deftypefn Function void tl_free_timer (tl_timer *@var{timer}) +Free memory storage used by timer structure. +@end deftypefn +@deftypefn Function void tl_reset_timer (tl_timer *@var{timer}); +Reset timer to current time (the time of last actication of @code{tl_update_time}). +@end deftypefn +@deftypefn Function int tl_lookup_timer (tl_timer *@var{timer}); +Return time since last call of tl_reset_timer or last activation of handler. +@end deftypefn +@deftypefn Function void tl_set_interval (tl_timer *@var{timer}, int @var{interval}); +@end deftypefn +@deftypefn Function void tl_set_handler (tl_timer *@var{timer}, void (*@var{handler}) (void *),void *userdata); +@end deftypefn +@deftypefn Function void tl_set_multihandler (tl_timer *@var{timer}, void (*@var{handler}) (void *,int),void *userdata); + +Handler, multihandler and interval control functions +@end deftypefn + + +@deftypefn Function void tl_add_timer (tl_group *@var{group}, tl_timer *@var{timer}) +Add timer to given group. A timer should be in only one group. +@end deftypefn +@deftypefn Function void tl_stop_timer (tl_timer *@var{timer}) +@end deftypefn +@deftypefn Function void tl_resume_timer (tl_timer *@var{timer}) +Stop and resume timer. +@end deftypefn + +@deftypefn Function void tl_slowdown_timer (tl_timer *@var{timer},int @var{time}) +The time in the timer is moved back to the given time. +@end deftypefn + +@section Emulator functions +@deftypefn Function struct timeemulator *tl_create_emulator (void); +This function creates new a emulator --- you need to create one first before +emulating. +@end deftypefn +@deftypefn Function void tl_free_emulator (struct timeemulator *@var{t}); +Destroy emulator's data. +@end deftypefn +@deftypefn Function void tl_elpased (struct timeemulator *@var{t}, int @var{elpased}); +Move emulated time. +@end deftypefn +@deftypefn Function void tl_emulate_timer (struct timer *@var{t}, struct timeemulator *@var{e}); +Set timer to the emulated mode; the passage of time is now controlled by the +emulator @var{e}. All other behavior of timer keeps unchanged. +@end deftypefn +@deftypefn Function void tl_unemulate_timer (struct timer *@var{t}); +Disable emulated mode for the timer. +@end deftypefn +@section Example main loop + +@example +while(1) +@{ + time=tl_process_group(syncgroup,activated); /*Call game control functions*/ + update_keys(); + if(activated) /*something changed*/ + display(); + else tl_sleep(time); +@} +@end example + +@node registry, index,timerlib ,Top +@chapter XaoS function registry + +XaoS has an ui helper library, which provides functionality used by the user +interface. All its useful functions are registered into a central registry. +This registry is used to generate menus and dialogs as well as command line +options or scripting language; so it is a very significant thing in XaoS design. + +This is not just useful for those who want to hack XaoS ui-helper layer, but +also for authors of drivers, who can use this to add new driver dependent +functions into XaoS's menu. The external user interface is also based on the +registry. The main idea behind external user interfaces@footnote{currently one +for Tcl/Tk and Gtk is under development} is this: XaoS transfers its registry to +the interface (using asimple description language). The user interface starts +XaoS in its window and builds menus and dialogs based on the registry. Then, +once user selects some function, the user interface creates a command in XaoS' +scripting language and sends it back to XaoS' engine. + +Knowledge of this part is thus essential for many developers. Please pay +attention. :) + +The implementation of the registry is in @code{xmenu.c}, and the header is +@code{xmenu.h}. For historical reasons, it talks about menus and dialogs (it was +originally designed for the GUI). I am keeping this terminology, since it is +quite clean and easy to understand instead of talking in some cryptic abstract +terms. + +@section Function description + +To add a function into the database, you need to put a description into the +@var{menuitem} structure. It has the following definition: + +@findex menuitem +@example +typedef struct menuitem + @{ + char *menuname; + char *key; + char *name; + char *shortname; + int type; + int flags; + void (*function) (); + int iparam; + void *pparam; + int (*control) (struct uih_context *); + menudialog *(*dialog) (struct uih_context *); + @} +menuitem; +@end example + +@defvar menuname +Name of menu (or category) the function belongs in. The root of all categories +is called @code{"root"}. XaoS also uses an @code{"animroot"} when animation +replay is active. If you are adding a function, it is better to add it into some +subcategory like @code{"ui"} (which will place it into the UI menu) or to create +a new category for your functions, which will appear as a submenu of the main +menu in the UI. +@end defvar + +@defvar key +@sc{ASCII} code of the hotkey that activates this function. Use @code{NULL} if +none. +@end defvar + +@defvar name +Longer name of the function, used in the menu entry, or @code{xaos --help} +listing. +@end defvar + +@defvar shortname +One-word name of function used in command language and other references to +the function. +@end defvar + +@defvar type +Type of function --- this is @emph{not} the return type. @var{type} should be +one of the following constants: +@table @code +@findex MENU_SUBMENU +@item MENU_SUBMENU + A submenu. This is not a function, but a name for the submenu. You can fill + in the @var{key}, @var{name}, and @var{shortname}. The name of this new + submenu is placed in the field @var{pparam}. +@findex MENU_NOPARAM +@item MENU_NOPARAM + A normal function without any parameters. When activated, + @var{function} will be called with a pointer to @code{uih_context} as its + parameter. +@findex MENU_INT +@item MENU_INT + This should be used to simplify entering of many similar functions (handled + by just one universal function in the C code). The @var{function} is handled + in the same way as @code{MENU_NOPARAM}, but also one integer parameter taken + from @code{iparam} is passed in. +@findex MENU_STRING +@item MENU_STRING + Similar to @code{MENU_INT} but uses a string instead of an integer. +@findex MENU_DIALOG +@item MENU_DIALOG + If your function needs some paramters, use the dialog structure to describe + them. In the scripting language your function then have parameters; in + the user interface, a dialog will be displayed. @var{pparam} must point + to array of dialog entries (writing them will be described later). + If your function has just one parameter described in the dialog structure, it + will be called in the normal C way --- if you want a string parameter, one + pointer pointing to a string (in addition to @code{uih_context}) will be + passed to the functions. + + If multiple parameters are requested, it is impossible to call + function in a C way without a special wrapper for each case. So it will + receive a pointer to an array of @code{dialogparam} unions, wich contain + one entry for each parameter. @code{dialogparam} is declared as follows: +@example +typedef union + @{ + char *dstring; + int dint; + number_t number; + number_t dcoord[2]; + xio_path dpath; + void *dummy; + @} +dialogparam; +@end example +@findex MENU_CDIALOG +@item MENU_CDIALOG + In some cases, it is useful to add some context specific default values + to the dialog structure. In this case you might use this type instead. In + this case the function @var{dialog} is called first, and it is expected to + return a pointer to the correct dialog structure. The dialog structure must + lie in static storage (since it is not freed), and must always have the same + fields, and differ only in the default values. + This function must also work correctly even when the pointer to + @code{uih_context} is @code{NULL}, since it is often called in the + initialization stages (parameter parsing etc.) +@end table +@end defvar +@defvar flags +The @var{flags} are used to set additional information about the function: +@table @code +@findex MENUFLAG_CHECKBOX +@item MENUFLAG_CHECKBOX +Some features act like check-boxes --- i.e. repeated calls to the function +toggle the features. In menus it is useful to add a check-box for this function +indicating whether the feature is on or off. This flag adds such a check-box. + +So that the UI can determine the current state of the checkbox, you need to +define the function @var{control}, which returns @code{1} when enabled and +@code{0} when disabled. In order to let external GUIs work correctly you also +need to call @code{uih_updatemenus("name")} every time the state of this +function changes. + +In the scripting language, this adds a single parameter, either @code{#t} or +@code{#f}. The engine then calls the function only when necessary. When +@code{#t}, a dialog is requested; when @code{#f}, the function is called just as +@code{NOPARAM}. I.e. the dialog is displayed only when enabling the feature. + +@findex MENUFLAG_DIALOGATDISABLE +@item MENUFLAG_DIALOGATDISABLE +Display dialog on disabling of this checkbox feature, instead of on enabling. +@findex MENUFLAG_RADIO +@item MENUFLAG_RADIO +Other features act like radio-buttons. Control functions in this case receive +the same parameter as is defined for @code{MENU_INT} or @code{MENU_STRING} +types, and is expected to return @code{1} when enabled and @code{0} +otherwise. You also need to call @code{uih_updatemenus} when the value is +changed. No special parameter is added in the scripting language. + +@findex MENUFLAG_INTERRUPT +@item MENUFLAG_INTERRUPT +Interrupt current calculation when this function is called (used by functions +with cause recalculation of the screen) + +@findex MENUFLAG_INCALC +@item MENUFLAG_INCALC +By default XaoS queues functions and calls them later when they are activated in +the calculation. This flag disables this feature. + +@findex MENUFLAG_ATSTARTUP +@item MENUFLAG_ATSTARTUP +By default XaoS queues functions and them calls later when they are activated as +command line parameters (in case the engine is not fully initialized yet). This +flag disables this feature. + +@findex MENUFLAG_NOMENU +@item MENUFLAG_NOMENU +If set, the function will not be visible in the menu. + +@findex MENUFLAG_NOPLAY +@item MENUFLAG_NOPLAY +If set, the function will not be available as a command in scripts (and therefore +won't be usable by external GUIs). + +@findex MENUFLAG_NOOPTION +@item MENUFLAG_NOOPTION +If set, the function will not be available as a command line option. +@end table +@end defvar + +@section Initializing the menuitem structure as a static variable +In most cases, menuitems should be written as static variables. Because the +contents of this structure could change in future, please use one of the macros +defined in @code{xmenu.h}. They provide a cleaner and easier to extend way to +define these entries than does doing it by hand. + +For example to define a @code{MENU_NOPARAM} function, use the following macro: +@defun MENUNOP (menuname, key, name, shortname, flags, function) +@end defun +Similar macros exist for other types too. They end in @code{CB} or @code{RB} for +check-boxed or radio-box functions. See @code{src/ui-hlp/menu.c} for a large +number of example definitions. They should look like this: + +@findex menuitem +@example +static menuitem menuitems[] = /*XaoS menu specifications */ +@{ + SUBMENU ("", NULL, "Root menu", "root"), + SUBMENU ("", NULL, "Replay only commands", "plc"), + MENUNOP ("comm", NULL, "print menus specifications of all menus", + "print_menus", MENUFLAG_NOMENU|MENUFLAG_NOPLAY|MENUFLAG_ATSTARTUP, + uih_printallmenus), + ... +@end example + +@section Dialog description +A dialog description is similar to a menuitem. It is an array of the following +structures: +@example +typedef struct dialog + @{ + char *question; + int type; + int defint; + char *defstr; + number_t deffloat; + number_t deffloat2; + @} +menudialog; +@end example +It is terminated by an element with the @var{question} pointer set to +@code{NULL}. + +The @var{question} contains the string the UI should display when it asks for +this field. + +@var{type} should be one of the following values: +@code{DIALOG_INT}, @code{DIALOG_FLOAT}, @code{DIALOG_STRING}, +@code{DIALOG_KEYSTRING} (the difference between string and keystring is that +in the scripting language string is passed as @code{"hello"}, but keystring is +passed as a Scheme keyword: @code{'hello}), @code{DIALOG_IFILE} (input file), +@code{DIALOG_OFILE}, @code{DIALOG_CHOICE} (choice between different +keystrings), @code{DIALOG_ONOFF} (boolean parameter), @code{DIALOG_COORD} (two +floats --- a complex number) + +Set the corresponding @var{def*} field to set the default value. In the case of +files, use a string in the format @code{"@var{[prefix]}*@var{[extension]}"}. For +type @code{DIALOG_CHOICE} set @var{defstr} to a pointer to an array of strings, +terminated by a @code{NULL} entry. + +To write dialog structures, as with menus, use macros defined in @code{xmenu.h} +like: +@example +DIALOGSTR(question,default) +@end example +The definition should look like: + +@findex menudialog +@example +static menudialog uih_viewdialog[] = +@{ + DIALOGCOORD ("center:", 0, 0), + DIALOGFLOAT ("Radius:", 1), + DIALOGFLOAT ("Angle:", 0), + @{NULL@} +@}; +@end example +@section Modifying the registry +@deftypefn Function void menu_add (menuitem *@var{item}, int @var{n}); +Add an array of @var{n} items to the database. +@end deftypefn +@deftypefn Function void menu_delete (menuitem *@var{items}, int @var{n}); +Remove an array of @var{n} items from the database. +@end deftypefn +@section Querying registry +@deftypefn Function menuitem* menu_findkey (char *@var{key}, char *@var{root}); +Find item for given key. @var{root} is menu to start (submenus are searched +recursively). +@end deftypefn +@deftypefn Function menuitem* menu_findcommand (char *@var{name}); +Find item for given short name. +@end deftypefn +@deftypefn Function char* menu_fullname (char *@var{menu}); +Return a long name for a menu, given a short name. +@end deftypefn +@deftypefn Function menuitem* menu_item (char *@var{menu}, int @var{n}); +Return the @var{n}th entry in the @var{menu}. Return @code{NULL} if that entry +does not exist. +@end deftypefn +@deftypefn Function int menu_enabled (menuitem *@var{item}, struct uih_context *@var{c}); +Check whether the given item is activated (for check-boxed and radio-boxed +functions). +@end deftypefn +@deftypefn Function int menu_havedialog (menuitem *@var{item}, struct uih_context *@var{c}); +Return whether this function has an associated dialog. +@end deftypefn +@defun menu_getdialog (@var{context}, @var{m}) +This macro returns a pointer to the dialog structure for a given menu item. (If +the item doesn't have a dialog, garbage is returned). +@end defun +@deftypefn Function int menu_available (menuitem *@var{item}, char *@var{root}); +Check whether an item is available as one of the entries of @var{root} (or it's +submenus) +@end deftypefn + +@node index, , registry, Top +@c node-name, next, previous, up +@unnumbered Index of functions, variables, types and constants + +@printindex fn + +@contents +@bye |