play
Folders and files
Name | Name | Last commit date | ||
---|---|---|---|---|
parent directory.. | ||||
PLAY = Portability LAYer ------------------------ The purpose of play is to provide a bare bones low-level interface to a variety of windowing environments. The specific targets are UNIX plus the X Window System, MicroSoft Windows (95 or later), and MacOS (back to the mid 8's?). Of course, play must be implemented differently in each case; the idea is that if higher levels are built on play, you only need to rewrite or repair play in order to maintain much more elaborate software across many different platforms. There is no leakage of OS-dependent structures or functions across the play interface. That is, higher level software which includes, say, play.h, need never include any OS-dependent header files. Even more strongly, the play.h header itself is absolutely identical across all platforms -- EVERYTHING which depends on a particular programming environment resides inside the play implementation. While play does not duplicate most ANSI C library functions, it does include a veneer over the ANSI/POSIX <stdio.h> functions. There are several reasons for this: (1) The play "pstdio.h" functions convert all pathnames to UNIX /-delimited form (on non-UNIX systems). (2) The play fread and fwrite functions provide access to the raw unbuffered I/O of the OS (where it exists) for binary files. (3) The play interface provides directory listing functions (inexplicably missing from the ANSI standard). Less crucial than "pstdio.h" and "pstdlib.h" (a memory management interface) and "phash.h" (a hash table interface). These functions are necessary to any play implementation, and are exposed to a play-based program as a convenience. ------------------------------------------------------------------------ pstdlib.h interface ------------------- pstdlib.h supplements the ANSI C stdlib.h interface; it includes <stdlib.h>, so you shouldn't include both -- the size_t type is unfortunately system dependent extern void *(*p_malloc)(size_t); extern void (*p_free)(void *); extern void *(*p_realloc)(void *, size_t); replacements for malloc, free, realloc p_malloc(<=0) allowed, returns non-zero address p_realloc(0, n) allowed, same as p_malloc(n) p_free(0) allowed, no-op all are function pointers to allow code to be linked against another memory manager without recompiling these functions call p_mmfail immediately if the play.h p_signalling semaphore is set extern void *(*p_mmfail)(unsigned long n); set to function pointer to get control if memory manager fails this function can longjmp or return 0 to signal the error to caller n is the size of the requested block, or 0 if failed in p_free extern void p_mminit(void); loads small-block memory manager into p_malloc, p_realloc, p_free this is fast if you allocate and deallocate many small (<=256 bytes) memory blocks, but never frees space used for small blocks (it just reuses the small block space for new small blocks) extern long p_nallocs; total calls to p_malloc or p_realloc(0,n) extern long p_nfrees; total calls to p_free extern long p_nsmall; current number of small blocks extern long p_asmall; current bytes allocated for small blocks extern void *p_memcpy(const void *, size_t); extern char *p_strcpy(const char *); extern char *p_strncat(const char *, const char *, size_t); versions of memcpy, strcpy, and strncat that allocate space for their result using p_malloc NULL source pointers allowed #define P_WKSIZ 2048 typedef union { char c[P_WKSIZ+8]; int i[P_WKSIZ/8]; long l[P_WKSIZ/8]; double d[P_WKSIZ/8]; } p_twkspc; extern p_twkspc p_wkspc; static global workspace, may be used by several play.h functions ------------------------------------------------------------------------ phash.h interface ----------------- typedef unsigned long p_hashkey; #define P_IHASH(x) macro to hash an integer (unsigned long) #define P_PHASH(x) macro to hash an address (void *) the low-level hash function macros produce a p_hashkey value the p_hashkey values are integers whose low order 32 bits are pseudo-randomized in a one-to-one fashion, so that P_IHASH(x)==P_IHASH(y) or P_PHASH(x)==P_PHASH(y) if and only if x==y typedef struct p_hashtab p_hashtab; extern p_hashtab *p_halloc(p_hashkey size); allocates a hash table which initially has space for size entries each table entry is a (p_hashkey, void*) pair the table may subsequently grow as you add elements or contract as you remove them; the table size is always a power of two, so the time to construct a table scales as n*log(n) for n>>size routines that modify a hash table all fail immediately if the play p_signalling semaphore is set extern void p_hfree(p_hashtab *tab, void (*func)(void *)); frees the hash table tab if func is non-0, it is called once for each entry in the table extern int p_hinsert(p_hashtab *tab, p_hashkey hkey, void *value); insert (hkey, value) into tab any existing (hkey, oldvalue) is silently replaced if value==0, any existing entry (hkey, oldvalue) is removed from tab the returned value is 0 unless the memory manager fails to expand the table, in which case 1 returns extern void *p_hfind(p_hashtab *tab, p_hashkey hkey); return value if (hkey, value) is an entry in tab, else return 0 extern void p_hiter(p_hashtab *tab, void (*func)(void *val, p_hashkey key, void *ctx), void *ctx); for each entry (key,val) in tab, call func(val,key,ctx) the order of the entries is random and changes when you add entries Two sets of higher level functions are not used in the impementation of play (shouldn't be part of play!): Higher level functions using a hidden (id,name) hash table: extern p_hashkey p_id(const char *name, int len); extern p_hashkey p_idmake(const char *name, int len); extern p_hashkey p_idstatic(char *name); p_id returns the id corresponding to name, which has at most len characters, unless len==0, in which case name must be 0-terminated returns 0 if and only if name has never been seen before p_idmake creates a new non-0 id if name has not been seen p_idstatic is like p_idmake, except it writes the string pointer name directly into the table, rather than making a copy extern char *p_idname(p_hashkey id); returns the string corresponding to id extern void p_idfree(p_hashkey id); dissociates the id from any string the number of calls to p_idfree must equal the number of calls to p_idmake (or p_idstatic) to actually remove (id,name) from the table Higher level functions using a hidden address hash table: extern void p_setctx(void *ptr, void *context); set up a (ptr,context) association, or, if context==0, delete any prior context for ptr extern void *p_getctx(void *ptr); return the context associated with ptr, or 0 if there is none ------------------------------------------------------------------------ pstdio.h interface ------------------ Note: all filenames are "UNIX style" in which the directory delimiter is /. (For MacOS, / and : are interchanged, so it is possible to access files whose names contain /, just weird.) typedef struct p_file p_file; typedef struct p_dir p_dir; extern p_file *p_fopen(const char *unix_name, const char *mode); extern p_file *p_popen(const char *command, const char *mode); open a file or a pipe, where mode is as in the ANSI fopen or POSIX popen functions, respectively p_popen may fail if the platform does not support pipes p_fopen may return raw non-buffered file if mode includes "b", so that p_fread and p_fwrite are the POSIX read and write instead of the ANSI fread and fwrite in that case extern int p_fclose(p_file *file); close a p_file extern unsigned long p_fsize(p_file *file); return the size of a file in bytes 0 length and non-existence are indistinguishable extern unsigned long p_ftell(p_file *file); extern int p_fseek(p_file *file, unsigned long addr); seek and tell routines extern char *p_fgets(p_file *file, char *buf, int buflen); extern int p_fputs(p_file *file, const char *buf); extern unsigned long p_fread(p_file *file, void *buf, unsigned long nbytes); extern unsigned long p_fwrite(p_file *file, const void *buf, unsigned long nbytes); read and write routines p_fgets and p_fputs must not be called for a binary p_file extern int p_fflush(p_file *file); flush any buffers extern int p_feof(p_file *file); extern int p_ferror(p_file *file); end-of-file and error condition detection p_ferror also does clearerr, so you only get one chance to call it extern int p_remove(const char *unix_name); extern int p_rename(const char *unix_old, const char *unix_new); delete or rename files extern int p_chdir(const char *unix_name); change working directory extern int p_mkdir(const char *unix_name); extern int p_rmdir(const char *unix_name); create or remove a directory (rmdir fails if directory not empty) extern char *p_getcwd(void); return the current working directory result is in temporary space; copy it quickly and do not try to free extern p_dir *p_dopen(const char *unix_name); open directory for listing extern char *p_dnext(p_dir *dir, int *is_dir); return the next file or subdirectory (is_dir says which) "." and ".." do not appear in the list return 0 means no more files (order is unspecified) there is no way to "rewind" the list result is in temporary space; copy it quickly and do not try to free in particular, the next call to p_dnext of p_dclose may clobber it extern int p_dclose(p_dir *dir); close directory ------------------------------------------------------------------------ play.h interface ---------------- extern int on_launch(int argc, char *argv[]); this must be supplied by the application; it is called in lieu of main non-0 return means to abort without entering main event loop 0 return enters main event loop -------event callback setup extern void p_idler(int (*on_idle)(void)); extern void p_stdinit(void (*on_stdin)(char *input_line)); extern void p_quitter(int (*on_quit)(void)); extern void p_handler(void (*on_exception)(int signal, char *errmsg)); extern void p_gui(void (*on_expose)(void *c, int *xy), void (*on_destroy)(void *c), void (*on_resize)(void *c,int w,int h), void (*on_focus)(void *c,int in), void (*on_key)(void *c,int k,int md), void (*on_click)(void *c,int b,int md,int x,int y, unsigned long ms), void (*on_motion)(void *c,int md,int x,int y), void (*on_deselect)(void *c), void (*on_panic)(p_scr *screen)); extern void p_gui_query(void (**on_expose)(void *c, int *xy), void (**on_destroy)(void *c), void (**on_resize)(void *c,int w,int h), void (**on_focus)(void *c,int in), void (**on_key)(void *c,int k,int md), void (**on_click)(void *c,int b,int md,int x,int y, unsigned long ms), void (**on_motion)(void *c,int md,int x,int y), void (**on_deselect)(void *c), void (**on_panic)(p_scr *screen)); the on_launch function should call at least some of these routines to set the callback procedures for all the events which might drive the remainder of program execution if on_launch returns non-0, the program enters an event loop, and calls the specified function whenever such an event arrives, or ignores it if no callback was set each of these setup routines may be called only once, and should be called from on_launch (or maybe the first call to on_idle?) The p_gui function installs callbacks to handle events from the graphical interface. The p_gui_query function gives the addresses of current callbacks (the one previously installed by p_gui). Note that the address(es) of the callback(s) can be NULL if there is no such callback(s). extern void p_set_alarm(double secs, void (*on_alarm)(void *context), void *context); the set_alarm function cal be called many times to schedule callbacks the secs argument is the number of wall seconds (see p_wall_secs) from now, and the context will be passed to the on_alarm function when it is called extern void p_clr_alarm(void (*on_alarm)(void *c), void *context); turns off one or more alarms set by p_set_alarm, so their callbacks will never be called if either on_alarm or context or both is non-zero, it is a wildcard and all alarms (if both are 0) or all matching alarms are cleared -------event callbacks (application defined) int on_idle(void) called whenever no other events are immediately available return non-0 means to check if any events have arrived, but immediately call on_idle again if not return 0 means to block until the next event arrives void on_stdin(char *input_line) input_line has arrived on "stdin" (which may not be the ANSI C stdin) void on_alarm(void *context) an alarm set by p_set_alarm has rung int on_quit(void) the p_quit function was called, or it is impossible for any more events to arrive and on_idle() (if any) has returned 0 the return value is the value of the ANSI C main(), if applicable void on_exception(int signal, char *errmsg) an exception has been raised; the previous callback may have been asynchronously interrupted - signal will be one of the following: #define PSIG_SOFT 1 p_abort() was called #define PSIG_INT 2 SIGINT C-c or other user-generated interrupt #define PSIG_FPE 3 SIGFPE floating point exception #define PSIG_SEGV 4 SIGSEGV segmentation violation (bad memory access) #define PSIG_ILL 5 SIGILL illegal machine instruction #define PSIG_BUS 6 SIGBUS bad data alignment #define PSIG_IO 7 SIGIO I/O exception #define PSIG_OTHER 8 the errmsg may or may not contain any useful information extern void p_abort(void); do not return to caller -- next "event" will be on_exception normal event processing continues after on_exception returns call p_abort() quickly if you check p_signalling and find it non-0 extern volatile int p_signalling; semaphore used to signal that p_abort() should be called as soon as possible -- if p_signalling is not set, you may set it to indicate an error condition The remaining events are all associated with a GUI window created using p_window or p_menu. Their first argument is the context supplied to the p_window or p_menu call that created the window "where the event takes place". void on_expose(void *c, int *xy) you need to draw or redraw this window if xy!=0, you may elect to redraw only the rectangle with opposite corners (xy[0],xy[1]) and (xy[2],xy[3]) you must not draw on a window before its first on_expose void on_destroy(void *c) the window has been destroyed p_destroy may or may not cause this callback void on_resize(void *c, int w, int h) the window has been resized p_resize may or may not cause this callback void on_focus(void *c, int in) this window's "focus" has changed (in&1)!=0 means the window now has "focus", else it has lost "focus" if (in&2)!=0 then "focus" simply refers to whether the mouse is inside the window or not -- this event will only be delivered when mouse motion events cease to be delivered to a window when the mouse leaves it (if the motion events are delivered elsewhere when the mouse is not in the window, then there is no way to tell when it has left) if (in&2)==0 then "focus" refers to whether on_key events will be delivered to this window void on_key(void *c, int k, int md) the key k has been pressed, and the current modifier (shift key) state is given by md (see on_click) k is an ASCII character or one of these constants: P_LEFT 0x0100 P_F0 0x0200 P_F9 0x0209 P_RIGHT 0x0101 P_F1 0x0201 P_F10 0x020a P_UP 0x0102 P_F2 0x0202 P_F11 0x020b P_DOWN 0x0103 P_F3 0x0203 P_F12 0x020c P_PGUP 0x0104 P_F4 0x0204 P_PGDN 0x0105 P_F5 0x0205 P_HOME 0x0106 P_F6 0x0206 P_END 0x0107 P_F7 0x0207 P_INSERT 0x0108 P_F8 0x0208 play does not provide a "key released" event, and the precise time the on_key event is delivered is unspecified -- pressing shift keys alone does not generate on_key (should this be reversed to allow for cursor changes when a shift key is pressed?) void on_click(void *c, int b, int md, int x, int y, unsigned long ms) the mouse button number b (1, 2, 3, 4, or 5, where 1 2 3 are left middle right respectively) has been pressed or released and the modifier (shift key and mouse button) state just before this event was given by oring the following bits: P_BTN1 000010 P_SHIFT 000400 P_BTN2 000020 P_CONTROL 001000 P_BTN3 000040 P_META 002000 P_BTN4 000100 P_ALT 004000 P_BTN5 000200 P_COMPOSE 010000 P_KEYPAD 020000 the click occurred at coordinates (x,y) in this window at time ms (the time can only be used relative to other on_click events to determine whether a double or triple click has occurred) on_click events are always delivered in pairs (press and release) to the same window, unless a call to p_qclear intervenes void on_motion(void *c,int md,int x,int y) the mouse has moved to position (x,y) in this window, and the modifier (shift key and mouse button) state is given by md (see on_click) void on_deselect(void *c) this window was the selection/clipboard owner (called p_scopy) and another window has taken the selection/clipboard void on_panic(p_scr *screen) an I/O error occurred on this screen; you should call p_destroy for every window on the screen -------general utility functions extern void p_quit(void); the next and final callback will be on_quit extern void p_qclear(void); clear any pending events off the event queue (most likely called in on_exception) extern void p_stdout(char *output_line); extern void p_stderr(char *output_line); print output_line on "stdout" or "stderr" must have called p_stdinit first extern double p_wall_secs(void); return number of seconds of wall time since this program started extern double p_cpu_secs(double *sys); return number of seconds of cpu and system time used by this program at least millisecond resolution if possible extern char *p_getenv(const char *name); extern char *p_getuser(void); return named environment variable or current user "login" name returned string is in temporary space, copy it quickly -------graphical user interface (GUI) utilities typedef struct p_scr p_scr; a screen (+ keyboard and mouse) typedef struct p_win p_win; a window, menu, pixmap, or metafile typedef unsigned long p_col_t; a color note that p_scr and p_win are opaque extern p_scr *p_connect(char *server_name); connect to server_name (e.g.- an X DISPLAY), or to the default screen if server_name==0 extern void p_disconnect(p_scr *screen); disconnect from the screen (destroy any windows there first) extern p_scr *p_multihead(p_scr *other_screen, int number); connect to a second (or third or fourth) screen associated with the same keyboard and mouse as other_screen extern int p_sshape(p_scr *s, int *width, int *height); return the width and height of the screen in pixels, return value is the depth in bits extern void p_winloc(p_win *w, int *x, int *y); return the screen coordinates of the upper left corner of the window extern int p_txheight(p_scr *s, int font, int pixsize, int *baseline); get the actual height of font in pixels on the screen (should be close to the specified size pixsize) also get the baseline -- number of pixels down from the capline to the coordinate you specify to p_text extern int p_txwidth(p_scr *s, const char *text, int n, int font, int pixsize); get the actual width in pixels of the first n characters of text rendered on scr with font at specified size pixsize /* screen graphics window and pixmap management */ extern p_win *p_window(p_scr *s, int width, int height, char *title, p_col_t bg, int hints, void *ctx); create a new window with the specified dimensions, title, background color, hints and context ctx ctx will be passed back to the event handlers for events occurring in this window the hints may or may not do anything; they are gotten by oring: #define P_NOKEY 0x02 do not deliver on_click #define P_NOMOTION 0x04 do not deliver on_motion #define P_NORESIZE 0x08 do not allow resizing #define P_DIALOG 0x10 decorate this window as a dialog box #define P_MODAL 0x20 do not deliver events to any other window until this one is destroyed #define P_PRIVMAP 0x01 use a private colormap for this window #define P_RGBMODEL 0x40 use a 5x9x5 color cube for this window P_PRIVMAP and P_RGBMODEL are no-ops unless the screen uses 8-bit color extern p_win *p_menu(p_scr *s, int width, int height, int x, int y, p_col_t bg, void *ctx); create a new menu at screen coordinates (x,y) mouse events should only be delivered to a menu if any menus currently exist p_destroy pops down the menu extern p_win *p_offscreen(p_win *parent, int width, int height); create a new offscreen pixmap, which can p_bitblt onto the ordinary window parent (made by p_window) extern p_win *p_metafile(p_win *parent, char *filename, int x0, int y0, int width, int height, int hints); create a new metafile which can contain the contents of the ordinary window parent (made by p_window) after p_metafile is called, all graphics calls will be to its p_win until p_destroy closes the metafile p_metafile may return 0 if the system does not define metafiles extern void p_destroy(p_win *w); destroy the window or pixmap, pop down the menu, or close the metafile extern int p_scopy(p_win *w, char *string, int n); extern char *p_spaste(p_win *w); copy to the selection/clipboard or paste from it (p_spaste needs the window to know which screen) extern void p_feep(p_win *w); ding the keyboard's bell extern void p_flush(p_win *w); flush any pending drawing calls so what the window shows reflects all graphics commands to this point this is automatic whenever the program goes idle (before on_idle) extern void p_clear(p_win *w); erase the window to its background color extern void p_resize(p_win *w, int width, int height); change the size of the window extern void p_raise(p_win *w); raise the window to the top of the stacking order (there is no lower function -- let the window manager do that) extern void p_cursor(p_win *w, int cursor); change the cursor when the mouse is in this window cursor can be one of: P_SELECT 0 P_N 3 P_CROSSHAIR 1 P_S 4 P_TEXT 2 P_E 5 P_ROTATE 10 P_W 6 P_DEATH 11 P_NS 7 P_HAND 12 P_EW 8 P_NONE 13 P_NSEW 9 extern void p_clip(p_win *w, int x0, int y0, int x1, int y1); set the clipping to the rectangle from (x0,y0) to (x1,y1) subsequent drawing commands will have no effect outside this rectangle extern void p_palette(p_win *w, p_col_t *colors, int n); change the palette (see p_color) for the window -------graphical output functions /* screen graphics drawing functions */ extern void p_text(p_win *w, int x0, int y0, const char *text, int n); draw first n characters of text at (x0,y0) in current font and color (x0,y0) is the point on the baseline at the beginning of the first character extern void p_rect(p_win *w, int x0, int y0, int x1, int y1, int border); extern void p_ellipse(p_win *w, int x0, int y0, int x1, int y1, int border); draw a rectangle (or an ellipse inscribed in the rectangle) with opposite corners (x0,y0) and (x1,y1) if border is non-0, draw a filled figure using the current color else draw just the outline with the current color and pen for a filled rectangle, the lower (y=y1) and right (x=x1) edges are not drawn; for an outlined rectangle, all four edges are actually drawn, and no points outside the specified rectangle are drawn (that is, the pen width is all interior to the specified corners) extern void p_dots(p_win *w); draw the current point list as dots in the current color extern void p_segments(p_win *w); draw consecutive pairs of points in the current point list as line segments using the current color and pen the segment endcaps are square extern void p_lines(p_win *w); draw the current point list as a connected polyline using the current color and pen line endpoints and joins are round extern void p_fill(p_win *w, int convexity); fill the polygon defined by the current point list with the current color extern void p_ndx_cell(p_win *w, unsigned char *ndxs, int ncols, int nrows, int x0, int y0, int x1, int y1); extern void p_rgb_cell(p_win *w, unsigned char *rgbs, int ncols, int nrows, int x0, int y0, int x1, int y1); draw the image ndxs or rgbs in the rectangle (x0,y0) to (x1,y1) in the window, stretching or compressing it if ncols!=x1-x0 or nrows!=y1-y0 the bottom y=y1 and right x=x1 edges are not part of the image extern void p_bitblt(p_win *w, int x, int y, p_win *offscreen, int x0, int y0, int x1, int y1); copy the rectangle (x0,y0) to (x1,y1) in the offscreen pixmap onto the window with the upper left corner at (x,y) in the window extern void p_pen(p_win *w, int width, int type); set the current pen this must be called not only when the pen changes, but also after any graphics call to any other window has been made possible pens are: P_SOLID 0 P_DOT 2 P_DASHDOTDOT 4 P_DASH 1 P_DASHDOT 3 P_SQUARE 8 extern void p_font(p_win *w, int font, int pixsize, int orient); set the current font this must be called not only when the font changes, but also after any graphics call to any other window has been made possible fonts are: P_COURIER 0 P_HELVETICA 8 P_NEWCENTURY 16 P_TIMES 4 P_SYMBOL 12 P_GUI_FONT 20 these may be ored together with: P_BOLD 1 P_ITALIC 2 extern void p_color(p_win *w, p_col_t color); set the current color this must be called not only when the color changes, but also after any graphics call to any other window has been made there are two distinct types of colors: indexed (<=255) and rgb (bit 0x01000000 set, r in lsb, then g, then b) additionally, within the indexed colors, there are 16 reserved colors; the p_palette function sets the rgb values for the other 240 indexed colors (any colors not set by p_palette are P_FG) the following macros are useful: P_IS_NDX(color) true if color is indexed P_IS_RGB(color) true if color is rgb P_R(color), P_G(color), P_B(color) r, g, and b components of an rgb color (0 black, 255 maximum) P_RGB(r,g,b) an rgb color the reserved indexed colors are: P_BG 255UL P_RED 251UL P_GUI_BG 245UL P_FG 254UL P_GREEN 250UL P_GUI_FG 244UL P_BLACK 253UL P_BLUE 249UL P_GUI_HI 243UL P_WHITE 252UL P_CYAN 248UL P_GUI_LO 242UL P_MAGENTA 247UL P_XOR 241UL P_YELLOW 246UL P_EXTRA 240UL (bg and fg are "background" and "foreground", which the user may be able to define differently on different screens or workspaces) extern void p_i_pnts(p_win *w, const int *x, const int *y, int n); extern void p_d_pnts(p_win *w, const double *x, const double *y, int n); set point list for p_dots, p_lines, p_fill, and p_segments (p_segments uses consecutive pairs in the list) if n>=0 creates a new list of points if n<0 appends -n points to the current list total number of points (after all appends) must be <=2048 extern void p_d_map(p_win *w, double xt[], double yt[], int set); sets the coordinate transform for p_d_pnts: input window pixel coordinates (int part) (x,y) --> (xt[0]*x+xt[1], yt[0]*y+yt[1]) extern void p_rgb_read(p_win *w, unsigned char *rgbs, int x0, int y0, int x1, int y1); return 3-by-(x1-x0)-by-(y1-y0) rgb triples corresponding to the current window contents in the specified rectangle if the window is occluded or iconified, this may not work properly always works properly if w is an offscreen pixmap w may not be a menu or a metafile -------miscellaneous utilities extern unsigned char p_bit_rev[256]; 0, 1, 2, 3, ..., 255 in bit-reversed order extern void p_lrot180(unsigned char *from, unsigned char *to, int fcols, int frows); extern void p_lrot090(unsigned char *from, unsigned char *to, int fcols, int frows); extern void p_lrot270(unsigned char *from, unsigned char *to, int fcols, int frows); least significant bit first versions of bitmap rotation functions extern void p_mrot180(unsigned char *from, unsigned char *to, int fcols, int frows); extern void p_mrot090(unsigned char *from, unsigned char *to, int fcols, int frows); extern void p_mrot270(unsigned char *from, unsigned char *to, int fcols, int frows); most significant bit first versions of bitmap rotation functions all these assume the rows begin on even byte addresses extern p_col_t p_595[225]; a 225 color 5x9x5 color cube for p_palette(w,p_595,225) -------internal functions (for play implementation) /* idle and alarm "events" */ extern void p_on_idle(void); extern double p_timeout(void); the p_idler, p_set_alarm, and p_clr_alarm have generic implementations the play implementation should call p_timeout just before it might block waiting for events -- if p_timeout returns <0, then the implementation should block forever waiting for the next event otherwise, it should wait at most p_timeout() seconds for an event to arrive -- if a timeout occurs before the next event, the implementation should behave as if an event had arrived and had been serviced the p_timeout return value is computed using the p_wall_secs timer after all pending events have been serviced (or a timeout has occurred), the play implementation must call p_on_idle (which will take care of calling the user's on_idle) -- the only thing that prevents an infinte loop in this scheme is the fact that p_timeout() will eventually return <0 ------------------------------------------------------------------------ phash.h implementation notes ---------------------------- The hashing algorithm is as follows: unsigned long p_hmasks[64]= { 0xb88f7f5e,0x9a5f430b,0x9ffc4579,0xf24f8239,0xa3ee4362,0x11f23e15, 0x79b365f0,0xdaa01682,0xfc32732b,0x5002d914,0xb91f0ad5,0xf62c0bd1, 0x0586bd83,0x6186c8ef,0xa422d1d0,0x94acf08b,0xd1618ed2,0xaff8c327, 0x8c65192f,0xa0fc60d0,0xca45848b,0xdb8c5251,0x4aa83d9d,0x2ab5bc8d, 0x8ef3321a,0x0da260f8,0x68aef4ad,0x2ea75120,0x5b00c5ef,0x4180ea63, 0xd8a2dad6,0x00d0ee07,0xbe260469,0x3bf21367,0x94299569,0xf517d7e0, 0x7c3f07ec,0x41da712a,0x4e73cabb,0x6388ae9e,0x248d894b,0x389f2cb7, 0x8504641e,0xb53898a9,0x071d8a73,0xeba24361,0x0bd1fe87,0xda1ff034, 0x29f5f9e2,0x3ce61746,0x38ab5382,0x8117f9b2,0xa8256e6d,0x161674bd, 0xbe111537,0x6cce6b6a,0x290ecf4f,0x1c47b104,0x37bd96bc,0x80f39033, 0x0c1b6161,0x70a94f9d,0xb90e1369,0xcbc2f924 }; P_IHASH(x) or P_PHASH(x) <== x ^ p_hmasks[ (x>>4)&0x3f ] The p_hmasks have been checked to assure that none is a cyclic shift of the bits of any other (you can xor any one with any shifted version of itself or any other without getting 0). This property may aid in combining several of the masks. The hash function works because the p_hmask have been cunningly chosen with the property that bits 2^4-2^9 are the 64 numbers: 53,48,23,35,54,33,31,40,50,17,45,61,24,14,29, 8, 45,50,18,13, 8,37,25, 8,33,15,10,18,30,38,45,32, 6,54,22,62,62,18,43,41,20,11, 1,10,39,54,40, 3, 30,52,56,27,38,11,19,54,52,16,43, 3,22,57,54,18 which in turn have the propery that when they are xor'ed with the indices 0-63, they produce the following pseudo-random permutation of the numbers 0-63: 53,49,21,32,50,36,25,47,58,24,39,54,20, 3,19, 7, 61,35, 0,30,28,48,15,31,57,22,16, 9, 2,59,51,63, 38,23,52,29,26,55,13,14,60,34,43,33,11,27, 6,44, 46, 5,10,40,18,62,37, 1,12,41,17,56,42, 4, 8,45 The inverse of this permutation is: 18,55,28,13,61,49,46,15,62,27,50,44,56,38,39,22, 26,58,52,14,12, 2,25,33, 9, 6,36,45,20,35,19,23, 3,43,41,17, 5,54,32,10,51,57,60,42,47,63,48, 7, 21, 1, 4,30,34, 0,11,37,59,24, 8,29,40,16,53,31 If this last array is called inv_mask[64], then the inverse of the P_IHASH or P_PHASH function is: x <== y ^ p_hmasks[ inv_mask[(y>>4)&0x3f] ] where y==P_IHASH(x) The existence of this inverse proves that P_IHASH values can never collide. The basic hash table presumes that the hash function is perfect; that is, hash keys are unique. This is exactly true for integers or pointers, so the basic hash functions can be used directly for that case. For strings and other larger objects, it is impossible to produce a hash function with no collisions. In those cases, you need to rehash, and you also need to "lock" any hashkeys which caused a rehash, so that you never remove them from the basic hash table (but you can free most of their value if they use significant space). The basic table contains a number of slots that is a power of two. The hash key is masked to that number of bits and used as an index into the slot array. The hash entries are kept as (very short) linked lists starting from each element of the slot array; they are checked in turn for an exact match of the unmasked hashkey, which is saved along with the value when an item is inserted into the table. When the number of items stored in the table becomes larger than half the number of slots, the number of slots is doubled. That unmasks one more bit of every hashkey; items with that bit set are moved to the new upper half of the slot array, while items with that bit clear remain in the lower half. Hence, the ideal hash function is one that produces a random sequence of bits as you increment through the items being hashed. The id routines provide a single global registry of name strings, which you can use the way X11 uses its Atom type, hugely speeding up subsequent string comparisons. The id number 0 is reserved; it will never be returned, except to indicate that a name has not been seen. The hashtab holding the names is not directly visible. The ctx routines provide a context mechanism for associating opaque pointers with higher level data structures. This is based on a single, hidden, hashtab as well. It is useful for situations in which you can neither use backpointers nor derived classes (a form of backpointer).