|  | /* | 
|  | * $Id: x11.c,v 1.29 2004/02/03 21:23:51 phil Exp $ | 
|  | * X11 support for XY display simulator | 
|  | * Phil Budne <phil@ultimate.com> | 
|  | * September 2003 | 
|  | * | 
|  | * Changes from Douglas A. Gwyn, Jan 8, 2004 | 
|  | * | 
|  | * started from PDP-8/E simulator (vc8e.c & kc8e.c); | 
|  | *	This PDP8 Emulator was written by Douglas W. Jones at the | 
|  | *	University of Iowa.  It is distributed as freeware, of | 
|  | *	uncertain function and uncertain utility. | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * Copyright (c) 2003-2004, Philip L. Budne | 
|  | * | 
|  | * Permission is hereby granted, free of charge, to any person obtaining a | 
|  | * copy of this software and associated documentation files (the "Software"), | 
|  | * to deal in the Software without restriction, including without limitation | 
|  | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | 
|  | * and/or sell copies of the Software, and to permit persons to whom the | 
|  | * Software is furnished to do so, subject to the following conditions: | 
|  | * | 
|  | * The above copyright notice and this permission notice shall be included in | 
|  | * all copies or substantial portions of the Software. | 
|  | * | 
|  | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
|  | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
|  | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL | 
|  | * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | 
|  | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | 
|  | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | 
|  | * | 
|  | * Except as contained in this notice, the names of the authors shall | 
|  | * not be used in advertising or otherwise to promote the sale, use or | 
|  | * other dealings in this Software without prior written authorization | 
|  | * from the authors. | 
|  | */ | 
|  |  | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include "ws.h" | 
|  | #include "display.h" | 
|  |  | 
|  | #include <X11/X.h> | 
|  | #include <X11/Xlib.h> | 
|  | #include <X11/Intrinsic.h> | 
|  | #include <X11/StringDefs.h> | 
|  | #include <X11/Core.h> | 
|  | #include <X11/Shell.h> | 
|  | #include <X11/cursorfont.h> | 
|  |  | 
|  | #include <sys/types.h> | 
|  | #include <sys/time.h> | 
|  |  | 
|  | #ifndef PIX_SIZE | 
|  | #define PIX_SIZE 1 | 
|  | #endif | 
|  |  | 
|  | //#define FULL_SCREEN 1 | 
|  | #define NO_CURSOR 1 | 
|  | #define NO_BORDER 1 | 
|  |  | 
|  | /* | 
|  | * light pen location | 
|  | * see ws.h for full description | 
|  | */ | 
|  | int ws_lp_x = -1; | 
|  | int ws_lp_y = -1; | 
|  |  | 
|  | static XtAppContext app_context; /* the topmost context for everything */ | 
|  | static Display* dpy;            /* its display */ | 
|  | static int      scr;            /* its screen */ | 
|  | static Colormap cmap;           /* its colormap */ | 
|  | static Widget crtshell;         /* the X window shell */ | 
|  | static Widget crt;              /* the X window in which output will plot */ | 
|  | static int xpixels, ypixels; | 
|  | #ifdef FULL_SCREEN | 
|  | /* occupy entire screen for vintage computer fan Sellam Ismail */ | 
|  | static int xoffset, yoffset; | 
|  | #endif | 
|  |  | 
|  | static GC whiteGC;              /* gc with white foreground */ | 
|  | static GC blackGC;              /* gc with black foreground */ | 
|  | static int buttons = 0;		/* tracks state of all buttons */ | 
|  |  | 
|  | static int os_pollfd(int, int);	/* forward */ | 
|  |  | 
|  | /* here on any mouse button down, AND movement when any button down */ | 
|  | static void | 
|  | handle_button_press(w, d, e, b) | 
|  | Widget w; | 
|  | XtPointer d; | 
|  | XEvent *e; | 
|  | Boolean *b; | 
|  | { | 
|  | int x, y; | 
|  |  | 
|  | x = e->xbutton.x; | 
|  | y = e->xbutton.y; | 
|  | #ifdef FULL_SCREEN | 
|  | x -= xoffset; | 
|  | y -= yoffset; | 
|  | #endif | 
|  | #if PIX_SIZE > 1 | 
|  | x *= PIX_SIZE; | 
|  | y *= PIX_SIZE; | 
|  | #endif | 
|  |  | 
|  | #ifndef NO_CURSUR | 
|  | if (!display_tablet) | 
|  | /* crosshair cursor to indicate tip of active pen */ | 
|  | XDefineCursor(dpy, XtWindow(crt), | 
|  | (Cursor) XCreateFontCursor(dpy, XC_crosshair)); | 
|  | #endif | 
|  |  | 
|  | y = ypixels - y - 1; | 
|  | /*printf("lightpen at %d,%d\n", x, y); fflush(stdout);*/ | 
|  | ws_lp_x = x; | 
|  | ws_lp_y = y; | 
|  |  | 
|  | if (e->type == ButtonPress) { | 
|  | buttons |= e->xbutton.button; | 
|  |  | 
|  | if (e->xbutton.button == 1) { | 
|  | display_lp_sw = 1; | 
|  | /*printf("tip switch activated\n"); fflush(stdout);*/ | 
|  | } | 
|  | } | 
|  |  | 
|  | if (b) | 
|  | *b = TRUE; | 
|  | } | 
|  |  | 
|  | static void | 
|  | handle_button_release(w, d, e, b) | 
|  | Widget w; | 
|  | XtPointer d; | 
|  | XEvent *e; | 
|  | Boolean *b; | 
|  | { | 
|  | if ((buttons &= ~e->xbutton.button) == 0) {	/* all buttons released */ | 
|  | #ifndef NO_CURSOR | 
|  | if (!display_tablet) | 
|  | /* pencil cursor (close to a pen!) to indicate inactive pen posn */ | 
|  | XDefineCursor(dpy, XtWindow(crt), | 
|  | (Cursor) XCreateFontCursor(dpy, XC_pencil)); | 
|  | #endif | 
|  |  | 
|  | /* XXX change cursor back?? */ | 
|  | ws_lp_x = ws_lp_y = -1; | 
|  | } | 
|  |  | 
|  | if (e->xbutton.button == 1) { | 
|  | display_lp_sw = 0; | 
|  | /*printf("tip switch deactivated\n"); fflush(stdout);*/ | 
|  | } | 
|  |  | 
|  | if (b) | 
|  | *b = TRUE; | 
|  | } | 
|  |  | 
|  | static void | 
|  | handle_key_press(w, d, e, b) | 
|  | Widget w; | 
|  | XtPointer d; | 
|  | XEvent *e; | 
|  | Boolean *b; | 
|  | { | 
|  | int shift = (ShiftMask & e->xkey.state) != 0; | 
|  | KeySym key = XKeycodeToKeysym( dpy, e->xkey.keycode, shift ); | 
|  |  | 
|  | /*printf("key %d down\n", key); fflush(stdout);*/ | 
|  | if ((key & 0xff00) == 0) | 
|  | display_keydown(key); | 
|  |  | 
|  | if (b) | 
|  | *b = TRUE; | 
|  | } | 
|  |  | 
|  | static void | 
|  | handle_key_release(w, d, e, b) | 
|  | Widget w; | 
|  | XtPointer d; | 
|  | XEvent *e; | 
|  | Boolean *b; | 
|  | { | 
|  | int shift = (ShiftMask & e->xkey.state) != 0; | 
|  | KeySym key = XKeycodeToKeysym( dpy, e->xkey.keycode, shift ); | 
|  |  | 
|  | /*printf("key %d up\n", key); fflush(stdout);*/ | 
|  | if ((key & 0xff00) == 0) | 
|  | display_keyup(key); | 
|  |  | 
|  | if (b) | 
|  | *b = TRUE; | 
|  | } | 
|  |  | 
|  | static void | 
|  | handle_exposure(w, d, e, b) | 
|  | Widget w; | 
|  | XtPointer d; | 
|  | XEvent *e; | 
|  | Boolean *b; | 
|  | { | 
|  | display_repaint(); | 
|  |  | 
|  | if (b) | 
|  | *b = TRUE; | 
|  | } | 
|  |  | 
|  | int | 
|  | ws_init(char *crtname,		/* crt type name */ | 
|  | int xp, int yp,		/* screen size in pixels */ | 
|  | int colors)			/* colors to support (not used) */ | 
|  | { | 
|  | Arg arg[25]; | 
|  | XGCValues gcvalues; | 
|  | unsigned int n; | 
|  | int argc; | 
|  | char *argv[1]; | 
|  | int height, width; | 
|  |  | 
|  | xpixels = xp;		/* save screen size */ | 
|  | ypixels = yp; | 
|  |  | 
|  | XtToolkitInitialize(); | 
|  | app_context = XtCreateApplicationContext(); | 
|  | argc = 0; | 
|  | argv[0] = NULL; | 
|  | dpy = XtOpenDisplay( app_context, NULL, NULL, crtname, NULL, 0, | 
|  | &argc, argv); | 
|  |  | 
|  | scr = DefaultScreen(dpy); | 
|  |  | 
|  | crtshell = XtAppCreateShell( crtname, /* app name */ | 
|  | crtname, /* app class */ | 
|  | #ifdef NO_BORDER | 
|  | overrideShellWidgetClass, | 
|  | #else | 
|  | applicationShellWidgetClass, /* wclass */ | 
|  | #endif | 
|  | dpy, /* display */ | 
|  | NULL, /* arglist */ | 
|  | 0);	/* nargs */ | 
|  |  | 
|  | cmap = DefaultColormap(dpy, scr); | 
|  |  | 
|  | /* | 
|  | * Create a drawing area | 
|  | */ | 
|  |  | 
|  | n = 0; | 
|  | #ifdef FULL_SCREEN | 
|  | /* center raster in full-screen black window */ | 
|  | width = DisplayWidth(dpy,scr); | 
|  | height = DisplayHeight(dpy,scr); | 
|  |  | 
|  | xoffset = (width - xpixels*PIX_SIZE)/2; | 
|  | yoffset = (height - ypixels*PIX_SIZE)/2; | 
|  | #else | 
|  | width = xpixels*PIX_SIZE; | 
|  | height = ypixels*PIX_SIZE; | 
|  | #endif | 
|  | XtSetArg(arg[n], XtNwidth, width);				n++; | 
|  | XtSetArg(arg[n], XtNheight, height);			n++; | 
|  | XtSetArg(arg[n], XtNbackground, BlackPixel( dpy, scr ));	n++; | 
|  |  | 
|  | crt = XtCreateWidget( crtname, widgetClass, crtshell, arg, n); | 
|  | XtManageChild(crt); | 
|  | XtPopup(crtshell, XtGrabNonexclusive); | 
|  |  | 
|  | /* | 
|  | * Create black and white Graphics Contexts | 
|  | */ | 
|  |  | 
|  | gcvalues.foreground = BlackPixel( dpy, scr ); | 
|  | gcvalues.background = BlackPixel( dpy, scr ); | 
|  | blackGC = XCreateGC(dpy, XtWindow(crt), | 
|  | GCForeground | GCBackground, &gcvalues); | 
|  |  | 
|  | gcvalues.foreground = WhitePixel( dpy, scr ); | 
|  | whiteGC = XCreateGC(dpy, XtWindow(crt), | 
|  | GCForeground | GCBackground, &gcvalues); | 
|  |  | 
|  | #ifndef NO_CURSOR | 
|  | if (!display_tablet) { | 
|  | /* pencil cursor */ | 
|  | XDefineCursor(dpy, XtWindow(crt), | 
|  | (Cursor) XCreateFontCursor(dpy, XC_pencil)); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | * Setup to handle events | 
|  | */ | 
|  |  | 
|  | XtAddEventHandler(crt, ButtonPressMask|ButtonMotionMask, FALSE, | 
|  | handle_button_press, NULL); | 
|  | XtAddEventHandler(crt, ButtonReleaseMask, FALSE, | 
|  | handle_button_release, NULL); | 
|  | XtAddEventHandler(crt, KeyPressMask, FALSE, | 
|  | handle_key_press, NULL); | 
|  | XtAddEventHandler(crt, KeyReleaseMask, FALSE, | 
|  | handle_key_release, NULL); | 
|  | XtAddEventHandler(crt, ExposureMask, FALSE, | 
|  | handle_exposure, NULL); | 
|  | return 1; | 
|  | } /* ws_init */ | 
|  |  | 
|  |  | 
|  | /* Added 2006-07-19 SAI */ | 
|  |  | 
|  | void ws_close(void) | 
|  | { | 
|  |  | 
|  | XtCloseDisplay(dpy); | 
|  |  | 
|  | } | 
|  |  | 
|  |  | 
|  | void * | 
|  | ws_color_black(void) | 
|  | { | 
|  | return blackGC; | 
|  | } | 
|  |  | 
|  | void * | 
|  | ws_color_white(void) | 
|  | { | 
|  | return whiteGC; | 
|  | } | 
|  |  | 
|  | void * | 
|  | ws_color_rgb(int r, int g, int b) | 
|  | { | 
|  | XColor color; | 
|  |  | 
|  | color.red = r; | 
|  | color.green = g; | 
|  | color.blue = b; | 
|  | /* ignores flags */ | 
|  |  | 
|  | if (XAllocColor(dpy, cmap, &color)) { | 
|  | XGCValues gcvalues; | 
|  | memset(&gcvalues, 0, sizeof(gcvalues)); | 
|  | gcvalues.foreground = gcvalues.background = color.pixel; | 
|  | return XCreateGC(dpy, XtWindow(crt), | 
|  | GCForeground | GCBackground, | 
|  | &gcvalues); | 
|  | } | 
|  | /* allocation failed */ | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* put a point on the screen */ | 
|  | void | 
|  | ws_display_point(int x, int y, void *color) | 
|  | { | 
|  | GC gc = (GC) color; | 
|  |  | 
|  | if (x > xpixels || y > ypixels) | 
|  | return; | 
|  |  | 
|  | y = ypixels - y - 1;		/* X11 coordinate system */ | 
|  |  | 
|  | #ifdef FULL_SCREEN | 
|  | x += xoffset; | 
|  | y += yoffset; | 
|  | #endif | 
|  | if (gc == NULL) | 
|  | gc = blackGC;			/* default to off */ | 
|  | #if PIX_SIZE == 1 | 
|  | XDrawPoint(dpy, XtWindow(crt), gc, x, y); | 
|  | #else | 
|  | XFillRectangle(dpy, XtWindow(crt), gc, | 
|  | x*PIX_SIZE, y*PIX_SIZE, PIX_SIZE, PIX_SIZE); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | void | 
|  | ws_sync(void) | 
|  | { | 
|  | XFlush(dpy); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * elapsed wall clock time since last call | 
|  | * +INF on first call | 
|  | */ | 
|  |  | 
|  | struct elapsed_state { | 
|  | struct timeval tvs[2]; | 
|  | int new; | 
|  | }; | 
|  |  | 
|  | static unsigned long | 
|  | elapsed(struct elapsed_state *ep) | 
|  | { | 
|  | unsigned long val; | 
|  |  | 
|  | gettimeofday(&ep->tvs[ep->new], NULL); | 
|  | if (ep->tvs[!ep->new].tv_sec == 0) | 
|  | val = ~0L; | 
|  | else | 
|  | val = ((ep->tvs[ep->new].tv_sec - ep->tvs[!ep->new].tv_sec) * 1000000 + | 
|  | (ep->tvs[ep->new].tv_usec - ep->tvs[!ep->new].tv_usec)); | 
|  | ep->new = !ep->new; | 
|  | return val; | 
|  | } | 
|  |  | 
|  | /* called periodically */ | 
|  | int | 
|  | ws_poll(int *valp, int maxusec) | 
|  | { | 
|  | static struct elapsed_state es;	/* static to avoid clearing! */ | 
|  |  | 
|  | #ifdef WS_POLL_DEBUG | 
|  | printf("ws_poll %d\n", maxusec); | 
|  | fflush(stdout); | 
|  | #endif | 
|  | elapsed(&es);			/* start clock */ | 
|  | do { | 
|  | unsigned long e; | 
|  |  | 
|  | /* tried checking return, but lost on TCP connections? */ | 
|  | os_pollfd(ConnectionNumber(dpy), maxusec); | 
|  |  | 
|  | while (XtAppPending(app_context)) { | 
|  | XEvent event; | 
|  |  | 
|  | /* XXX check for connection loss; set *valp? return 0 */ | 
|  | XtAppNextEvent(app_context, &event ); | 
|  | XtDispatchEvent( &event ); | 
|  | } | 
|  | e = elapsed(&es); | 
|  | #ifdef WS_POLL_DEBUG | 
|  | printf(" maxusec %d e %d\r\n", maxusec, e); | 
|  | fflush(stdout); | 
|  | #endif | 
|  | maxusec -= e; | 
|  | } while (maxusec > 10000);	/* 10ms */ | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* utility: can be called from main program | 
|  | * which is willing to cede control | 
|  | */ | 
|  | int | 
|  | ws_loop(void (*func)(void *), void *arg) | 
|  | { | 
|  | int val; | 
|  |  | 
|  | /* XXX use XtAppAddWorkProc & XtAppMainLoop? */ | 
|  | while (ws_poll(&val,0)) | 
|  | (*func)(arg); | 
|  | return val; | 
|  | } | 
|  |  | 
|  | void | 
|  | ws_beep(void) | 
|  | { | 
|  | XBell(dpy, 0);			/* ring at base volume */ | 
|  | XFlush(dpy); | 
|  | } | 
|  |  | 
|  | /**************** | 
|  | * could move these to unix.c, if VMS versions needed | 
|  | * (or just (GASP!) ifdef) | 
|  | */ | 
|  |  | 
|  | /* public version, used by delay code */ | 
|  | unsigned long | 
|  | os_elapsed(void) | 
|  | { | 
|  | static struct elapsed_state es; | 
|  | return elapsed(&es); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * select/DisplayNumber works on VMS 7.0+? | 
|  | * could move to "unix.c" | 
|  | * (I have some nasty VMS code that's supposed to to the job | 
|  | * for older systems) | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * sleep for maxus microseconds, returning TRUE sooner if fd is readable | 
|  | * used by X11 driver | 
|  | */ | 
|  | static int | 
|  | os_pollfd(int fd, int maxus) | 
|  | { | 
|  |  | 
|  | /* use trusty old select (most portable) */ | 
|  | fd_set rfds; | 
|  | struct timeval tv; | 
|  |  | 
|  | if (maxus >= 1000000) {		/* not bloody likely, avoid divide */ | 
|  | tv.tv_sec = maxus / 1000000; | 
|  | tv.tv_usec = maxus % 1000000; | 
|  | } | 
|  | else { | 
|  | tv.tv_sec = 0; | 
|  | tv.tv_usec = maxus; | 
|  | } | 
|  | FD_ZERO(&rfds); | 
|  | FD_SET(fd, &rfds); | 
|  | return select(fd+1, &rfds, NULL, NULL, &tv) > 0; | 
|  | } |