This is Info file xaosdev.info, produced by Makeinfo version 1.68 from the input file xaosdev.texinfo. INFO-DIR-SECTION Graphics START-INFO-DIR-ENTRY * XaoS: (xaosdev). The fast real-time interactive fractal zoomer (developers documentation END-INFO-DIR-ENTRY (C) 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.  File: xaosdev.info, Node: Top, Prev: (dir), Up: (dir) XaoS 3.1 ******** An real-time interactive fractal zoomer Hacker's guide May 14, 1998 This manual contains documentation for those who are interested in studying and improving XaoS sources or using them in other programs. It includes description of algorithm and documentation of those parts of XaoS I think they should be useful for someone. * 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  File: xaosdev.info, Node: design, Next: driver, Prev: Top, Up: Top Overview of the XaoS design *************************** Whole sources of XaoS are designed into several "libraries" (some of them are not really libraries, but added into other's, but should be separated easily). Understanding to the main philosophy should help you to navigate in the sources. I also expect that many of the lower level stuff should be useful in the other projects, since it is designed to be fairly generic. So here is an overview from the lowest level stuff to the highest. Palette and image library ========================= Sources are in directory `src/filter'. The aim of palette library is to provide relatively abstract interface to the various visuals and hide differences in the hardware and driver implementation. Fixedcolor, pseudocolor, grayscale and truecolor visuals should be handled in the almost same way. It provides the structure `palette', which contains actual palette. You might allocate new colors here (you give RGB value and corresponding pixel is returned), interpolate colors where possible, cycle colors and so on. Every palette also consist from the two parts--the preallocated color cells and the actual palette. This lets for example to GUI possibility to allocate statically colors for its texts and dialogs, while rest of palette is under control of different parts of XaoS. This library also contain set of functions to allocate different palettes used by other parts. I expected that different parts of XaoS should use same palette. Nothing similar happened yet, but functions are kept here. The image library is built at the top of palette library. It extends functionality for handling actual image data. Each image is represented by one or two frame-buffers (it is useful for double-buffering). One frame-buffer is called current and other old. They should be flipped by special function. Program can draw into both of them. Frame-buffers are hold as the set of pointers to the scan-lines. This brings better flexibility, because tricks like sub-windows, or flipped bitmaps are possible. Also speeds up, since you should avoid one multiplication. The last significant information image structure hold is of course bpp depth. It is counted in bytes, and should be 0-4. Where 0 is used for 1bit bitmaps. Filter library ============== Source are available in `src/filter'. This library controls the process of creation of the image. It handles an queue of the filters, where each filter should modify the image. There are two special filter at the beginning and end of queue. The first filter is usually the actual fractal engine which creates image, while the terminal filter is usually user interface helper library. Xthread library =============== This library provides interface to various multi-threading libraries (currently the BeOS, plan9 and POSIX implementations are available). It allows to run various function paraelly and some synchronization primitives (semaphores). It is simple, but has all the functionality required for the XaoS engine. Fractal library =============== Source are available in `src/engine/', headers in `fractal.h'. This library contains the actual fractal calculation routines. It operates with fractal context, which contains informations like current formula, seed for julia, palette etc. Functions for calculating the various fractal types and various coloring modes are available here. Zooming engine and other filters. ================================= Source are available in `src/engine/'. This is the actual zooming engine filter. It is done in fairly independent way at fractal library, so it should be possibly used for zooming other stuff. (it was already used for zooming large scale images containing maps of Hungary). All other filter has their special file, where is implementation and structure containing all functions exported from the filter to user interface. They are registered in the file `ui_helper'. One other terminal filter is implemented--Julia morpher. Other filters adds special effects (such as motion blur), or does conversions (such as rotation, dithering etc.) Timer library ============= This library provides many of very useful timing primitives. Such as timers, etc. Currently it is used by some other programs too. xio library =========== This library aims to provide united interface to file-system. Some strange systems (such as MacOS) has file-system API done in much different way than in UNIX. They don't have names in string, and uses special structures etc. xshl library ============ Xshl stands for XaoS simple hypertext library. It contains fairly universal engine parsing an xshl language. It is similar to HTML with some additions and many restrictions. It should render this texts for the proportional/non-proportional fonts and various sizes. help library ============ it is built at the top of xshl and xio libraries. It should read help files, wick contains an chapters. Parse chapter with given keyword etc. 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. Catalog library =============== This is library for handling an message catalogs. It should read catalog and convert the keyword into actual message. PNG library =========== This library provides the function for saving an image from Image library to the file (in PNG format). Other formats should be added as well if required. UI-helper library ================= This library controls all the low-level stuff and provides an high level interface to it. It has functions for playing animations, zooming/UN-zooming and such. It heavily uses all the described libraries. It don't implement functions for handling menus and such, but makes great help for such implementations, because of the function registry database. Ugly interface ============== This is currently the only real user interface for XaoS (there is also an second, wich is used for rendering animations, but it is not user interface, how users expect it). It is built at the top of UI-helper library and provides functions for drawing menus, dialogs and such. It has drivers for many platforms, and it should be easily ported to the others. In the future, it should be quite easily to extended to let drivers specify their own menu/dialog handling code, so it should be possible to give it an "native" look of given platform. It has also an function, where an GUI drawing routines are disabled. Function registry database is transfered trough pipe to external program, wick should build the menus and act as external user interface. It then back sends an commands in the scripting language representing things, that user done. So it is an another way, how to give native look to ugly interface. Ugly interface has also one serious limitation--for the historical reasons it is coded to handle just one window (rest of XaoS probably can do multiple windows--untested). So in windowed environments it is impossible to open multiple menus with fractals. At the other hand, this limitation is not so important, once external GUI enter the role. They should just start multiple XaoS engines. This will bring extra robustness, multitasking and some other advantages, so it is the proffered way. Thats why I don't plan to remove this limitation yet.  File: xaosdev.info, Node: driver, Next: gui-driver, Prev: design, Up: Top Driver API description ********************** To port successfully XaoS to some platform you need: * ANSI C compatible optimizing compiler. Note that optimizing compiler is really required, since XaoS is coded to be good target for optimizations and don't have any routines coded in assembly, so if you will use some bad compiler, you should receive more than ten times slower result. Also note that some compilers has serious problems with compiling XaoS--like most of DOS compilers (Watcom C, Borland C, Microsoft C etc...), has serious problems. They generate incorrect code or crash during compilation. I highly recommend to use GNU C compiler. Even some versions of GNU C has problems. Please read `compilers.txt' for more information. * Fast way to avoid division by zero/overflow and other floating point exception. XaoS is carefully coded to not to crash in this case, but don't have any tests to avoid such situation and expect random result in such case. Many platforms provide way to switch coprocessor into mode, where 1/0 is evaluated into Inf etc. If there is no such way, try to use some kind of signal handler that will ignore such exceptions. The "normal" solution--add ifs to avoid division by zero is almost impossible. The division is quite easy to check. But other cases--overflows are much worse. So I don't think it is possible to avoid all crashes just by adding ifs. XaoS don't depend at IEEE arithmetic. Result in such cases should me mostly undefined. XaoS usually works well with compiler's switches for inexact math enabled (such as `-ffast-math' in GNU). But no guarantees. For example at Alphas this is not true--since they usually generates exceptions then. Also `-mno-ieee-fp' at Intel don't work. This is due to gcc bug. Gcc in some cases reverse the condition when this switch is enabled. I've made patch to fix this problem and hope that it will get to egcs or gcc soon. * Text or graphics output device. If you have only text output device, you may use AA driver, which renders fractals into high quality ASCII art. In this case you might skip this chapter, download AA-lib (http://www.ta.jcu.cz/aa) and read porting chapter of AAlib manual. Graphics device must one of: * 8bits per pixel with user definable palette `C256', static palette `FIXEDCOLOR', or static grayscale `GRAYSCALE' * 16bits per pixel with arbitrary bits per each color `TRUECOLOR' * 24bits per pixel with 8 bits per each color, arbitrary order `TRUECOLOR24' * 32bits per pixel with arbitrary order of colors, where each colors fit to exactly one byte `TRUECOLOR' * 1bits per pixel bitmap with both orders (Least or Most significant bit first) Please contact me if you have different kind of device. Some modes (like miss-ordered truecolor modes) should be added really easily if required. Note that mono/4/16 colors devices will be probably never supported internally by XaoS, since I expect they will be slower than 8bpp, so XaoS will internally work in 8bpp and then image should be converted. Contact me if you want to write such converter. (For bitmap there already exists--see `dither.c'. * Some way to save images. By default XaoS uses `pnglib', which is ported to many platforms, but there is still many others. If your system has some standard image format, which is easier to handle than `.png', contact me and I will show you, how to add such support to XaoS (see `png.c'). * Stdio compatible library (this is problem at Mac or BeOS). XaoS has the abstract layer at the top of stdio, so it should use other input/output libraries too. You might write just another implementation if it's library called `xio'. See `xio.h'. Ugly interface is designed to make writing of new drivers as easy as possible. You need to write just few functions to fill following table: (use file `ui_template' for starting of writing new driver from scrath) 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; }; Functions ========= Ui uses following functions to communicate with driver: - Function: init function that initializes driver and returns 1 if success and 0 if fail - Function: getsize (INT *WIDTH, INT *HEIGHT) returns size of screen(window) x and y - Function: processevents (INT WAIT, INT *X,INT *Y, INT *BUTTONMASK, INT &KEYS) gets new keyboard/mouse events. parameters: WAIT if 1 function can wait for next event otherwise just lookup if something came. This is useful on multi-tasked os where xaos does not eats unnecesaru CPU. *X,*Y here returns current positions of mouse *B returns mask of `BUTTON1',`BUTTON2',`BUTTON3' for mouse buttons *K returns mask for cursor keys `1' left `2' right `4' up `8' down function also calls `ui_key' (ASCII character) and ui_resize if required. For special keys use `UIKEY_UP', `UIKEY_DOWN', etc. See `ui.h' for complete lists of this constants. note in case of problems freeing/allocating inside processevents you may call `ui_call_resize' that calls resize later outside this function - Function: uninit Unitialises driver--called before exit. - Function: set_range (UI_PALETTE *PALETTE, INT START, INT END) This is an preffered way to set palette (second way is `set_color') when `imagetype' is `UI_C256' (256 color with palette) one of this two functions is required. In truecolor modes they are unused. In case direct access to palette is possible at your platform, define this one. Function is expected to set all color cells between START to END to colors defined in PALETTE. `Ui_palette' is array of UI_RGB. `Palette[0]' is color for entry number START. `Ui_rgb' is an array of `char'. `Palette[0][0]' is red field of entry number START, `Palette[0][1]' is green and `Palette[0][2]' is blue. `0' means black and `255' means full intensity. Use `NULL' if your driver don't support this call. - Function: set_color (INT R, INT G, INT B, INT INIT) This is an secondary way, that should be used at platforms w/o direct palette access (like X11 or static color schemes). It receives RGB value of color, and returns index of color cell with this color or -1 if no more color cells available. An INIT parameter is set to 1, when first entry of palette is allocated, `set_color' is expected to free all color entries previously allocated. Use `NULL' if your driver don't support this call - Function: print (INT `x',INT `y', CHAR *`text') prints text to screen at position x/y. This function is a bit archaistic (XaoS now uses in the most cases its own functions drawing directly to the buffer), but in some cases--initialization messages or calculation, functions are unusable, so we still need this primitive. In the `C256' mode you might rely, that first allocated color is always black and second is white. - Function: display (VOID) displays current buffer to screen - Function: alloc_buffers (CHAR **BUFFER1,CHAR **BUFFER2) allocs two buffers that can hold screen size bitmap. Also sets current buffer to BUFFER1. Since version 2.1 returns scan-line size in bytes(usually width) and 0 if fail. This is useful on systems, that allocated bitmap bigger than window/screen(dividable by 4 or so) - Function: free_buffers (CHAR *BUFFER1, CHAR *BUFFER2) frees allocated buffers - Function: flip_buffer (VOID) flips buffers--set current buffer to other one - Function: flush (VOID) This function should be used by drivers with buffered output to flush output buffers. Other driver should set it to NULL. - Function: mousetype (INT TYPE) This function is used to change mouse cursor. It receives following values: `NORMALMOUSE' This mouse is usually displayed at screen, when UI waits for user commands `WAITMOUSE' This mouse is displayed when UI is busy(should be famous wait clocks) or you may use mouse defined in ui_dos--mandelbrot set `REPLAYMOUSE' This mouse is displayed during replay. Should be none at fullscreen drivers, since blinking mouse cursor during replay looks ugly. At windowed system disabling mouse looks ugly, so it should be some funny cursor. You should use NULL if your driver don't support this. Other information ================= Also some additional variables are used to inform ui about driver. All this values can be changed by init functions in case they are unknown before. TEXTHEIGHT, TEXTWIDTH width and height of your font PALETTESTART, PALETTEEND First and last palette entry, that should be changed. This you should use to avoid changing of entries reserved for window system, text, mouse etc. RMASK, GMASK, BMASK This fields are used in truecolor modes to specify, where each color is defined MAXENTRIES; Number of allocatable entries. Normally should be PALETTESTART-PALETTEEND IMAGETYPE defines type of image. Should be one of following values: `UI_C256' classical 256 color with palette scheme used by most older graphics adapters. You should use it also for static-color schemes but they are not supported well in current version. `UI_TRUECOLOR' 32bpp truecolor mode `UI_TRUECOLOR24' 24bpp truecolor mode. `UI_TRUECOLOR16' 16bpp truecolor mode FOLLOWING PART IS NOT REQUIRED TO MAKE FIRST VERSION OF DRIVER WORKING. so you may skip to REGISTERING DRIVER for first read and return here later. PARAMS Using this you may define command line options for you driver. They are defined using params structure like: static struct params params[]={ {"-mode",P_NUMBER,&defmode, "Select graphics mode(same number as in interactive menu)"}, {NULL,0,NULL,NULL} /*this is MUST be last option field*/ }; every line is one parameters. List ends with `{NULL,0,NULL,NULL}'. First filed is option name. Second field is type of parameter: `P_SWITCH' no parameter--variable is just set to 1 if option `P_NUMBER' integer number `P_STRING' string `P_FLOAT' floating point number (variable is float) Third is pointer to variable that is changed if option is set. It is for example `int*' for `P_NUMBER' or `P_SWITCH' and so on.. Last one is help text. Displayed by `ui -h' WIDTH,HEIGHT see FLAGS. May be set to `0.0, 0.0' for the beginning MAXWIDTH,MAXHEIGHT see FLAGS. May be set to 0,0 for the beginning FLAGS This variable says more about your driver. You may start with value 0. But for final version it is recommended to read following chapter carefully. Flags are uppercase constants and should be set by following way: `ASYNC_PALETTE | RANDOM_PALETTE_SIZE' following switches are supported: `RANDOM_PALETTE_SIZE' random size of palette. This is used in X where palette is shared between programs. By default xaos allocates all available colors up to 256. This is not very nice to other applications in X. So randomsize causes that just some random number of colors(between 8-256) are allocated. Also when this variable is off XaoS expects that allays same number of colors is available. `UPDATE_AFTER_RESIZE' recalculate and redraw screen even if its size is not changed. In case that resize procedure destroys data in buffers `RESIZE_COMMAND' Some drivers (mainly the fullscreen ones) may in the function `get_size' ask user for the size and color depth. It should be nice to let user change this parameter at runtime. I.E force XaoS to reinitialize his images. This is done by `ui_resize' call. This call in windowed drivers is called by the external event. But in fullscreen drivers you need key/menu item for this. You might add this function directly into XaoS's function registry (see for example the GGI driver)--it is usefull mainly when you want to make some size selection dialog in the standard way, or let XaoS add his default one. And this is done by this flag. See for example SVGAlib or DOG driver. Screen/window size informations: Xaos needs to know exact size of displayed images. This is required for random dot stereo-grams and also for keeping fractals in their shape (do not make them wide on 640x200 resolution etc.) So minimally one of the following values should be defined. (they are sorted in order I prefer them) `SCREENSIZE' values width/height specifies exact size of screen/window in centimeters `PIXELSIZE' values width/height specifies exact size of one pixel in centimeters This is better for windowed environments where window size is often changed `FULLSCREEN' driver runs fullscreen. XaoS automatically uses default screen size (29.0cm x 21.5cm) `RESOLUTION' driver does not know exact screen size. But knows resolution used. (it is in variables width/height) XaoS automatically calculates pixel width using:29.0cm/maxwidth and height: 21.5/maxheight Of course default width and height can be changed by command line options. You may also use combinations like: `SCREENSIZE | FULLSCREEN' the best for fullscreen drivers `PIXELSIZE | RESOLUTION' the best for windowed drivers `FULLSCREEN' for fullscreen drivers than have no idea about screen size... do not forget to set WIDTH, HEIGHT, MAXWIDTH, MAXHEIGHT fields if required. GUI_DRIVER See next section for description. Registering driver ================== Than just register driver to `driver.c' and you may compile :) You may use `ui_template.c' as driver template.. You may also look at xthreads library description if you are porting XaoS to some SMP platform. Please let me know if you want to start code some driver.  File: xaosdev.info, Node: gui-driver, Next: eui, Prev: driver, Up: Top Writting GUI driver ******************* XaoS have builtin GUI. Many operating systems have native gui toolkits and XaoS default GUI might look strange there. To avoid this problem, you might write external gui program (see eui section) or write mappings of XaoS GUI functions. The advantage of external gui process in multitasking. XaoS is not thread safe and GUI must be synchronous with calculation. Also ugly interface code currently don't support multiple windows (this should be solved in future). This solution is suitable mainly for those systems, where cooperation of two programs sharing one window should be problem (like on Windows). To write gui driver you need to fill following structure: 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); }; All function have `uih_context' parameter. You don't need to worry about it's contents. Just pass it to the called functions that require it. This parameter is for multiple window support, that is not implemented yet. The `setrootmenu' function expected to draw root menu according to the menu called `name'. To get menu fields you might use following piece of code: #include #include .... int i; menuitem *item; for (i = 0; (item = menu_item (name, i)) != NULL; i++) { if (item->type == MENU_SUBMENU) { /* This field is submenu. You might call here function to construct submenu. item->shortname contains name for submenu */ } /* add menu field here. You might check flags here: item->flags&MENUFLAG_CHECKBOX field have beckbox item->flags&MENUFLAG_RADIO field is part of radio button group. In current implementation there is one radio button group per menu. in both cases you might call: menu_enabled(uih, item) to see if item is checked or not. item->name contains field's text item->key contains hotkey (one letter string in current implementation) } Once field is selected, call function `ui_menuactivate(item, NULL)' where `item' is pointer to `menuitem' record of selected field. Function `enabledisable' is called when checkbox or radiobutton state is changed. The `name' parameter match to `item->shortname' of changed field. So you need to browse all created menus, compare `item->shortname' and in case it match, call `menu_enabled' to obtain new state. For radiobuttons only enable events are noticed. Your code is expected to automatically disable all other radiobuttons in the same submenu. function `menu' works in similar way to `setrootmenu' but displays popup menu. Function `dialog' is called for dialogs. The function should look like: menuitem *item = menu_findcommand(name); menudialog *dialog = menu_getdialog(uih, item); int i; for(i=0; dialog[i].question; i++) { /* Construct dialog, where left side contains labels with dialog[i].question. 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 availble exact). 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. retype dialog[i].defstr to char ** to get pointer to NULL terminated array of the choices. } Once dialog is filled by user, gui_driver is expected to allocate array of union `dialogparam' `dialogparam': dialogparam *p = calloc (sizeof (*p), nitems); fill selected values. `p[i].dint' is used to pass integer value, or number of DIALOG_CHOICE selection, `p[i].number' is used for floating point number, `p[i].dstring' for strings and filenames, `p[i].dcoord[0]' and `p[i].dcoord[1]' for complex values. The string values are expected to be in separate malloced chunks. Once array is filled, call `ui_menuactivate(item, p)'. The function `help' is used to display help about given topic. To implement it you might eighter convert XaoS help file to some native format, or use xshl library to render help page for you. To render xshl page use: #include 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 - emphatized text XSHL_MONOSPACE - monospaced text (typewriter) XSHL_LINK - line (should be underlined or so) XSHL_WHITE XSHL_RED XSHL_BLACK - color of text (not very meaningfull here) XSHL_RIGHTALIGN XSHL_CENTERALIGN - alignment of the text } lines = help_make (name, getwidth, textheight, largetextheight); if (lines == NULL) lines = help_make ("main", getwidth, textheight, largetextheight); Now you might use `lines' to draw the help. It is pointer to the arraw of structures: struct xshl_line { int y; struct xshl_item *first; }; `y' is possition of the line from beggining of text and first is pointer to the blocks of texts on the line. Last line contains NULL pointer in the first section. `first' is linked list of the structures: struct xshl_item { struct xshl_context c; char *text; int x; int width; struct xshl_item *next; }; you might draw text `text' on the possition `x' (and `y' from the line record) using style described by `xshl_context': struct xshl_context { int flags; char *linktext; }; `flags' have same meaning as in `getwidth' section. `linktext' is name of the next help page in case field have XSHL_LINK atribute. As an example of `gui_driver' see win32 driver code.  File: xaosdev.info, Node: eui, Next: ui-helper, Prev: gui-driver, Up: Top Writting an external user interface *********************************** This part describes, how to make an external user interface--it is the separate program, which makes an window with all menus and dialogs. It uses XaoS engine for calculating the fractal as separate process. This design brings many advantages--the external GUI implementation should have an "native look" for given platform and should contain many extensions, such as multiple windows etc. Also all calculation are done in the multitasking and user interface is usable even when engine is busy. The X window provides a way, when program draws into other's window--"master" program creates window and sub-window, where he wants to have fractal, then calls engine with `-windowid' NUMBER_OF_WINDOW parameters. It instead of creating new window uses specified window. Most famous example of such cooperation is probably ghostscript/ghostview. Other windowed environments probably provides similar way for cooperation. At others it should be implemented using shared memory, so it should work at most platforms, I expect. Of course, you might also design UI as separate button box in another window, like most of animation players, or Imagemagick have. In fact external GUI should be very similar to Imagemagick style. basic concept ============= The UI implementation has function to disable it's GUI functions. Because of the function registry, all it's menus and dialogs are described in the fairly simple database. This database is mapped also to the scripting language similar to scheme. So the external UI implementation just translate the actions into this scripting language and sends it trough pipe. This commands should be created automatically from the database, as well as menus and dialogs, so UI don't need to have special code for various XaoS features. At the beginning it should use XaoS' command `(print_menus)' to force him to send information about database, then build menus using this information. For this you need just some equivalent to UNIX pipes, so again I expect it is doable at most platforms. starting XaoS as slave process ============================== One of the first thinks, engine needs to do is to initialize XaoS in right mode to work as slave process. For this you need to do several thinks: * Open the pipe * Disable builtin GUI * Read menu hierarchy (this is optional--GUI can also have all menus coded into it. But it is not recommended, since it will make problems with future adding new features) Opening pipe is done via `-pipe' option. It takes one parameter, which is name of FIFO you want use. If you specify "`-'", XaoS will read input from stdin. To disable XaoS GUI use option `-nogui'. This will disable all menus, dialogs and help text. To read menu hiearchy just add `-print_menus' parameter and then parse XaoS's output. This will print the whole hierarchy. In case you are building menus at the time, they are selected, you might prefer usage of the command `print_menu'. It prints just one menu without it's sub-menus, so it's output should be directly used for building it. It takes one string parameter, which is name of menu you want to print. To print root menu use `"root"'. Option should look like this: `-print_menu root'. Under X Window you need also specify the `-windowid'. Also the `-shared' is quite recommended. Otherwise in pseudocolor visuals XaoS will create it's own colormap, wich will most probably collide with UI's colormap and XaoS or UI will have false colors. If you have any idea, how to avoid this, let me know. You might also let user to specify some extra parameters from the command line. You should simple add the to the end of command line. The `-nogui' and `-print_menus' commands must be first for the simple reason: XaoS parses it's command line in the early initialization stages. Some commands (like `-print_menus') should be processed at this time, while others (like `-loadpos' needs to have working engine. This commands are queued and processed later, once engine is initialized. In case some such command is before `-print_menus' XaoS will decide to keep same order of commands, so it will queue `-print_menus' too. This will case, that menus will be printed much later and startup will be slower. So the proper calling sequence for the user interface under X should look like: xaos -nogui -print_menus -windowid -share -pipe - [OTHER OPTIONS] Parsing the menu structure ========================== The structure is printed menu by menu. Each menu contains an header, some entries and `endmenu'. Whole listing from `print_menus' is terminated by `endmenus'. The header starts with `menu' and then contains an identifier of menu and full name. Such as: menu "fractal" "Fractal" Then each entry has its own line. It starts by type, which should be `submenu' or `menuentry'. `submenu' has similar format to header--fullname of menu and identifier. `menuentry' adds next few fields. It has an type of entry, which should be `normal', `radio' or `checkbox'. `radio' and `checkbox' are followed by `on' or `off' specifying whether it is enabled or disabled. The radio-buttons don't have explicit information about groups they belongs to. For now I just expect, that each menu contains just one such group, so it is clear. Then set of flags should follow. Currently two flags are defined. `dialog', wich specifies, that function has dialog, and `dialogatdisable'. By default, dialog for check-boxed functions are displayed just in case they are enabled. The second flag reverses this behaviour. It is now used for `mandlebrot' function, which behaves in this style. When you disable it, user is prompted for the Julia seed. So specification should look like this: 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 Activating functions and dialogs ================================ Once the menu structure is built and user selects some item, it should be activated. It is done by simple command: `(NAME)'. Once "`)'" is sent, command is executed by XaoS. Check-boxed functions has one extra parameter--`#t' to enable them and `#f' to disable. So if you want enable item `autopilot' send: `autopilot #t' Radio-buttons don't have any such special parameter--because disabling radio-button is nonsense. In case, item has flag dialog enabled, engine expects that UI will make dialog first, ask user of values and then call function with parameters. UI needs first to know, what parameters function expect. It is done by sending command `(print_dialog "NAME")'. XaoS replies with dialog specification very similar to menu specification. It has header `dialog' followed by the name of function. Then one dialog entry per line is sent. it is started by `dialogentry' followed by question UI should display. The is type, which should be one of the following: `integer' Integer number such as `123' `float' Floating point number such as `123.123' `string' String such as `"ahoj"' `keyword' String such as `'ahoj'. The keywords are mostly similar to string, except they can not contain space. They are used for example for specifying formula type. Strings are used for printing texts etc. `inputfile' `outputfile' Here UI should display file selection dialogs. With `outputfile' it is also good idea to check, whether file exist and in this case make some overwriting dialog too. `onoff' Boolean value (`#f', or `#t') `complex' Complex value--two floating point numbers such as `123.123 123.123' `choice' Choice between some keywords. Those keywords are send after `choice' in enclosed the `{' `}'. Last information at the line is the default value in the same format as examples above. For files, the default value is in format `"[PREFIX]*[EXTENSION]"'. Some examples: 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 To activate function, send command which contain function name, possible `#t'/`#f' in check-boxes and parameters in the same order as in dialog, same format as in examples, separated by the space. Such as: (uiview 0 0 0.5 0) (load "text.xpf") (color 'white) Synchronization =============== In some cases, XaoS can change radio-box and check-box values. (like when user pressed a key, or loaded some file). So all changes are sent to GUI, wich should inform about this. They are sent to standard output in following format: checkbox "name" on/off radio "name" on/off So your GUI should parse this and change it's menus when necessary. Also XaoS's menus can contain more distinct trees. In some cases (like when animation replay is active) root of menu structure should change. The XaoS sends command: root "name" Also user can press keys, which normally displayed menus, dialogs or help. Then XaoS sends commands: menu "name" dialog "name" help "topic" All this commands should be taken into account by GUI, or should be ignored. help ==== XaoS's help is in the simple hypertext language. In order to simplify it's parsing I've made an xshl and help libraries. So making of help window should be quite easy. Just call help function: - Function: struct xshl_line *help_make (char *COMMAND, int GETWIDTH (void *, int FLAGS, char *TEXT), int WIDTH, int SMALLHEIGHT, int BIGHEIGHT); and you will receive of listings of text with positions, where to print into window. `command' parameter is topic of help. `getwidth' function is function, wich returns width of given text. `width' is width of window, `smallheight' is height of small font and `bigheight' is height of big font. Please ask me for more details if necessary. And thats all. Good luck with coding.  File: xaosdev.info, Node: ui-helper, Next: xthreads, Prev: eui, Up: Top UI-helper library ***************** UI helper library takes care to all XaoS' engine functions and features and gives the higher level API, which is quite easy to understand. If you want to write completely new user interface (replacement for the ugly interface--not just new bindings for native menus or external user interfaces) or you want to use XaoS engine in your program as an library, you will probably want to use this library. It's API has many calls and features. This section gives just brief overview of it's calls. Please ask me about details. initialization ============== To initialize ui helper library, you need to prepare an palette and image. Palette is created using palette library calls `createpalette'. Creating truecolor palette should look like this: struct palette *pal = createpalette (0, 0, TRUECOLOR, 0, 0, NULL, NULL, NULL, NULL); For details about creating palettes see `ui.c' or ask me. To create image call: struct *image img = create_image_mem (width, height, 2, pal, pixelwidth, pixelheight); This creates image in the memory. If you want to create it in your own buffers, you might use `create_image_cont' or `create_image' calls. Again see `ui.c'. Then it is time to fire up main library: struct uih_context *uih = uih_mkcontext (0, img, passfunc, NULL, NULL); The `passfunc' is called when engine is calculating. It might process events and display process information. It should look like this: 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 */ } } It might set `uih->interrupt', if it wants to interrupt current calculation. You also might load the catalog file in order to make tutorials working: uih_loadcatalog (uih, "english"); Since this ui_helper library is fully functional and you might enter the main loop. main loop ========= UI helper library does an timing primitives. So it expect an standard form of the main loop. It asks the program to display changed image when necessary. Library also use timerlib for it's timing. So read section about this library, since you might use it for your purposes too. Main loop should look like this: 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 given time in usec - wait of events etc..*/ } /*and repeat*/ } calling functions ================= UI helper library has many functions declared in `ui_helper.h' for various actions. There is too much of them to describe, but their names are quite informative, so I hope you will not have problems. You might also use XaoS function registry, which does all this stuff for you. You will just draw menus and dialogs based at this registry and automatically all features will be available. So if you are writing an ordinary user interface, this is the preffered way. Note that `ui_helper' library is not reentrant, so you can't call most of this function from the `passfunc'. If you are using registry, activating function handles this automatically and queues functions when necessary. To process them you need to flush queue in the main loop as follows: static void processbuffer (void) { menuitem *item; dialogparam *d; if (uih->incalculation) return; while ((item = menu_delqueue (&d)) != NULL) { menu_menuactivate (item, d); } } closing library =============== This is done using: uih_freecontext (uih); One implementation of user interface at the top is ugly interface. See dirrectory `src/ui'. Another, much simpler is `render.c', which does animation rendering.  File: xaosdev.info, Node: xthreads, Next: filters, Prev: ui-helper, Up: Top XaoS thread library ******************* This description should be useful for those, who want to port XaoS into multiprocessor platforms and those, who want to implement some filter or other relatively computational expensive code. Note that thread library should be mapped into nothread calls, in case host does not allows multi-threading or it is not SMP architecture (since this library is used only to distribute calculation into other CPUs) XaoS thread library is simple map of few functions required by XaoS to system library for threads. It has following variables: - Variable: ethreads This is set to 1 in case that threads are enabled - Variable: nthreads Number of threads It and following calls: - Function: void xth_init (int THREADS) This function initializes threading library (starts threads, sets ETHREAD to 1 and NTHREADS to n. THREADS parameter should be set to 0--auto-detection or number of threads users wants. In case threads is set to 1, threading library is disabled and following functions are mapped into those nothread_ equivalents defined in `xthread.h'. Note that threads are not identical--there is main thread (one that called xth_init) that communicates with drivers, controls calculation etc. and other tasks that are waiting to orders from main task. They also can't use functions from xthread library. - Function: void xth_uninit (void) This function UN-initialize thread library--kills child threads, sets ETHREAD to 0 and NTHREADS to 1. - Function: void xth_function (xfunction *FUNCTION, void *data, int RANGE) This function is used in case, engine wants to perform some operation at image in parael. It is expected to wait until all threads are ready and start FUNCTION at all threads including control one with following parameters: DATA--this parameter is same as DATA passed to xth_function, TASKINFO--pointer to structure taskinfo, that is platform depended (defined in `xthread.h') but must have at least field `n', that holds number of thread (control thread has 0 and other numbers in range 1 - NTHREADS). Next two parameters is range of image, function is expected to do action. Xth_function is expected to divided RANGE into NTHREADS equal pieces and pass always start of piece and start of next piece (RANGE in case of last one). Function does not wait for other threads at the end and returns immediately to main thread after FUNCTION returns. This function is called approx. 5-10 times per frame - Function: void xth_sync (void) This functions waits until all threads are ready for next order from main task. This function is called approx 5-10 times per frame - Function: void xth_bgjob (xfunction *FUNCTION, void *DATA) This function is expected to behave as follows: look if there is any thread waiting for orders, if so, ask him to call FUNCTION with similar conventions as in xth_function except that range parameters are set to 0. Otherwise it starts function in normally (at foreground). This function is called once per frame. - Function: void xth_nthread (struct taskinfo *S) This function should be used to determine number of current thread. Do not use `taskinfo->n' instead since in case threads are disabled, it should be defined to 0 and that allows optimizer to perform better optimizations. This function should be called by all threads. - Function: void xth_lock (int N) - Function: void xth_unlock (int N) Lock/unlock lock number N. At least `MAXSEMAPHORS' locks must be available. Note that locks are used always for very short fragments of code so they needs to be fast. So spin-locks are maybe better than Dijskra semaphores. Untested. They are called once per calculated line/row during zoom and once per approx 10 pixels during calculation of new image. - Function: void xth_sleep (int N, int L) It is expected to atomically unlock lock L and sleep in queue N. At least `MAXCONDS' queues must be available. After it is waked up, lock L again. This mechanism is used by calculation of new image algorithm, but it is designed to minimize its calls, so I expect they should be called once or twice. - Function: void xth_wakeup (int N) Wake up some thread from queue N. Lock used by sleep calls is locked in this cases. Function should also wake up all threads if such operation is not supported by host API. With luck, this function should not be called at all. It should be called by new image calculation routines in case queue is empty. This happens in case of 50 threads but happens rarely at two or eight threads according to my tests. - Function: void xth_wakeall (int N) Similar to wakeup but wake up all threads.  File: xaosdev.info, Node: filters, Next: algorithm, Prev: xthreads, Up: Top Filters ******* This is a brief description of filter system used internally by XaoS. Filters in XaoS provides an object oriented interface to every part of XaoS engine. Main filters are: User interface implemented in ui_helper.c and zooming engine implemented in zoom.c. Filters are connected into an queue--at the beginning there is just two filters here(zoom and ui) but later additional filters should be inserted into the middle of queue like an stereo-gram generation etc. The queue supports operations like remove filter, add filter and initialize. In the calculation every filter should use data calculated by filter lower in the queue. Data are stored into image. So for example stereo-gram filter should use fractal generated by zooming engine and create an stereo-gram. This makes XaoS's code more flexible and makes easy future enhancements like different zooming engine, image rotation, other special effects, plug-ins and some other funny stuff since interface of each such part is well defined and each filter has quite good control over his child. So stereo-gram filter should change palette, force zooming engine to change depth, width and height of calculated image to fit his needs and so on. This document describes mainly creating of filter like stereo-gram generator i.e. filter placed into middle of queue since I don't expect there will be many people creating "terminal" filters (zooming engines/user interface layer) note that different user interface is possible since user interface layer is not the real user interface just set of high level functions that should be called by main application like set_view. So in case you want to use XaoS as an calculation engine in your program this document is probably not for you. Each filter is defined by filter_action structures as follows: 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); }; This structure describes static filter's parameters (like its name) and basic set of methods required for communication with resto of XaoS. The name field describes filter's name like "An random dot stereo-gram generator". Name is displayed by ugly interface in filter's menu. So it is expected to be descriptive and shorter than 30 characters. The short name is one word long name for filter like "stereogram". This name is used by save files, possibly by command line parameters. Simply everywhere where user should need to write it and writing long descriptive name should be just wasting of time and disk space. Flags field is kept for future enhancements and is expected to be 0 for now. Creating / destroying of instance ================================= Functions getinstance and destroyinstance are equivalents to constructor and destructor in OOP. Getinstance is expected to create and fill following structure: 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; }; Altrought this structure seems to be long and complex, most of fields are unused at this time and rest of them are filled automatically by function: - Function: struct filter * createfilter (struct filteraction *FA); That should be used to create instance. Only possibly interesting field is data. It's pointer reserved for filter's internal use. So it should be pointer to filter's internal variables if required. Following is example implementation of get-instance with allocating of such additional structure. In case nothing similar is required you should use directly createfilter at getinstance's place. 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); } The destroyinstance is expected to free memory used by filter structure and all internal data of filter. To free filter structure use normal free(filter); So implementation of such function should look like: static void destroyinstance(struct filter *f) { destroyinheredimage(f); free(f->data); free(f); } The meaning of destroyinheredimage will be described later. Initialization ============== During initialization phaste each filter says to his parent what kind of images it supports (this should depend on images supported by his child), parent chooses best supported image format for his purposes and gives it to the child. Initialization is done in two pases. First pass start by lowest filter in the queue and each filter passes to his parents requirement structure. Second pass starts by the highest filter and each filter passes to child an image and some other stuff. Then calculation should begin. Queue needs to be reinitialized after creating, resizing, adding/removing of filter and similar operations. First pass is implemented using require function. This function is expected to take care at child's requirements it received as parameter, fill requirements structure and call require function of his parent filter. struct requirements { int nimages; int supportedmask; int flags; }; The nimages field should be set to 1 or 2. In case it is 2, parent filter must pass image with two buffers. Note that in case it is 1, parent should pass image with two buffers too. Supported mask is mask of supported image types by filter. Image types are following: `C256' An normal 8bpp image with palette `TRUECOLOR24' An 24bpp truecolor image with 8bits for each color. `TRUECOLOR16' An 16bpp truecolor image `TRUECOLOR' An 32bpp truecolor image with 8bits for each color. `LARGEITER' An 16bpp image but w/o colors. It is expected to hold number of iterations it should be also tought as 16bpp grayscale image `SMALLITER' Similar to `LARGEITER' but 8bpp In case you don't worry about palettes, allocations of colors and you do just some operation with bitmap, so you worry just about depth of image you should use mask of following: `MASK1BPP' for 8 bit images, `MASK2BPP' for 16bit and so on. The latest field of requirements structure is flags. It mask from following constants: `IMAGEDATA' in case your filter requires data from previous frame untouched. In case this is not set, filters should reuse your image and change it. But some filters like and motion blur or zooming engine requires data from previous frame to construct new, so this flag should be set there is no more flags supported at the moment. Function require should also save child's require structure into filter->req for later use by initialize pass. So you should look like: 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*/ } Next pass is main initialization. It goes in opposite order(from parent to child) and child's infers some stuff from parent like images etc... The initialize structure receives an initdata structure: struct initdata { void (*wait_function) (struct filter *f); struct image *image; struct fractal_context *fractalc; int flags; }; an wait_function is function called by filter during calculation that lets the parent filters(usually user interface layer) to inform user how calculation continues. Image is an image expected to be filled by image in calculation phaste. Fractalc is pointer to structure that will contain information about fractal during calculation(like formula type etc...) Flags is mask of following constants: `DATALOST' this is set in case, that data in image was lost(image was cleared or resized or freshly allocated). Filters that uses data from previous frames should take care to this flag. Zooming engine for example recalculates whole image since pixels from previous frame was lost. Note that data should be lost also in case, filter receives different image that in previous initialization since some filter behind was removed. An inhering process is done using function: - Function: void inhermisc (struct filter *F,struct initdata *I); This function sets fields in filter structure like as fractalc or wait_func. Inhering of image is quite complex, since new image needs to be prepared for child. In order to save memory it is highly recommended to use same image or at least same memory for data when passing to child. But this is not allays possible. Following function implements heuristic to do this: - Function: int inherimage (struct filter *F,struct initdata *DATA, int FLAGS, int WIDTH, int HEIGHT, struct palette *PALETTE, float PIXELWIDTH, float PIXELHEIGHT) You should call this function in your initialize pass. It fills image and childimage in filter structure, prepares initdata for child and creates image for child. Note that it should fail in some cases and return 0. In this case filter is expected to interrupt initialization and return 0 too. An FLAGS parameter is mask of following constants: `IMAGEDATA' in case your filter requires data from previous frame `TOUCHDATA' In case your filter touches data in output image. This is very usual but there is some filters (like interlace or subwindow that don't) `NEWIMAGE' Set in case your filter can not deal with shared images (images that have input data in same memory are as output) WIDTH and HEIGHT should be set to 0 in case you want same width/height as in parent image or width and height of image you want to pass to child. PALETTE is palette of image you want to pass. Set to `NULL' if palette should be inhered from parent's image (usual). PIXELWIDTH and PIXELHEIGHT specifies physical size of pixel in centimeters. If set to 0 they are inhered from parent's image. In case you use inherimage mechanism you also must call destroyinheredimage in destroyinstance function and updateinheredimage at the beginning of calculate function. Example implementation: 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)); } Also note that fractal context hold pointer to fractal palette. In case You don't change image palette everything is OK. But in case child's image differs from parents image, there should be two behaviors--fractal's palette is child one (this should be common for example in conversion filters ( 8bpp to truecolor etc)) or fractal's palette is parent's one (like in edge detection filter). By default fractal's palette is kept to parent's one. This should be changed by setfractalpalette call. It has two parameters--filter structure and palette. When you pass as palette child's palette, fractal's palette will be changed to child. In case you pass NULL. Changing of palette will be disabled (like in motion blur filter in 8bpp mode). Note that this is changed just in case you still have access to fractal palette. Some parent should redirect palette before. Than this function does nothing. Caluclation =========== Main calculation is done using doit function. It is expected to call child's calculation function when required and apply filter at output. It receives flags. Only flag in `INTERRUPTIBLE' for now. It is mainly for zooming engine so I do not describe it here. But filter is expected to pass this flag to child. Next parameter is time in milliseconds that expired since last doit call. It should be used to calculate speed of animation. Calculation loops returns flags. Flags is mask from following constants: `ANIMATION' in case filter performs some animation and expect that calculation will be called again soon `CHANGED' in case something changed in output image (usual) `INEXACT' This is enabled by zooming engine in `INTERRUPTIBLE' mode in case that time exceeded. An doit function changes image. Image structure contains following fields significant for you: `bytesperpixel' number of bytes per pixel (image depth) `palette' palette of image. `currlines' array of pointers to beginnings of every scanline of image `oldlines' array of pointers like currlines but for previous image in case doublebuffering is enabled `nimages' set to 2 in case doublebuffering is active `flipimage' pointer to function that flips oldlines and currlines. palette structure contains following significant fields: `type' type of palette/image (`C256', `TRUECOLOR' etc...) `size' number of allocated entries `pixels' array of allocated entries. Conversion table from number of iteration to pixel value. `rgb' Rgb values for pixels (`NULL' for `TRUECOLOR', `HICOLOR' and similiar types) To make easier writing calculation loops for different depths `pixel8_t', `pixel16_t' and `pixel32_t' are predefined. You also can use include system as in edge detection filter, that lets you write calculation loops just once and use cpixel_t and it will be compiled for every bitmap depth. See edge detection filter (engine/edge.c and engine/edged.c) for implementation details. Conversion ========== Convertup and convertdown functions are used for converting screen coordinates to position in fractal and back. Convertup is function that receives coordinates in child's image and is expected to convert them into coordinates in parents image and call parent's convertup function. Convertdown is reversed(from parent to child). In case coordinates respond 1:1 you should use convertupgeneric and convertdowngeneric. In other case implementation should look like: 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); } Removing of filter ================== Before filter is removed from queue, removefilter function is called. It is expected to clean up thinks filter changed. Should be NULL in most cases. Registering of filter ===================== Once filteraction structure is filled, filter is done and you should try to enable it. To enable it in user interface you need to edit ui/ui_helper.c, add filter into uih_filters structure and increase uih_nfilters. Note that order of filters in uih_filter is significant, since same order is kept in filter queue, so you should specify if you want to be called before/after filter xy. Then it is high time to start experimenting. Good luck!  File: xaosdev.info, Node: algorithm, Next: timerlib, Prev: filters, Up: Top Algorithm description ********************* The main idea behind XaoS is that it is not required to calculate the whole image every frame. Most pixels are already calculated in 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 flicker a bit and why points do not blink randomly as in recalculated animations. This document describes some of the most important algorithms in XaoS * Saving Previous Pixels * Approximation Algorithm * Moving Pixels to New Positions * Calculating New Pixels * Symmetry * Calculation of Mandelbrot Set * Dynamic Resolution * Autopilot 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 program Frang. It remembers all pixels as x,y and value fields and when it builds new image, it draws all pixels to it and then browses image and fills it by new pixels. Possibly some rle cache should be used for calculated pixels. Frang actually uses algorithm, that takes away pixels out of screen, so it behaves exactly in same way as algorihm described here. At the other hand, this method seems to require much more memory than XaoS algorithm and drawing pixels/browsing image cost quite a lot, so algorithm described here seems to be faster. Since it never requires examining of whole image and new image is constructed using block move operations. For this reason only the last generated frame is used as reference. This way the memory requirements are proportional to 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: Imaginary and real parts of the calculated image are not precise since they are the result of successive iterations of same algorithm. In order to prevent errors from being distributed 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 calculate to fill in the gaps in the frame. Obviously, much less work is done. There are only xsize + ysize calculations instead of xsize * ysize. So the main loop in XaoS looks like this: * Make approximations for rows * Make approximations for columns * Move old pixels to their new positions * Calculate pixels for which there is no good approximation for their row * Calculate pixels for which there is not good approximation for their column but there is one for their row Approximation Algorithm ======================= Introduction to problem ----------------------- You can see that the approximation algorithm is central to the implementation of XaoS. If the 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, in the instance of doubling (i.e., using an old row or column more than once) the resolution will lower. 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 xsize + ysize values but if its speed is proportional to O(n^2), it can be slower than a whole recalculation of the image. Speeds of O(n) or O(n * log(n)) are acceptable. 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 (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 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 step / 2. This cause a considerable slowdown so the limit was returned to 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 to 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): X1 X2 X3 X4 X5 X6 X7 O1 O2 O3 O4 O5 O6 O7 O8 The normal algorithm will aproximate X1 by O2, X3 by O4 but nothing more. For the algorithm with threshold step instead of step / 2: O2 to X1 O3 to X2 O4 to X3 O5 to X4 O6 to X5 O8 to X6 But this will fail with X7. The second algorithm which relies on lower values will do the following: O1 to X1 O3 to X2 O4 to X3 O5 to X4 O6 to X5 O7 to X6 O8 to X7 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. 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 define exactly what is 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 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 (4 * step) * (4 * step). This means that the approximation is acceptable when 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 length < n to make a guess at the length of 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 previous one: Try new position n. Calculate the price and add the best approximation for the previous (row/column 1) one that uses a new position lower than n(prohibits doubling or swapping). This should be one of 8 positions or eventually adding of 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, 9 * n steps are required and n steps to backtrack best approximation. The only problem is that this algorithm is still a little slow (chiefly because of slow memory access on 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 the real problem. The last optimization made was based upon the face that added rows/columns do not have the exact real and imaginary components calculated by (beginning + x * step) but lies at the average of 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 if this algorithm is that adding new rows/columns into their ideal positions should cause miss-ordered results, since some rows/columns should be off more that is distance between them. To avoid this, I use algorithm that always examine start and end of block of new rows/columns and linearly interpolates value between them. Special care needs to be at the blocks that start at the beginning or overs at the end. Implementation should be much faster using custom fixedpoint routines--first recalculate values that 0 means start of image and 65536 means end. Than calculation is much cleaner. Values <0 and >65536 are of screen, calculation is independent at scale and many thinks should be recalculated--like tables for calculating price from distance. Also dividing main loops into many specialized parts and avoiding filing unnecessary parts of table helps. So current algorithm in XaoS is about 5 or 6 times faster than first naive implementation. Moving Pixels to New Positions ============================== Since XaoS is using the approximation algorithm the following table is filled for every row/column: * calculate * oldpoint * position 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 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 about four times and made this function so fast that it is no longer a problem. (In fact, it takes much less processing than all other parts of XaoS.) 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 traverse the whole set and then 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 not 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. 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. Calculation of the Mandelbrot Set ================================= Internal Mandelbrot calculation loop is unrolled--it calculates first 8 iterations using normal method and then it expects that number of iterations will be probably large so it switches into mode, where it calculates iterations in block of 8 with one bailout test at the end. When bailout is received, saved values from previous iterations is restored and last 8 iterations are recalculated slowly to get exact values. This helps a lot especially at Pentium, where conditionals in floating point code is slow. Another stuff is periodicity checking. XaoS has both version of loops--with an without periodicity checks. In most cases it uses nonperiodicity checking version. Periodicity check version is used just in case, some inside set pixel has been found during solid guessing paste around. This is done mainly because periodicity checking version of loop is significantly slower. 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 simple approximated by referencing the nearest other 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 priority to every row/column and calculating the high priority row/column first. The algorithm for adding these priorities is as follows: * Find middle row/column of uncalculated block. Priority is the size of the block (in floating point coordinates) * Start function for left block and right block This function produces quite good results. It tends to make same size 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 center of the zoom anyhow. This is done by simply adding to the calculated priority normal_priority / (abs(newposition - oldposition) / step + 1). This prefers rows/columns that do not move a great deal. Of course, unzooming uses the formula reversed. 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 time is then lower than 15FPS, 15FPS is used instead, since slower animations are unacceptable. At the other hand if it is higher than 35FPS, it is set to 35FPS, since higher framerate is just wasting of computer resources. When image is not animating, this values is changed, so framerate is choose between 5FPS and 15FPS. This caused that images are calculated quickly after zooms stops. 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. Also detects oscillating. Current implementation also does detection of out of range numbers and randomly choosed points are choosed near the old one, to avoid too often changes of direction. SMP support =========== Since version 3.0 XaoS supports SMP. This is done using threads. Most of XaoS routines should be threaded easily--for example moveoldpoints just divides image into n equal part and each part is proceeded by one processor. Only unthreaded part is realloc table calculation routines. I don't see any way to paraelize it except it calculates both--x and y approximation at one time (using two processors). Another interesting algorithm to paraelize is boundary trace. See comments `btrace.c' for discussion about current implementation. Only problem of current implementation I see is possibility, that calculation is divided into too many parts (realloc tables, move points, calculate, symmetries, dynamic resolution) and tasks needs to synchronize between each part. So this should be too slow at real SMP box.  File: xaosdev.info, Node: timerlib, Next: registry, Prev: algorithm, Up: Top The timer library ***************** Timer library is library I did for timing in XaoS. But I found it useful in many other programs (like demonstrations, games, animation players and all 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 is many ways how to design of such timed application (game) 1. read user input, move badies, display and again this way has one disadvantage. Speed of game depends on speed of computer. This was acceptable in old times where only processor was Z80 :) but now with wide variety of various hardwares such internal loop is unacceptable 2. read user input, measure time since last loop and calculate step for badies, move badies for set step, display and again. This way fixes problem with speed. But moving badies just for calculated step, that should differ a much is quite complex, usually introduces complex calculation, floating/fixedpoint math and other unnecesarry stuff that makes program long and introduces many bugs. 3. Set the fixed framerate that is high enough to make game smooth but low enough to do whole internal loop in time. So internal loop should look like: read user input, move badies, display, measure time spent in loop an sleep rest of time until next frame. This is quite popular scheme but has another disadvantage--game can not be designed to use whole CPU power since on slower computers internal loop should take longer time that is reserved for one frame. Game will run slowly again. 4. To take away disadvantage of previous method, many games times just moving of badies and user input. Other stuff like displaying should be done in rest of time. In DOS games moving and user input is often at asynchronous interrupt and drawing runs as main loop. This solves problem in case that drawing of game takes significantly longer time than moving of badies. This is quite usual so this scheme works well. 5. previous scheme still has one problem--since timer interrupt works asynchronously, there should happend many race condition, in case moving takes longer time than time reserved from frame, computer can crash. So this scheme should be enhanced into synchronous one with exactly same result but avoiding problem with race condition: read user input, measure time spent by loop and calculate how many simulated frame interrupts activated since last activation, if zero sleep until simulated interrupt, move badies as many times as required, display this is an combination of 4 and 3 and seems to be most comfortable way for writing games but since main loop is now quite complex many games don't do that. 6. there is still one small problem. Method 5 expect that moving takes significantly lower time that displaying. This may not be truth. Simple work around is to write moving routine, that should move for x moves by faster way that calling move x times. This is often possible and makes easy extension to scheme 5. This scheme allows you to use very large maximal framerate (say 100FPS) and to have same results as method 2 (that is maximally exact method) As you can see, designing of main loop isn't so easy. This is just very simple example. More advanced application for example should want to move one set of badies at one framerate and other at different framerate. This requires two such timings. Another complication is that there is many different ways to measure time exactly at different platforms. Under Linux you can measure using gettimeofday but under DOS this is exact just to 1/18 of second and thats too low for smooth animation and so on. Thats why I decided to design portable easy to use timer library, that makes easy to implement all described method, combining of them and much more. During design I taken care at the following thinks: quality of timing, how easy to use it is, speed, portability and to minimalize inexpected situations (like race conditions in asynchronous interrupts and so on) The name of game ================ Timer library operates with "timers". They should be created, you should measure time since last reset, pause them or set "handler" and "interval". But handler is not activated at given interval yet. Since timer library is not asynchronous, you must activate them. For activating is used "groups". You should process group at some place in your program. Then all timers in group are checked and their handlers activated if required. When time spent since last activation is higher than interval, handler is activated more times. Also interval to next invocation is calculated to keep frequency. Simple scheduling is performed at handler--handler is activated just once and then all other timers are checked before it is activated again. You should also define an multihandler--handler that is activated just once and receives argument how many intervals has left. There is two special groups--`asyncgroup'. Timers in this group are activated asynchronously like from interrupt. It is not recommended to use it, since it brings many problems and usually isn't required. Also it does not work at many platforms. `Syncgroup' is the default group. Program is expected to process is quite often. In case you don't need to use more groups, 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 `tl_updatetime'. I used this way in order to minimize context switches but later I found this scheme very useful, since you should lookup timer, do something and then reset it and don't wory about time spend between lookup and reset since it is 0 in case you did not called tl_updatetime. This helps to keep frequency of timers exact w/o any errors caused by such situations. At the other hand you need to call tl_updatetime at least once in your main loop. Maybe you don't know why to create more groups, but I found it quite useful. For example an autopilot in XaoS has such special group--I need to call it approx. every 1/20 of second but just at one place in program. Invoking of autopilot when calculation is active should produce incorrect results, so I have special group for autopilot and process just at one place where I am sure it is safe. Timers should be also emulated. You should stop them and then control flow of time for given timer. This should be quite useful for example when you want precalculate animation at given framerate. To control group of timers, you might create "emulators". It is just another time controlled by you. It is useful in cases you want to emulate fixed framerate (for animation rendering) or such. Time functions ============== - Function: void tl_update_time (void) Update time used by timerlib. This must be called at least once in main loop otherwise time will not flow. See above. - Function: void tl_sleep (int TIME) Sleep given time. Similar to usleep at POSIX. Group functions =============== - Function: tl_group* tl_create_group (void) Allocate and initialize group header. Returns NULL when malloc fails. - Function: void tl_free_group (tl_group *GROUP) Free memory storage used by group structure - Function: int tl_process_group (tl_group *GROUP, int *ACTIVATED) Process timers in group and activates their handlers. Returns time until next invocation. Main loop should sleep returned time then. An ACTIVATED parameter sould be `NULL'. If it is non `NULL', variable is set to number of activated handlers. Timer functions =============== - Function: tl_timer* tl_create_timer (void) Create timer structure. - Function: void tl_free_timer (tl_timer *TIMER) Free memory storage used by timer structure - Function: void tl_reset_timer (tl_timer *TIMER); Reset timer to current time. (time of last actication of `tl_update_time') - Function: int tl_lookup_timer (tl_timer *TIMER); Return time since last call of tl_reset_timer or last activation of handler. - Function: void tl_set_interval (tl_timer *TIMER, int INTERVAL); - Function: void tl_set_handler (tl_timer *TIMER, void (*HANDLER) (void *),void *userdata); - Function: void tl_set_multihandler (tl_timer *TIMER, void (*HANDLER) (void *,int),void *userdata); Handler, multihandler and interval control functions - Function: void tl_add_timer (tl_group *GROUP, tl_timer *TIMER) Add timer to given group. Timer should be added into just one group. - Function: void tl_stop_timer (tl_timer *TIMER) - Function: void tl_resume_timer (tl_timer *TIMER) Stop and resume timer. - Function: void tl_slowdown_timer (tl_timer *TIMER,int TIME) Time in timer is moved back for given time. Emulator functions ================== - Function: struct timeemulator *tl_create_emulator (void); This function creates new emulator--you need to create one first before emulating. - Function: void tl_free_emulator (struct timeemulator *T); Destroy emulator's data - Function: void tl_elpased (struct timeemulator *T, int ELPASED); Move emulated time. - Function: void tl_emulate_timer (struct timer *T, struct timeemulator *E); Set timer to the emulated mode. Since now all time is controled by emulator E. All other behavior of timer keeps unchanged. - Function: void tl_unemulate_timer (struct timer *T); Disable emulated mode for the timer. Example main loop ================= while(1) { time=tl_process_group(syncgroup,activated); /*Call game control functions*/ update_keys(); if(activated) /*something changed*/ display(); else tl_sleep(time); }  File: xaosdev.info, Node: registry, Next: index, Prev: timerlib, Up: Top XaoS function registry ********************** XaoS has an ui helper library, which provides functionality provided by user interface. All it's useful functions are registered into central registry. This registry is used to generate menus and dialogs as well as command line options or scripting language. So it is very significant think in XaoS design. Not only those who want hack XaoS ui-helper layer needs to know this, but also authors of drivers should use this to add new driver depended functions into XaoS's menu. Also external user interface is based at this. Main idea behind external user interfaces (currently one for TCL/Tk and Gtk is under development) is following: XaoS transfers it's registry to interface (using simple description language). User interface starts XaoS in it's window and builds menus and dialogs based at this registry. Then once user select some function, it creates an command for scripting language and sends it back to XaoS' engine. So knowledge of this part is essential for many developers. Please take care for this part. The implementation of registry is in file `xmenu.c', header is `xmenu.h'. Mainly for historical reasons it speaks about menus and dialogs. (It was designed for the GUI at the beginning) I am keeping this terminology, since it is quite clean and easy to understand instead of talking in some cryptic abstract term. Function description ==================== To add function into database, you need write it's description into menuitem structure. It has following prototype: 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; - Variable: menuname Name of menu (or category) function belongs in. The root of all categories is called `"root"'. XaoS alternativly uses an `"animroot"' when animation replay is active. If you are adding an function, it is better to add it into some subcategory like as `"ui"' (it will be placed into UI menu then) or create an new category for your functions if needed. It will appear as submenu in main menu in the UI. - Variable: key Ascii code of hotkey to activate this function. Use `NULL' if none. - Variable: name Longer name of functions used in the menu entry, or `xaos --help' listing - Variable: shortname One-word name of function used in command language and references to function. - Variable: type type of function--this is not return type. Type should be one of following constants: `MENU_SUBMENU' An submenu. This is not real function, but name for submenu. You might fill the KEY, NAME, SHORTNAME functions. An name of this new submenu is placed in the field PPARAM. `MENU_NOPARAM' An normal function without any parameters. When actived, function FUNCTION with just an pointer to `uih_context' will be called. `MENU_INT' This should be used to simplify entering of more similar functions (handled by just one universal function in the c code). This function is handled in same way as `MENU_NOPARAM', but also in one integer parameters taken from `iparam' is passed. `MENU_STRING' Similar to `MENU_INT' but uses string instead of integer. `MENU_DIALOG' If you function needs some paramters, use dialog structure to describe them. In scripting language your function will have parameters then, in user interface dialog will be displayed. PPARAM must then point to array of dialog entries. Witting them will be described later. If your function has just one parameter described in dialog structure, it will be called in normal c way--if you want string parameter, one pointer pointing to string (in addition to `uih_context') will be passed to the functions. In case of multiple parameters are requested, it is impossible to call function in c way without special wrapper for each such case. So it will receive pointer to array of `dialogparam' unions, wich contains one entry for each parameter. `dialogparam' is declared as follows: typedef union { char *dstring; int dint; number_t number; number_t dcoord[2]; xio_path dpath; void *dummy; } dialogparam; `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 function DIALOG is called first, and it is expected to return pointer to correct dialog structure. Dialog structure must lie in static storage (since it is not freed) and always must have the same fields, just differ in the default values. Also this function must work correctly even in the case pointer to `uih_context' is `NULL', since it is often called in the initialization stages (parameter parsing etc.) - Variable: flags Flags are used to set additional information about function: `MENUFLAG_CHECKBOX' Some features act like check-box--i.e. by one calling of function they are enabled, second call disables this. In menu it is useful to add an check-box for this function indicating whether feature is on or off. And thats exactly what this flag does :). You need also define the function CONTROL wich return's `1' when enabled and `0' when disabled. In order to let external GUI's work correctly you need also call `uih_updatemenus("name")' every time state of this function changes. In the scripting language this adds an first parameter, wich if `#t' or `#f'. Engine then calls function just when it is necessary. For `#t' dialog is requested, with `#f' function is called just as `NOPARAM'. I.e. dialog is displayed just when enabling feature. `MENUFLAG_DIALOGATDISABLE' In checkbox display dialog when this feature is disabled instead when enabled. `MENUFLAG_RADIO' Other features act like radio-button. Control function in this case receives same parameter as defined for `MENU_INT' or `MENU_STRING' types and is expected to return `1' when enabled. You also need to call `uih_updatemenus' when changed. No special parameter is added in the scripting language. `MENUFLAG_INTERRUPT' Interrupt current calculation when this function called (it is used by functions with causes recalculation of screen) `MENUFLAG_INCALC' By default XaoS queues functions and calls later when they are activated in caluclation. This flag disables this feature. `MENUFLAG_ATSTARTUP' By default XaoS queues functions and calls later when they are activated as command line parameters (in time engine is not fully initialized yet). This flag disables this feature. `MENUFLAG_NOMENU' Function will not be visible in the menu `MENUFLAG_NOPLAY' Function will not be available as command in scripts `MENUFLAG_NOOPTION' Function will not be available as command line option Initializing menuitem structure as static variable ================================================== In most case menuitems should be wrote as static variables. Because contents of this structure should change in future, please use one of macros defined in `xmenu.h'. They provides cleaner and easier to extend way to define this entries. For example to define `MENU_NOPARAM' function use following macro: - Function: MENUNOP (MENUNAME, KEY, NAME, SHORTNAME, FLAGS, FUNCTION) Similar macros exist for the other types too. They ends for `CB' or `RB' for check-boxed or radio-boxes functions. See `menu.c' for large example of definitions. They should look like this: 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), ... Dialog description ================== Dialog description is similar to menuitem. It is array of following structures: typedef struct dialog { char *question; int type; int defint; char *defstr; number_t deffloat; number_t deffloat2; } menudialog; It is terminated by QUESTION pointer set to `NULL'. The QUESTION contains string ui should display when is asking for this field. TYPE should be one of following values: `DIALOG_INT', `DIALOG_FLOAT', `DIALOG_STRING', `DIALOG_KEYSTRING' (the difference between string and keystring is, that in scripting language string is passed as `"hello"', but keystring is passed as scheme keyword as `'hello'), `DIALOG_IFILE' (input file), `DIALOG_OFILE', `DIALOG_CHOICE' (choice between different keystrings), `DIALOG_ONOFF' (boolean parameter), `DIALOG_COORD' (two floats--complex number) Set to corresponding DEF* field for default value. In case of files use string in the format `"[PREFIX]*[EXTENSION]"'. For type `DIALOG_CHOICE' set DEFSTR to pointer to array of strings terminated by `NULL' entry. To write dialog structures again use macros defined in `xmenu.h' like: DIALOGSTR(question,default) The definition should look like: static menudialog uih_viewdialog[] = { DIALOGCOORD ("center:", 0, 0), DIALOGFLOAT ("Radius:", 1), DIALOGFLOAT ("Angle:", 0), {NULL} }; Modifying registry ================== - Function: void menu_add (menuitem *ITEM, int N); Add array of N items to the database. - Function: void menu_delete (menuitem *ITEMS, int N); Remove array of N items from database. Querying registry ================= - Function: menuitem* menu_findkey (char *KEY, char *ROOT); Find item for given key. ROOT is menu where to start (submenus are searched recursively) - Function: menuitem* menu_findcommand (char *NAME); Find item for given short name - Function: char* menu_fullname (char *MENU); Long name for the short name of menu - Function: menuitem* menu_item (char *MENU, int N); Nth entry of the MENU. Return `NULL' if no more entries available. - Function: int menu_enabled (menuitem *ITEM, struct uih_context *C); Check whether given item is activated (for check-boxed and radio-boxed functions) - Function: int menu_havedialog (menuitem *ITEM, struct uih_context *C); Does this function have dialog? - Function: menu_getdialog (CONTEXT, M) Macro returns pointer to dialog structure. (function must have it, otherwise garbage is returned). - Function: int menu_available (menuitem *ITEM, char *ROOT); Check whether item is available as one of entries of ROOT (or it's submenus)  File: xaosdev.info, Node: index, Prev: registry, Up: Top Index of functions, variables, types and constants ************************************************** * Menu: * alloc_buffers: driver. * ANIMATION: filters. * C256 <1>: filters. * C256: driver. * CHANGED: filters. * createfilter: filters. * display: driver. * ethreads: xthreads. * filter: filters. * filteraction: filters. * FIXEDCOLOR <1>: filters. * FIXEDCOLOR: driver. * flip_buffer: driver. * flush: driver. * free_buffers: driver. * FULLSCREEN: driver. * getinstance: filters. * getsize: driver. * GRAYSCALE <1>: filters. * GRAYSCALE: driver. * image <1>: filters. * image: design. * INEXACT: filters. * inherimage: filters. * inhermisc: filters. * init: driver. * initdata: filters. * MASK1BPP: filters. * MAXCONDS: xthreads. * MAXSEMAPHORS: xthreads. * menu_add: registry. * menu_available: registry. * MENU_CDIALOG: registry. * menu_delete: registry. * MENU_DIALOG: registry. * menu_enabled: registry. * menu_findcommand: registry. * menu_findkey: registry. * menu_fullname: registry. * menu_getdialog: registry. * menu_havedialog: registry. * MENU_INT: registry. * menu_item: registry. * MENU_NOPARAM: registry. * MENU_STRING: registry. * MENU_SUBMENU: registry. * menudialog: registry. * MENUFLAG_ATSTARTUP: registry. * MENUFLAG_CHECKBOX: registry. * MENUFLAG_DIALOGATDISABLE: registry. * MENUFLAG_INCALC: registry. * MENUFLAG_INTERRUPT: registry. * MENUFLAG_NOMENU: registry. * MENUFLAG_NOOPTION: registry. * MENUFLAG_NOPLAY: registry. * MENUFLAG_RADIO: registry. * menuitem: registry. * MENUNOP: registry. * mousetype: driver. * nthreads: xthreads. * P_FLOAT: driver. * P_NUMBER: driver. * P_STRING: driver. * P_SWITCH: driver. * palette: design. * params: driver. * PIXELSIZE: driver. * print: driver. * processevents: driver. * RANDOM_PALETTE_SIZE: driver. * requirements: filters. * RESIZE_COMMAND: driver. * RESOLUTION: driver. * SCREENSIZE: driver. * set_color: driver. * set_range: driver. * timeemulator: timerlib. * tl_add_timer: timerlib. * tl_create_group: timerlib. * tl_create_timer: timerlib. * tl_elpased: timerlib. * tl_emulate_timer: timerlib. * tl_free_emulator: timerlib. * tl_free_group: timerlib. * tl_free_timer: timerlib. * tl_lookup_timer: timerlib. * tl_process_group: timerlib. * tl_reset_timer: timerlib. * tl_resume_timer: timerlib. * tl_set_handler: timerlib. * tl_set_interval: timerlib. * tl_set_multihandler: timerlib. * tl_sleep: timerlib. * tl_slowdown_timer: timerlib. * tl_stop_timer: timerlib. * tl_unemulate_timer: timerlib. * tl_update_time: timerlib. * TRUECOLOR <1>: filters. * TRUECOLOR: driver. * TRUECOLOR24 <1>: filters. * TRUECOLOR24: driver. * TRUECOLOR32 <1>: filters. * TRUECOLOR32: driver. * UI_C256: driver. * ui_driver: driver. * UI_FIXEDCOLOR: driver. * UI_GRAYSCALE: driver. * UI_TRUECOLOR: driver. * UI_TRUECOLOR24: driver. * UI_TRUECOLOR32: driver. * uninit: driver. * UPDATE_AFTER_RESIZE: driver. * xshl_line: eui. * xth_bgjob: xthreads. * xth_function: xthreads. * xth_init: xthreads. * xth_lock: xthreads. * xth_nthread: xthreads. * xth_sleep: xthreads. * xth_sync: xthreads. * xth_uninit: xthreads. * xth_unlock: xthreads. * xth_wakeall: xthreads. * xth_wakeup: xthreads.  Tag Table: Node: Top501 Node: design1475 Node: driver9067 Node: gui-driver25868 Node: eui32613 Node: ui-helper43320 Node: xthreads47876 Node: filters52923 Node: algorithm69506 Node: timerlib89511 Node: registry99566 Node: index111446  End Tag Table