home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume1
/
8711
/
7
< prev
next >
Wrap
Text File
|
1990-07-13
|
48KB
|
1,951 lines
Path: uunet!husc6!bbn!rochester!udel!burdvax!bpa!cbmvax!vu-vlsi!devon!ncoast!allbery
From: ganek@apollo.UUCP
Newsgroups: comp.sources.misc
Subject: A sample bitmapped window manager
Message-ID: <5324@ncoast.UUCP>
Date: 8 Nov 87 21:45:39 GMT
Sender: allbery@ncoast.UUCP
Lines: 1939
Approved: allbery@ncoast.UUCP
X-Archive: comp.sources.misc/8711/7
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********/