ganek@apollo.UUCP (11/09/87)
The following C code is from the SIGGRAPH '87 course #23 notes. Course #23 was titled "Introduction to Window Management". The code is set of routines that can be used to build a simple window system and some simple applications. They were used to demonstrate some basic windowing principles. I had promised the student that I would distribute them electronically. (Better late than never! :-) Any suggestions, improvements or bugs reports would be welcomed and may be applied to future courses. Daniel E. Ganek CHM-02-RD Apollo Computer Co. 220 Mill Rd Chelmsford, MA 01824 (617)-256-6600 x8543 ganek@apollo.UUCP /****start sws.h********/ /* sws.h - * Include file for the Simple Window System * This is used by clients (applications) * * Author: Daniel E. Ganek * SIGGRAPH '87 */ #include <sys/types.h> #include <sys/time.h> /* window system error codes*/ #define sws_no_such_window -1 /* bad wid */ #define sws_not_connected -2 /* window_system_connect not called */ #define sws_too_many_displays -3 /* can pen another dislay */ #define sws_bad_display_name -4 /* display name unknown */ #define sws_bad_size -5 /* zero or negative size window */ #define sws_bad_background_op -6 /* invalid background operation */ #define sws_bad_queue -7 /* bad qid id */ /* define background and foreground "colors" */ #define BACKGROUND 0 #define FOREGROUND 1 /* * Define some window related types: point, area, window_info */ typedef struct { short int x; /* a point = (x,y) */ short int y; } point; typedef struct { point origin; /* an area = origin, size */ point size; } area; /* visibility information block */ typedef struct { area warea; /* window area */ area *vis_list; /* ptr to visiblity list */ } vis_info; /* * Input Types * This is very simple event record. In the real world we'd have a device ID and quite an extensive * set of data types - here everything is encapsulated into an single 8-bit byte */ typedef struct { struct timeval tmstamp; /* event time */ long window; /* window id */ point position; /* position of locator */ unsigned char code; /* event data/code */ /* codes 0-127 are ASCII */ /* codes 128-255 are other */ } input_event; /* * Event Codes * Codes 0-127 are the standard ASCII events * use codes 128-255 for our special stuff */ #define LAST_ASCII 0x7F /* last valid ascii code */ #define REFRESH 0x80 /* refresh window needed */ #define ENTER 0x82 /* locator entered window */ #define EXIT 0x83 /* locator left window */ #define LOCATOR 0x91 /* locator event */ /* mouse buttons down and up*/ #define LEFT 0xA0 /* left mouse button down */ #define LEFT_UP 0xA1 /* left mouse button up */ #define MIDDLE 0xA2 /* middle mouse button down*/ #define MIDDLE_UP 0xA3 /* middle mouse button up */ #define RIGHT 0xA4 /* right mouse button down */ #define RIGHT_UP 0xA5 /* right mouse button up */ /* 10 function keys f0-f9 down and up */ #define F0 0xB0 #define F0_UP 0xB1 #define F1 0xB2 #define F1_UP 0xB3 #define F2 0xB4 #define F2_UP 0xB5 #define F3 0xB6 #define F3_UP 0xB7 #define F4 0xB8 #define F4_UP 0xB9 #define F5 0xBA #define F5_UP 0xBB #define F6 0xBC #define F6_UP 0xBD #define F7 0xBE #define F7_UP 0xBF #define F8 0xC0 #define F8_UP 0xC1 #define F9 0xC2 #define F9_UP 0xC3 /* * Input Selection Mask * The input selection mask is also greatly simplified. We've * collapsed everything into a small number of bits. Again in * the real world we have to select on a per device and per * code basis, e.g. we'd be able to select any specific ASCII * character. */ typedef long event_mask; /* system events */ #define REFRESH_MASK 1<<0 /* window refresh needed */ #define ENTER_MASK 1<<2 /* locator entered window */ #define EXIT_MASK 1<<3 /* locator left window */ #define LOCATOR_MASK 1<<4 /* locator event */ #define KBD_MASK 1<<5 /* kbd (ASCII) events */ #define LEFT_MASK 1<<6 /* left mouse button down */ #define LEFT_UP_MASK 1<<7 /* left mouse button up */ #define MIDDLE_MASK 1<<8 /* middle mouse button down*/ #define MIDDLE_UP_MASK 1<<9 /* middle mouse button up */ #define RIGHT_MASK 1<<10 /* right mouse button down */ #define RIGHT_UP_MASK 1<<11 /* right mouse button up */ /* 10 Functions f0-f9 down and up */ #define F0_MASK 1<<12 #define F0_UP_MASK 1<<13 #define F1_MASK 1<<14 #define F1_UP_MASK 1<<15 #define F2_MASK 1<<16 #define F2_UP_MASK 1<<17 #define F3_MASK 1<<18 #define F3_UP_MASK 1<<19 #define F4_MASK 1<<20 #define F4_UP_MASK 1<<21 #define F5_MASK 1<<22 #define F5_UP_MASK 1<<23 #define F6_MASK 1<<24 #define F6_UP_MASK 1<<25 #define F7_MASK 1<<26 #define F7_UP_MASK 1<<27 #define F8_MASK 1<<28 #define F8_UP_MASK 1<<29 #define F9_MASK 1<<30 #define F9_UP_MASK 1<<31 #define ALL_EVENTS -1 /* window system functions*/ extern long window_system_connect(); /* connect to a display system */ extern long window_create(); /* create a new window */ extern long window_close(); /* close a window */ extern long window_pop(); /* pop(raise) a window */ extern long window_change(); /* change origin or size */ extern long window_wid(); /* return wid of containing point */ extern long window_information(); /* return window info */ /* input functions */ extern long input_connect(); /* connect to the input system */ extern long input_wait(); /* wait for an input event */ extern long input_check(); /* check for input */ extern long input_select(); /* declare event interests */ extern long input_process(); /* process an input event */ /****end sws.h********/ /****start sws_env.h****/ /* sws_env.h * System dependent include file * for the Simple Window System * * Author: Daniel E. Ganek * SIGGRAPH '87 */ #define TRUE 1 #define FALSE 0 #include "sws.h" /* * Internal Definitions * These are used by the SWS system independent module (sws.c) * and the system dependent module. */ /* * Input event queue - a simple ring buffer with and additional system * dependent piece to handle synchronization. */ #define BUFFER_SIZE 16 /* size of input buffer */ #define Q_SYS_SIZE 2 /* size of system specific stuff */ typedef struct { int in; /* ring buffer in index */ int out; /* wout index */ input_event buffer[BUFFER_SIZE]; /*buffer */ long system[Q_SYS_SIZE]; /*system specific data */ } event_queue; /* system routine to handle synchronization */ extern void system_init_queue(); /* init system specific stuff in queue structure */ extern void system_adv_queue(); /* wake up any waiters on the queue */ extern void system_wait_queue(); /* wait for an event to appear on the queue */ /* * Event Routing Block * A list of these hangs off each window * They are used to determine to which queue events are to be routed. */ typedef struct route_desc { struct route_desc *next; /* forward */ struct route_desc *prev; /* back ptrs */ event_mask mask; /* mask definig which event to route */ event_queue *queue; /* queue to route them t */ } route_block; /* * Window Input Information * This record contains info relating to input. */ typedef struct { route_block *erb; /* event routing block */ } input_info; /* * Window Information * This record contains all info relating to a window, output and input. */ typedef struct win_rec { struct win_rec *next; /* forward ptr */ struct win_rec *prev; /* back ptrs */ vis_info visinfo; /* area and visiblity info*/ input_info inputinfo; /* input info */ } window_rec; /* * This record contains all the info relating to a display * A display consists of all a screen with windows and input devices. */ typedef struct { area screen; /* origin and size of screen */ window_rec *windows; /* window list */ long back_window; /* wid of background window */ route_block *grab_list; /* grab list */ long locator_wid; /* window containing locator */ point locator_pos; /* abs position of locator */ long *system; /* system info */ } display_rec; /* * Procedure to get display record from system */ extern display_rec *system_get_display(); /* * System specific versions of malloc, realloc, and free. * In a shared memory environment these work out of shared memory */ extern char *sws_malloc(); extern char *sws_realloc(); extern void sws_free(); /**** end sws_env.h****/ /****start clock.c******/ /* * clock.c * * Author: Daniel E. Ganek * SIGGRAPH '87 * * Clock program for use with the Simple Window Manager * Draws a clock face once every n seconds. * */ #include <stdio.h> /* some standard stuff */ #include <math.h> #include "sws.h" /* window system definitions */ #define N_seconds 2 /* redisplay clock every N_seconds */ #define TRUE 1 #define FALSE 0 #define TWOPI 6.28318 void DispClock(); /* routine to actually draw the clock */ main() { long bid; /* background ID */ long wid; /* our window ID */ vis_info w_info; /* size of window, etc. */ point center; /* where to center clock */ short radius; /* radius of clock proper */ long status; /* internal status - if false, exit */ bid = window_system_connect(0L); /* connect to the wid system */ if (bid <= NULL) /* oops problems */ return(bid); /* First create a window for the clock */ w_info.origin.x = 25; w_info.origin.y = 25; w_info.size.x = 50; w_info.size.y = 50; wid = window_create(w_info.origin,w_info.size); status = (wid > 0); /* set status */ /* * Once every N_second, redraw the clock. * Get window size, * Clear window, * Calculate clock position * center it in the window, use the smaller * of x and y dimensions to determine radius * Draw it */ /* To exit program delete the clock window */ while (status == TRUE) { if ((status = (draw_lock(wid)) > 0)) { if ((status = (window_information(wid, &w_info)) > 0) ) { draw_clear(wid,BACKGROUND); center.x = (w_info.size.x - 1)/2; center.y = (w_info.size.y - 1)/2; radius = w_info.size.x < w_info.size.y ? (.9*w_info.size.x/2) : (.9*w_info.size.y/2); DispClock(wid, center, radius); } draw_unlock(wid); } sleep(N_seconds); } return (status); } /* * This routine displays the image of a clock. * The image consists of a large circle with small * circles at each hour. The time is indicated by * hour and minute hands which are simple lines. */ void DispClock(wid, center, radius) short radius; long wid; point center; { int i; point c; long tim; struct tm t; float f_hour; /* Cursory error check*/ if (radius == 0) return; /* Draw the outlining circle */ draw_circle(wid, center, radius); /* Draw the hour tics */ for (i = 0; i < 12; i++) { c.x = center.x + .9*radius*sin(i*TWOPI/12) + .5; c.y = center.y + .9*radius*cos(i*TWOPI/12) + .5; draw_circle(wid,c,(short int)(.05*radius)); } /* Get the time and draw hands */ tim = time(NULL); t = *localtime(&tim); /* Draw minute hand */ c.x = center.x + .7*radius*sin(t.tm_min*TWOPI/60) + .5; c.y = center.y - .7*radius*cos(t.tm_min*TWOPI/60) + .5; draw_line(wid, center, c); /* Draw hour hand */ f_hour = (t.tm_hour + t.tm_min/60.)*TWOPI/12; c.x = center.x + .5*radius*sin(f_hour) + .5; c.y = center.y - .5*radius*cos(f_hour) + .5; draw_line(wid, center, c); return; } /**** end clock.c******/ /****start swm.c********/ /* * swm.c * * Author: Daniel E. Ganek * SIGGRAPH '87 * * Simple Window Manger for the Simple Window System * * This is what we refer to as a "minimal" window manager * It does the following: * grow window * move window * pop window * close window * refresh background window * * It demostrates some of the basic functions * input * refresh(exposure events) * grabbing events * */ #include <stdio.h> #include "sws.h" #define TRUE 1 #define FALSE 0 void process_event(); static long bid; /* background wid */ static long qid; /* our queue id */ static event_mask grab; /* default grab mask */ static input_event event; /* current input event */ main() { vis_info w_info; long wid; /* window to draw in */ /* Connect us to SWS and get the background ID */ if ((bid = window_system_connect(0L)) <= NULL) return (bid); qid = input_connect(bid); /* * We want to grab some function keys for window manager stuff, * i.e. POP, CLOSE, MOVE AND GROW * * We also want refresh events for the backgorund */ grab = F0_MASK | F0_UP_MASK | F1_MASK | F1_UP_MASK | F2_MASK | F2_UP_MASK | F3_MASK | F3_UP_MASK; input_select(0, qid, grab); input_select(bid, qid, REFRESH_MASK); /* just loop waiting for an event we asked for */ /* and then process it. */ while (TRUE) { input_wait(qid,&event); process_event(&event); } exit(0); } /* * a simple routine to xor a box */ void box(wid, barea) long wid; /* window to draw in */ area barea; /* box area */ { point p0; point p1; draw_lock(wid); draw_clip(bid,FALSE); p0 = barea.origin; p1.x = barea.origin.x + barea.size.x - 1; p1.y = p0.y; draw_xor(wid, p0, p1); /* top */ p0.x = p1.x; p0.y = p1.y + 1; p1.y = barea.origin.y + barea.size.y - 1; draw_xor(wid, p0, p1); /* right */ p0.x = p1.x - 1; p0.y = p1.y; p1.x = barea.origin.x; draw_xor(wid, p0, p1); /* bottom */ p0.x = p1.x; p0.y = p1.y - 1; p1.y = barea.origin.y + 1; draw_xor(wid, p0, p1); /* left */ draw_clip(bid, TRUE); /* turn clipping back on*/ draw_unlock(wid); return; } /* * move_window * * This routine moves a window. It grabs the locator * and follows it with a box. It stops when F2 is let up. * */ void move_window(event) input_event *event; { long wid; /* window to move */ vis_info winfo; wid = window_wid(event->position); if (wid == bid) /* can't move background */ return; input_select(0, qid, ALL_EVENTS); /* grab everything */ window_information(wid, &winfo); /* get current origin and size */ winfo.origin = event->position; /* start postion */ box(bid, winfo); /* draw a box */ while (TRUE) { /* look for locs and F2 up */ input_wait(qid, event); /* wait for next event */ if (event->code == F2_UP) { /* F2_up */ box(bid, winfo); /* erase old box */ break; /* all done! */ } if (event->code == LOCATOR) { box(bid, winfo); /* erase old box */ winfo.origin = event->position; /* move the box */ box(bid, winfo); /* redraw box */ } } draw_lock(wid); window_change(wid, winfo); /* move it */ window_pop(wid); /* pop it */ draw_unlock(wid); input_select(0, qid, grab); /* back to old grab */ return; } /* * grow_window * * This routine grows a window. It grabs the locator * and follows it with the lower right hand corner of a box. * It stops when F3 is let up. * */ long grow_window(event) input_event *event; { long wid; /* window to grow */ vis_info winfo; point new_size; char box_drawn; wid = window_wid(event->position); if (wid == bid) /* can't grow background */ return; input_select(0, qid, ALL_EVENTS); /* grab everything */ window_information(wid, &winfo); /* get current origin and size */ box_drawn = FALSE; /* no box yet */ while (TRUE) { /* look for locs and F3 up */ if ((event->code == LOCATOR) || (event->code == F3)) { /* calc new size */ new_size.x = event->position.x - winfo.origin.x; new_size.y = event->position.y - winfo.origin.y; /* ignore "negative" and small boxes */ if ((new_size.x > 5) && (new_size.y > 5)) { if (box_drawn) box(bid, winfo); /* erase old box */ winfo.size = new_size; /* new box size */ box(bid, winfo); /* draw it */ box_drawn = TRUE; } } input_wait(qid, event); /* wait for next event */ if (event->code == F3_UP) { /* F3_up */ if (box_drawn) box(bid, winfo); /* erase old box */ break; } } if ((new_size.x > 5) && (new_size.y > 5)) { /* ignore "negative" boxes */ draw_lock(wid); window_change(wid, winfo); /* new size */ window_pop(wid); /* pop it */ draw_unlock(wid); } else box_drawn = FALSE; input_select(0, qid, grab); /* back to old grab */ return (box_drawn); } /* * process_event * * This routine processes the events we receive. * by calling the appropriate routine. * Since most routines modify someone elses * window, i.e. move it or grow, etc. we * fill it in with the background value until * the other guy gets around to fixing it up. * * We "own" the background; so we're looking for * refresh events for it. * */ void process_event(event) input_event *event; { long wid; /* window */ wid = 0; switch (event->code) { case F0: wid = window_wid(event->position); if (window_pop(wid) <= 0) wid = 0; break; case F1: wid = window_wid(event->position); window_close(wid); wid = 0; break; case F2: wid = window_wid(event->position); move_window(event); break; case F3: wid = window_wid(event->position); if (grow_window(event) <= 0) wid = 0; break; case REFRESH: if (event->window == bid) wid = bid; /* refresh background */ } if (wid) { draw_lock(wid); if (wid != bid) draw_clear(wid, BACKGROUND); else draw_clear(wid, FOREGROUND); /* The "background" color for the bid */ /* is the writing color of other windows */ draw_unlock(wid); } return; } /**** end swm.c********/ /****start sws.c********/ /* * sws.c * Simple Window System * * Author: Daniel E. Ganek * SIGGRAPH '87 * * This is a simple (almost trivial) window system * There is no window hiearchy; but it does support occluding windows. * Input is limited to ASCII keystroke, mouse motion, * mouse buttons, and 10 function keys. * * Though simple, this system demostrates many of the functions * that must exists in a window system and problems associated * with them. The methods and algorithms included here are * not necessarily the most efficient or desirable. As a matter * of fact, things have been kept simple so as not to confuse * the beginner. NUMEROUS optimizations would be made in a real system. */ #include <stdio.h> #include "sws_env.h" #define max(a, b) ((a) < (b) ? (b) : (a)) /* std max and min */ #define min(a, b) ((a) > (b) ? (b) : (a)) /* * static declarations */ static display_rec *display = NULL; /* pointer to the display record */ static int is_locked; /* did I already lock the db? */ static area null_area = { 0, 0, 0, 0 }; /* handy for zeroing things */ /* * forward declarations */ window_rec *check_window(); void calc_vis_list(); /*****************************************************************************************/ /* * window_lock * window_unlock * * These routines are used to set and release the mutex lock on the * window database. This is necessary in shared memory environment * since we don't want two or more client simultaneously modifying the * database! We rely on system_lock() to do the actual locking. * * This is not necessary in a server environment, if only the server is * accessing the database and display. * */ void window_lock() { system_lock(display); } void window_unlock() { system_unlock(display); } /*****************************************************************************************/ /* * window_system_connect * * This must be the first routine called by a client, since * it connects the client to the display system specified * by "display_name". It returns the wid of the background * window. By convention, NULL specifies the default display. */ long window_system_connect(display_name) char *display_name; { /* If this is this first time we've been called, do the initialization */ if (display <= NULL) { display = system_get_display(display_name); /* get screen info pointer */ if(display <= NULL) /* some sort of error */ return ((long)display); if (display->windows == NULL) { /* if no windows, create */ /* background same size as screen */ window_lock(); display->back_window = window_create(display->screen.origin, display->screen.size); display->locator_wid = display->back_window; /*init locator database */ window_unlock(); } return ((long)display->back_window); } else /* already inited - just return background id */ return ((long)display->back_window); } /*****************************************************************************************/ /* * window_create * * This routine creates a new window at the specified origin and specified * size. It places this window on top of all others. Most windows systems * would make this window of null size and not display it. This would give * the application time to fix it up and make it look right. A window can be * larger than the size of the display, the visibility lists will not include * any areas outside of the display. * * After creating the window, this routine recalculates the * visibilty areas of all other windows. */ long window_create(origin, size) point origin; /* origin of new window */ point size; /* size of new widnow */ { window_rec *new_window; /* some checks first */ if (display <= NULL) /* not inited */ return (sws_not_connected); if ((size.x < 0) | (size.y < 0)) return (sws_bad_size); /* negative size is illegal */ /* alloc memory for new window descriptor */ new_window = (window_rec*) sws_malloc(sizeof *new_window); /* init window descriptor */ new_window->visinfo.warea.origin = origin; new_window->visinfo.warea.size = size; new_window->visinfo.vis_list = NULL; /* input stuff */ new_window->inputinfo.erb = NULL; /* no input selected */ window_lock(); /* make sure no one else is around */ /* insert this window on top of the window list*/ new_window->next = display->windows; display->windows = new_window; new_window->prev = NULL; if (new_window->next != NULL) new_window->next->prev = new_window; /* new window is on top, so recalculate vis lists */ /* no exposure events are possible. */ calc_vis_list(new_window, FALSE); window_unlock(); /* all done with db */ return ((long)new_window); } /*****************************************************************************************/ /* * window_change * * This routine changes the size and origin of a window. * Some systems get fancy and have two routines. * * This routine recalculates the visibility lists; refresh * events can be generated. * */ long window_change(wid, origin, size) long wid; /*window to change*/ point origin; /*new origin */ point size; /*new size */ { window_rec *window; /* some checks first */ if (display <= NULL) /* not inited */ return (sws_not_connected); if ((window = check_window(wid)) == NULL) /* bad ID */ return (sws_no_such_window); if (window->next == NULL) /* can't change background */ return (sws_bad_background_op); if ((size.x < 0) || (size.y < 0)) /* no negative size */ return (sws_bad_size); window_lock(); /* make sure no one else is around */ /* change parameters and recalc vis lists */ window->visinfo.warea.size = size; window->visinfo.warea.origin = origin; /* recalc vis lists, refresh possible */ calc_vis_list(window, TRUE); window_unlock(); /* all done with db */ return (TRUE); } /*****************************************************************************************/ /* * window_close * * This routine closes, i.e. deletes, a window. All traces of the * window are removed from the system. * * This routine recalculates the visibility lists; refresh * events can be generated. * */ long window_close(wid) long wid; { window_rec *window; route_block *erb; /* some check first */ if (display <= NULL) /* not inited */ return (sws_not_connected); if ((window = check_window(wid)) == NULL) /* bad ID */ return (sws_no_such_window); if (window->next == NULL) /* don't touch background */ return (sws_bad_background_op); window_lock(); /* make sure no one else is around */ /* first remove window from linked list*/ if (window->next != NULL) window->next->prev = window->prev; if (window->prev != NULL) window->prev->next = window->next; else display->windows = window->next; /* recalc vis lists, refresh events possible */ calc_vis_list(window->next, TRUE); window_unlock(); /* all done with db */ /* free up memory */ /* This is usually the source of memory leaks */ /* In this case we must delete the vis list, and */ /* event routing blocks */ erb = window->inputinfo.erb; /* erb's */ while (erb != NULL) { window->inputinfo.erb = erb->next; sws_free(erb); erb = window->inputinfo.erb; } sws_free(window->visinfo.vis_list); /* vis list */ sws_free(window); /* window record */ return (TRUE); } /*****************************************************************************************/ /* * window_pop * * This routine pops (raises) a window to the top of * of the window list. * * Refresh events are only possible for window being popped. * */ long window_pop(wid) long wid; { window_rec *window; /* some checks first */ if (display <= NULL) /* not inited */ return (sws_not_connected); if ((window = check_window(wid)) == NULL) /* bad ID */ return (sws_no_such_window); window_lock(); /* make sure no one else is around */ /* if we were on top, don't bother */ if (window->prev == NULL) { window_unlock(); return (TRUE); } /* can't pop, background */ if (window->next == NULL) { window_unlock(); return (sws_bad_background_op); } /* first remove window from linked list */ window->next->prev = window->prev; window->prev->next = window->next; /* now insert on top of the list */ window->next = display->windows; /* we point to old top */ display->windows = window; /* screen points to us */ window->prev = NULL; /* we're on top */ window->next->prev = window; /* next guy points back to us */ /* (can't be null) */ /* recalc vis lists, refresh events are only possible for window */ calc_vis_list(window, FALSE); input_process(window, REFRESH); /* might get unnecessary REFRESH events */ window_unlock(); /* all done with db */ return (TRUE); } /*****************************************************************************************/ /* * window_information * This routine returns interesting info * about a window: * origin * size * ptr to vis list * */ long window_information(wid, winfo) long wid; vis_info *winfo; { window_rec *window; /* some checks first */ if (display <= NULL) /* not inited */ return (sws_not_connected); if ((window = check_window(wid)) == NULL) /* bad ID */ return (sws_no_such_window); /* Just copy internal info - this is a bit dangerous */ /* since we're giving the app our ptr to the vis lists */ /* Some people may consider this a feature! */ window_lock(); /* make sure no one else is around */ *winfo = window->visinfo; window_unlock(); /* all done with db */ return (TRUE); } /*****************************************************************************************/ /* * window_wid * This routine return the wid of the topmost * window containing the point (x, y). This is * used by clients to figure out which window * the locator is pointing to. * */ long window_wid(position) point position; { window_rec *window; /* some check first */ if (display <= NULL) /* not inited */ return (sws_not_connected); window_lock(); /* make sure no one else is around */ /* Start at the top and look for the first */ /* window that contains the point */ for (window = display->windows; window != NULL; window = window->next) { if (((position.x > window->visinfo.warea.origin.x) && (position.x <= (window->visinfo.warea.origin.x + window->visinfo.warea.size.x - 1))) && ((position.y > window->visinfo.warea.origin.y) && (position.y <= (window->visinfo.warea.origin.y + window->visinfo.warea.size.y - 1)))) { window_unlock(); return ((long)window); } } window_unlock(); /* all done with db */ /* if outside just return background */ /* We could return NULL */ return (display->back_window); } /*****************************************************************************************/ /* * check_window * validates a wid and returns pointer to the window record * in our case wid IS the pointer, this routine simply * checks to see that it is in the current list * this prevents random wid from being used, but is not * so robust as to detect re-use of the same memory * */ window_rec * check_window(wid) long wid; { window_rec *window; /* search the window list */ for (window = display->windows; window != NULL; window = window->next) if ((long)window == wid) return (window); /* found it */ /* didn't find it */ return (NULL); } /*****************************************************************************************/ /* * overlap * little routine to determine if two rectangles overlap * returns TRUE if they do, FALSE otherwise. Doesn't figure * out who's on top. * */ int overlap(a, b) area a; area b; { if (a.origin.x >= b.origin.x+b.size.x) /* a to the right? */ return (FALSE); if (a.origin.y >= b.origin.y+b.size.y) /* a below? */ return (FALSE); if (a.origin.x+a.size.x <= b.origin.x) /* a above? */ return (FALSE); if (a.origin.y+a.size.y <= b.origin.y) /* a to the left? */ return (FALSE); /* If the above tests fail areas overlap */ return (TRUE); } /*****************************************************************************************/ /* * exclude * * This routine excludes a rectangle(top) from a list of rectangles * note that this routine does not produce an irreducible set; but * does gaurantee that no two rectangles overlap. * * (WARNING: I think there's a bug in here!) * */ area * exclude (top, list) area top; /* area to be exclude */ area *list; /* list of areas to operate on */ { area *new_list; int i; int j; int len; struct { int xs; int xe; int ys; int ye } rem, top_limits; /* it's a lot easier using start and end point */ top_limits.xs = top.origin.x; top_limits.xe = top.origin.x + top.size.x; top_limits.ys = top.origin.y; top_limits.ye = top.origin.y + top.size.y; j = 0; for (len = 0;list[len].size.x != 0; len++) /* get list len */ ; new_list = (area *)sws_malloc((len+1)*sizeof (*new_list)); /* make new list */ /* look for the four exposed strips at top, sides and bottom */ for (i = 0; i < len; i++) { if (overlap(top, list[i]) == FALSE) /*if no overlap, just copy */ new_list[j++] = list[i]; else { /* calc 4 fragments */ /* make enough room for 4 new entries */ new_list = (area *)sws_realloc(new_list, max(len+4, j+4)*sizeof (*new_list)); rem.xs = list[i].origin.x; /* start with full area */ rem.xe = list[i].origin.x + list[i].size.x; rem.ys = list[i].origin.y; rem.ye = list[i].origin.y + list[i].size.y; if (top_limits.xs > rem.xs) { /* left side strip */ new_list[j].origin.x = rem.xs; new_list[j].origin.y = rem.ys; new_list[j].size.x = top_limits.xs - rem.xs; new_list[j++].size.y = rem.ye - rem.ys; rem.xs = top_limits.xs; } if (top_limits.xe < rem.xe) { /* right side strip */ new_list[j].origin.x = top_limits.xe; new_list[j].origin.y = rem.ys; new_list[j].size.x = rem.xe - top_limits.xe; new_list[j++].size.y = rem.ye - rem.ys; rem.xe = top_limits.xe; } if (top_limits.ye < rem.ye) { /* bottom strip */ new_list[j].origin.x = rem.xs; new_list[j].origin.y = top_limits.ye; new_list[j].size.x = rem.xe - rem.xs; new_list[j++].size.y = rem.ye - top_limits.ye; rem.ye = top_limits.ye; } if (top_limits.ys > rem.ys) { /* top strip */ new_list[j].origin.x = rem.xs; new_list[j].origin.y = rem.ys; new_list[j].size.x = rem.xe - rem.xs; new_list[j++].size.y = top_limits.ys - rem.ys; } } } /* free the old list and return new */ new_list[j] = null_area; /* insert terminator */ sws_free(list); return (new_list); } /*****************************************************************************************/ /* calc_vis_list * This routine calculates the visibility list for a set of windows * starting with "start_window" and going down to the bottom. * The resultant vis lists are not irreducible - we could add * a coalescing routine at the end. * * When we expose an area of a window, we generate an refresh event. * If we don't expose an area we shouldn't generate an refresh event. * That calculation is a bit tedious. Our simple exposure algorithm * can't tell the difference between exposure and damage. So, we ask * for help from the caller in the form of the "notify" parameter. * If TRUE, we generate refresh events; if FALSE we don't. * * (Doing it right would be a nice excerise for the student) * */ void calc_vis_list(start_window, notify) window_rec *start_window; /* where in the window list to start */ int notify; { window_rec *window; window_rec *super_window; int i; int max_len; int gen_exposure; area *new_list; point end; for (window = start_window; window != NULL; window = window->next) { max_len = 10; new_list = (area *)sws_malloc(max_len*sizeof (*new_list)); /* Since the physical display can be smaller than the window, * start with physical display limits and intersect them with * the window limits, if this were hierarchical system we'd * use the parent limits instead of the display limits. */ /* first, adjust the origin if necessary */ new_list->origin.x = max(display->screen.origin.x, window->visinfo.warea.origin.x); new_list->origin.y = max(display->screen.origin.y, window->visinfo.warea.origin.y); /* now calc the size */ end.x = min(display->screen.origin.x + display->size.x, window->visinfo.warea.origin.x + window->visinfo.warea.size.x); end.y = min(display->screen.origin.y + display->size.y, window->visinfo.warea.origin.y + window->visinfo.warea.size.y); new_list->size.x = max(end.x - new_list->origin.x, 0); new_list->size.y = max(end.y - new_list->origin.y, 0); /* if either dim is zero, set the other to zero */ if (new_list->size.x != 0 && new_list->size.y != 0) { new_list[1].size.x =0; new_list[1].size.y =0; /* we have a no-zero visibility list, exclude area * covered by a superior window */ for (super_window = display->windows; super_window != window; super_window = super_window->next) { /* check for no overlap first */ if (overlap(super_window->visinfo.warea, window->visinfo.warea)) new_list = exclude(super_window->visinfo.warea, new_list); } } /* all done with this window check to see if */ /* we should generate an exposure event */ if (window->visinfo.vis_list != NULL) { if (notify) { /* Compare the old list to the new and if they are different */ /* generate an exposure event. NOTE THAT THIS METHOD WILL */ /* CAUSE SPURIOUS EXPOSURE EVENTS. (nice exercise for the student!) */ gen_exposure = FALSE; for (i = 0; new_list[i].size.x + window->visinfo.vis_list[i].size.x != 0; i++) { /* compare element */ if (!same_area(&window->visinfo.vis_list[i]), &new_list[i]) { gen_exposure = TRUE; /* not equal */ break; /* get out */ } } if (gen_exposure) (void)input_process(window, REFRESH); } } sws_free(window->visinfo.vis_list); /* free up the old list, and get new one */ window->visinfo.vis_list = new_list; } return; } static same_area(a, b) area a, b; { if (a.origin.x != b.origin.x || a.origin.y != b.origin.y) return FALSE; if (a.size.x != b.size.x || a.size.y != b.size.y) return FALSE; return TRUE; } /*****************************************************************************************/ /* INPUT ROUTINES */ /*****************************************************************************************/ /* * input_connect * * Creates an input connection between the window system * and a client. This conection consists of an event queue * used to queue input events. Queue is standard ring buffer. */ long input_connect() { event_queue *queue; /* some checks first */ if (display <= NULL) /* not inited */ return (sws_not_connected); /* create the event queue */ queue = (event_queue *)sws_malloc(sizeof (*queue)); queue->in = 0; /* set in and out equal - i.e. queue empty */ queue->out = 0; /* do system specific init */ system_init_queue(queue); return ((long)queue); } /*****************************************************************************************/ /* * input_checks * This routine returns the next event from the input queue * if none is available it return null. * */ long input_check(queue, event) event_queue *queue; input_event *event; { /* some checks first */ if (display <= NULL) /* not inited */ return (sws_not_connected); if (queue == NULL) /* null qid */ return (sws_bad_queue); /* if something in queue, get it */ if (queue->in != queue->out) { *event = queue->buffer[queue->out]; queue->out = (queue->out + 1) % BUFFER_SIZE; return (TRUE); } else return (FALSE); } /*****************************************************************************************/ /* * input_wait returns the next event from the input queue * if none is available this routine blocks until one is available * */ long input_wait(queue, event) event_queue *queue; input_event *event; { long result; /* if something in queue or error return it */ /* else wait for something */ while (TRUE) { if (result = input_check(queue, event)) return (result); else system_wait(queue); } /* NOTREACHED */ } /*****************************************************************************************/ /* * input select * This routine is used by caller to specify which type * events the caller is interested in. They are is specified * by an event mask. No events are ever given to a client * unless the client has asked for them. * * There two type of selects: * * Normal - caller specifies a wid, if the event occurs * in that window, all clients who requested it * for that window get it - multiple routes! * * Grab - caller specifies a NULL qid, the caller wants * all such events whether or not the event occured * in the window. * * An event occurs in the window if the locator is in the window * or the event is window related, i.e. EXIT, ENTER, REFRESH. * */ long input_select(wid, queue, mask) long wid; event_queue *queue; event_mask mask; { window_rec *window; /* window of interest */ route_block *route_rec; route_block **last_rec; /* some checks first */ if (display <= NULL) /* not inited */ return (sws_not_connected); if (wid != 0) { if ((window = check_window(wid)) == NULL) /* bad ID */ return (sws_no_such_window); route_rec = window->inputinfo.erb; last_rec = &(window->inputinfo.erb); } else { /* no wid, it's a grab request */ route_rec = display->grab_list; last_rec = &display->grab_list; } /* get ptr to first routing block and * see if we already have a mask for it * if mask = 0, delete the entry */ while (route_rec != NULL) { if (route_rec->queue = queue) { /* found it */ if (mask != 0) /* store new mask */ route_rec->mask = mask; else { /* if NULL mask, delete entry */ if (route_rec->next != NULL) route_rec->next->prev = route_rec->prev; if (route_rec->prev != NULL) route_rec->prev->next = route_rec->next; sws_free(route_rec); } return (TRUE); } else { /* keep looking */ last_rec = &route_rec; route_rec = route_rec->next; } } /* no entry for this queue, create one (unless mask == 0) */ if (mask != 0) { /* get some memory */ route_rec = (route_block *)sws_malloc(sizeof (*route_rec)); route_rec->queue = queue; /* store qid */ route_rec->mask = mask; /* current mask */ route_rec->prev = *last_rec; /* fix-up pointers */ route_rec->next = NULL; /* we are the end of list */ if (route_rec->prev != NULL) route_rec->prev->next = route_rec; else *last_rec = route_rec; } return (TRUE); } /*****************************************************************************************/ /* * input_process * This routine takes an event from a client and processes it, i.e, * tries to route it. It is primarily used internally and by an * "input server process". It can be called by any client. * * If a wid is specified the event is routed directly to that * window, it cannot be "grabbed". If no wid is specified, * the event is routed to the window containing the locator. * */ long input_process(wid, code, data) long wid; /* route event to this window */ unsigned char code; /* event code */ point data; /* device specific data */ { void position_locator(); /* some forward declarations */ long route_event(); input_event event; /* event to be generated and routed */ long success; /* boolean as to whether or not we've routed the event */ window_rec *window; /* window of interest */ event_mask mask; /* routing mask */ if (display <= NULL) /* not inited */ return (sws_not_connected); /* check for device specific processing */ /* in our case that's the locator */ if (code == LOCATOR) position_locator(data); /* this routine will generate enter/exit events */ /* check the wid and validate locator postion */ /* if wid route to that window, if 0 use wid that locator is in */ if (wid != 0) { if ((window = check_window(wid)) == NULL) /* bad ID */ return (sws_no_such_window); event.window = wid; /* store the window ID */ } else { /* no wid, use locator wid */ /* bad locator wid */ if ((window = check_window(display->locator_wid)) == NULL) position_locator(display->locator_pos); /* fix it */ event.window = display->locator_wid; } /* now generate an event record and route it to all who want it */ gettimeofday(&event.tmstamp, NULL); /* get timestamp */ event.code = code; /* store code */ success = FALSE; /* we haven't routed yet */ mask = 0; /* force mask generation */ /* if no wid initially specified, check grab list */ if (wid == 0) { event.window = display->back_window; /* for grabs use backgropund wid */ event.position = display->locator_pos; /* and abs position */ success = route_event(display->grab_list, &event, &mask, FALSE); } /* if no grab, try window routing list */ if (success == FALSE ) { window = check_window(event.window); /* get window ptr */ /* calculate relative position of locator in window */ event.position.x = display->locator_pos.x - window->visinfo.warea.origin.x; event.position.y = display->locator_pos.y - window->visinfo.warea.origin.y; success = route_event(window->inputinfo.erb, &event, &mask, TRUE); } return (success); } /*****************************************************************************************/ /* * route_event * This routine routes an event, using the specified routing list * It calculates and returns a mask for the event. * If multi_route is TRUE, the event is routed to multiple clients. * */ long route_event(list, event, mask, multi_route) route_block *list; input_event *event; event_mask *mask; long multi_route; { route_block *route_rec; long in; long success; /* Generate a mask if necessary. * This is really cumbersome since we crammed * all events into a single 32 bit mask. In the * real world we would have set up a cleaner relation. */ if (*mask == 0) switch (event->code) { case LOCATOR: *mask = LOCATOR_MASK; break; case REFRESH: *mask = REFRESH_MASK; break; case ENTER: *mask = ENTER_MASK; break; case EXIT: *mask = EXIT_MASK; break; case LEFT: *mask = LEFT_MASK; break; case LEFT_UP: *mask = LEFT_UP_MASK; break; case MIDDLE: *mask = MIDDLE_MASK; break; case MIDDLE_UP: *mask = MIDDLE_UP_MASK; break; case RIGHT: *mask = RIGHT_MASK; break; case RIGHT_UP: *mask = RIGHT_UP_MASK; break; case F0: *mask = F0_MASK; break; case F0_UP: *mask = F0_UP_MASK; break; case F1: *mask = F1_MASK; break; case F1_UP: *mask = F1_UP_MASK; break; case F2: *mask = F2_MASK; break; case F2_UP: *mask = F2_UP_MASK; break; case F3: *mask = F3_MASK; break; case F3_UP: *mask = F3_UP_MASK; break; case F4: *mask = F4_MASK; break; case F4_UP: *mask = F4_UP_MASK; break; case F5: *mask = F5_MASK; break; case F5_UP: *mask = F5_UP_MASK; break; case F6: *mask = F6_MASK; break; case F6_UP: *mask = F6_UP_MASK; break; case F7: *mask = F7_MASK; break; case F7_UP: *mask = F7_UP_MASK; break; case F8: *mask = F8_MASK; break; case F8_UP: *mask = F8_UP_MASK; break; case F9: *mask = F9_MASK; break; case F9_UP: *mask = F9_UP_MASK; break; default: *mask = KBD_MASK; break; /* event->code <= LAST_ASCII */ } /* Try to route the event, look at each mask on the routing list. */ /* Keep going until the list is exhausted unless no multiple routes */ /* were specified. In that case exit on the first route */ success = FALSE; route_rec = list; while (route_rec != NULL) { /* look for a match */ if (route_rec->mask & *mask) { /* found one */ in = (route_rec->queue->in + 1) % BUFFER_SIZE; /* calc new in */ if (in != route_rec->queue->out) { /* check for overflow */ route_rec->queue->buffer[route_rec->queue->in] = *event; /* there's room */ route_rec->queue->in = in; /* advance the in */ system_adv(route_rec->queue); /* wake up waiters */ success = TRUE; /* successful route */ } } if (success && (multi_route == FALSE)) break; route_rec = route_rec->next; /* go thru all routing blocks */ } return (success); } /*****************************************************************************************/ /* * position_locator * This routine keeps track of the position of the locator. It will generate * EXIT and ENTER EVENTS as the locator moves from window to window. * * It also moves the cursor pattern. */ void position_locator(position) point position; { long new_wid; /* get wid of new position */ new_wid = window_wid(position); if (display->locator_wid != new_wid) { /* it's different */ if (display->locator_wid != 0) /* generate an exit event */ input_process(display->locator_wid, EXIT); display->locator_pos = position; /* new position */ display->locator_wid = new_wid; /* new wid */ /* generate an enter event */ input_process(display->locator_wid, ENTER); } else /* same wid, just new position */ display->locator_pos = position; draw_cursor(position); /* fix up the cursor */ return; } /**** end sws.c********/ /****start sis.c********/ /* * sis.c * Simple Input Server * for the Simple Window System * * Author: Daniel E. Ganek * SIGGRAPH '87 * */ #include <stdio.h> #include "sws_env.h" main() { long bid; /* background wid */ char code; /* input code */ point data; /* input data */ /* Connect us to the SWS - we're probably the first ones */ if ((bid = window_system_connect(0L)) <= NULL) return (bid); /* * Just loop getting an input event from the system and * processing it. To convert this to a general purpose * server replace "get_system_input" with a "get_event" * routine and replace "input_process" with a switch * statement to process either input events or client * requests. */ while (TRUE) { get_system_input(&code, &data); /* get a system event */ input_process(0, code, data); /* give it to the SWS */ } /* and let it route it */ exit(0); } /**** end sis.c********/