1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797
|
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));
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?)
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).
|