Todo:

Make this driver support the X11 matrix XLFD enhancement introduced in X11R6 (and/or support the STSF font API which supports matrix transformations as well). Fonts can be rotated at any angle.

#ident "@(#)VOGLE:driver/X11.c - VOGLE driver for X11 Windows" #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> #include <X11/Xlib.h> #include <X11/Xutil.h> #include "vogle.h" static char *me = "vogle"; #define LARGEFONT "fixed" #define SMALLFONT "fixed" #define MIN(x,y) ((x) < (y) ? (x) : (y)) #define CMAPSIZE 256 #define EV_MASK KeyPressMask|ButtonReleaseMask|ExposureMask|ButtonPressMask static Window winder; static Display *display; static int screen; static unsigned long carray[CMAPSIZE]; static Colormap colormap; static Drawable theDrawable; static GC theGC; static Pixmap bbuf; /* Back buffer pixmap */ static int back_used = 0; /* Have we backbuffered ? */ static XFontStruct *font_id = (XFontStruct *)NULL; XEvent event; static unsigned long colour; static unsigned int h, w; static char *smallf, *largef; static char *wintitle; #define X11DEV 0 #define x11DEV 1 static int GLOBAL_device = X11DEV; /******************************************************************************/ /* PROTOTYPES */ int X11_mapcolor(int i, int r, int g, int b); int X11_backbuf(void); int X11_color(int ind); int X11_clear(void); int X11_swapbuf(void); int X11_frontbuf(void); int X11_sync(void); int X11_checkkey(void); /******************************************************************************/ void X11_NEW(void){ int idum; idum=X11_checkkey(); XFlush(display); X11_backbuf(); X11_clear(); X11_swapbuf(); X11_frontbuf(); XSetBackground(display,theGC,carray[7]); XFlush(display); } /******************************************************************************/ int X11_RESIZE(void) { /* Adjust to new window size if it has changed */ XWindowAttributes retWindowAttributes; int x, y, w, h; Window rootw, childw; unsigned int mask; /* get window size -- using XGetWindowAttributes() */ /* Fill attribute structure with information about window in case size has changed */ if (XGetWindowAttributes(display, winder, &retWindowAttributes) == 0) { fprintf(stderr, "X11_clear: failed to get window attributes.\n"); exit(-1); } if(retWindowAttributes.width != vdevice.sizeSx || retWindowAttributes.height != vdevice.sizeSy ){ x = retWindowAttributes.x; y = retWindowAttributes.y; w = retWindowAttributes.width; h = retWindowAttributes.height; XQueryPointer(display, winder, &rootw, &childw, &x, &y, &x, &y, &mask); XTranslateCoordinates(display, winder, retWindowAttributes.root, 0, 0, &x, &y, &rootw); vdevice.sizeSx = w; vdevice.sizeSy = h; if (back_used) { back_used = 0; X11_backbuf(); } XFlush(display); } return(UNUSED); } /******************************************************************************/ int X11_init(void) { /* X11_init -- initialises X11 display. */ extern int myhandler(); int i; int x, y, prefx, prefy, prefxs, prefys; unsigned int bw, depth, mask; Window rootw, childw; char *av[2], *geom; char name[MAXVERTS]; char *varname; XSetWindowAttributes theWindowAttributes; XWindowAttributes retWindowAttributes; XSizeHints theSizeHints; unsigned long theWindowMask; XWMHints theWMHints; av[0] = me; av[1] = (char *)NULL; varname=getenv("USHDRIVER"); if(varname==(char *)NULL) { varname="UNDEFINED"; } if (!strcmp(varname,"NOCLOSE")) { fprintf(stderr,"%s: returning to X11 window\n", me); }else{ if ((display = XOpenDisplay((char *)NULL)) == (Display *)NULL) { fprintf(stderr,"%s: X11_init: can't connect to X server\n", me); exit(1); } winder = DefaultRootWindow(display); screen = DefaultScreen(display); colormap = DefaultColormap(display, screen); XSetIOErrorHandler(myhandler); } /* Set our standard colors... */ depth = vdevice.depth = DefaultDepth(display, screen); if (vdevice.depth == 1) { /* Black and white - anything that's not black is white for X11, anything that's not white is black for x11. */ if (GLOBAL_device == X11DEV ){ carray[0] = BlackPixel(display, screen); for (i = 1; i < CMAPSIZE; i++) carray[i] = WhitePixel(display, screen); } else{ carray[0] = WhitePixel(display, screen); for (i = 1; i < CMAPSIZE; i++) carray[i] = BlackPixel(display, screen); } } else { /* * Color, try to get our colors close to what's in the * default colormap. */ if(GLOBAL_device == X11DEV){ X11_mapcolor(0, 0, 0, 0); X11_mapcolor(7, 255, 255, 255); } else{ X11_mapcolor(7, 0, 0, 0); X11_mapcolor(0, 255, 255, 255); } X11_mapcolor(1, 255, 0, 0); X11_mapcolor(2, 0, 255, 0); X11_mapcolor(3, 255, 255, 0); X11_mapcolor(4, 0, 0, 255); X11_mapcolor(5, 255, 0, 255); X11_mapcolor(6, 0, 255, 255); } vogle_getprefposandsize(&prefx, &prefy, &prefxs, &prefys); /* NEED TO USE XGRABPOINTER here??? */ XQueryPointer(display, winder, &rootw, &childw, &x, &y, &x, &y, &mask); if (childw == None) childw = rootw; XGetGeometry(display, childw, &rootw, &x, &y, &w, &h, &bw, &depth); vdevice.depth = depth; /* theWindowAttributes.backing_store = WhenMapped; */ theWindowAttributes.backing_store = Always; theWindowAttributes.save_under = True; theWindowAttributes.border_pixel = carray[1]; theWindowAttributes.background_pixel = carray[7]; /* See if there is something in the .Xdefaults file regarding VOGL/VOGLE. */ if ((smallf = XGetDefault(display, me, "smallfont")) == (char *)NULL) smallf = SMALLFONT; if ((largef = XGetDefault(display, me, "largefont")) == (char *)NULL) largef = LARGEFONT; if ((wintitle = XGetDefault(display, me, "title")) == (char *)NULL) wintitle = (char *)NULL; geom = XGetDefault(display, me, "Geometry"); if (geom != (char *)NULL) { theSizeHints.flags = PSize | PMinSize ; theSizeHints.flags = 0 ; mask = XParseGeometry(geom, &x, &y, &w, &h); if (mask & XValue) theSizeHints.flags |= USPosition; if (mask & YValue) theSizeHints.flags |= USPosition; if (mask & WidthValue) theSizeHints.flags |= USSize; if (mask & HeightValue) theSizeHints.flags |= USSize; if (mask & XNegative) x = DisplayWidth(display, screen) - 2*bw - w + x; if (mask & YNegative) y = DisplayHeight(display, screen) - 2*bw - h + y; } else theSizeHints.flags = PPosition | PSize; if (prefx > -1) { x = prefx; y = prefy; } if (prefxs > -1) { w = prefxs; h = prefys; } if (bw == 0) bw = 4; x -= bw; y -= bw; if (x <= 0) x = 0; if (y <= 0) y = 0; w -= 4 * bw; h -= 4 * bw; if (strcmp(varname,"NOCLOSE")){ theWindowMask = CWBorderPixel|CWBackingStore; winder = XCreateWindow(display, winder, x, y, w, h, bw, (int)depth, InputOutput, CopyFromParent, theWindowMask, &theWindowAttributes ); theSizeHints.x = x; theSizeHints.y = y; theSizeHints.width = w; theSizeHints.height = h; theSizeHints.min_width = w; theSizeHints.max_width = w; theSizeHints.min_height = h; theSizeHints.max_height = h; if (wintitle == (char *)NULL || strlen(wintitle) <= 0) { sprintf(name, "%s %d (win id 0x%x)", me, getpid(), (int)winder); } else { strcpy(name,wintitle); } XSetStandardProperties(display, winder, name, name, None, av, 1, &theSizeHints ); theWMHints.initial_state = NormalState; theWMHints.input = True; theWMHints.flags = StateHint | InputHint; XSetWMHints(display, winder, &theWMHints); theDrawable = (Drawable)winder; /* Create Graphics Context and Drawable */ theGC = XDefaultGC(display, screen); theDrawable = (Drawable)winder; } XSelectInput(display, winder, EV_MASK); XMapRaised(display, winder); XFlush(display); /* Wait for Exposure event. */ /* !! do { !! XNextEvent(display, &event); !! } while (event.type != Expose && event.type != MapNotify); */ /* Set the input Focus to us. */ /* !! if (prefx == -1 && prefxs == -1) !! XSetInputFocus(display, winder, RevertToParent, CurrentTime); */ X11_RESIZE(); vdevice.sizeX = vdevice.sizeY = MIN(h, w); if (strcmp(varname,"NOCLOSE")){ if (back_used) { back_used = 0; X11_backbuf(); } } else{ X11_NEW(); } return(1); } /******************************************************************************/ /* X11_exit cleans up before returning the window to normal. */ int X11_exit(void) { char *varname; varname=getenv("USHDRIVER"); if(varname==NULL) { varname="UNDEFINED"; } if (!strcmp(varname,"NOCLOSE")) { fprintf(stderr,"%s planning on returning to X11 window\n", me); return(1); } XSync(display, 0); if (back_used) { XFreePixmap(display, bbuf); bbuf = 0xffffffff; back_used = 0; } if (font_id != (XFontStruct *)NULL) { XFreeFont(display, font_id); font_id = (XFontStruct *)NULL; } #ifdef NEWCMAP if (colormap != DefaultColormap(display, screen)){ XFreeColormap(display, colormap); colormap = 0; } #endif /* ------------------- !! XUnmapWindow(display, winder); !! XSetCloseDownMode(display,RetainTemporary); !! XSetCloseDownMode(display,DestroyAll); ----------------- */ XDestroyWindow(display, winder); /* ------------------- !! XFreeGC(display, theGC); !! CloseDisplay(display); ----------------- */ XSync(display,0); XFlush(display); XCloseDisplay(display); display = (Display *)NULL; winder = 0; return(1); } /******************************************************************************/ /* * X11_draw * * draws a line from the current graphics position to (x, y). * * Note: (0, 0) is defined as the top left of the window in X (easy * to forget). */ int X11_draw(int x, int y) { XDrawLine(display, theDrawable, theGC, vdevice.cpVx, vdevice.sizeSy - vdevice.cpVy, x, vdevice.sizeSy - y ); if (vdevice.sync) XSync(display, 0); return(UNUSED); } /******************************************************************************/ /* * X11_getkey * * grab a character from the keyboard - blocks until one is there. */ int X11_getkey(void) { char c; do { XNextEvent(display, &event); if (event.type == KeyPress) { if (XLookupString((XKeyEvent *)&event, &c, 1, NULL, NULL) > 0) return((int)c); else return(0); } } while (event.type != KeyPress); return(UNUSED); } /******************************************************************************/ /* * X11_checkkey * * Check if there has been a keyboard key pressed. * and return it if there is. */ int X11_checkkey(void) { char c; if (!XCheckWindowEvent(display, winder, KeyPressMask, &event)) return(0); if (event.type == KeyPress) if (XLookupString((XKeyEvent *)&event, &c, 1, NULL, NULL) > 0) return((int)c); return(0); } /******************************************************************************/ /* * X11_locator * * return the window location of the cursor, plus which mouse button, * if any, has been pressed. */ int X11_locator(int *wx, int *wy) { Window rootw, childw; int x, y ; unsigned int mask; XQueryPointer(display, winder, &rootw, &childw, &x, &y, wx, wy, &mask); *wy = (int)vdevice.sizeSy - *wy; return(mask >> 8); } /******************************************************************************/ int X11_clear(void) { /* Clear the screen (or current buffer )to current colour */ X11_RESIZE(); XSync(display, 0); XSetBackground(display, theGC, colour); XFillRectangle(display, theDrawable, theGC, 0, 0, (unsigned int)vdevice.sizeSx + 1, (unsigned int)vdevice.sizeSy + 1 ); XFlush(display); /* always flush on clear even if vdevice.sync is not on (0) */ return(UNUSED); } /******************************************************************************/ /* set the current drawing color index if ind >= 0.; set the line width in raster units if ind < 0. */ int X11_color(int ind) { static unsigned int line_width=0; /* 0 would be fast line of width 1 */ int line_style = LineSolid; /* If LineOnOffDash or LineDoubleDash, must set dashes */ int cap_style = CapRound; /* else CapNotLast, CapButt, or CapProjecting */ int join_style = JoinRound; /* else JoinMiter or JoinBevel */ if ( ind < 0 ) { if ( ind == -1 ) { ind = 0; } line_width=abs(ind); XSetLineAttributes(display,theGC,line_width, line_style, cap_style, join_style); } else { colour = carray[ind]; XSetForeground(display, theGC, colour); } return(UNUSED); } /******************************************************************************/ /* set the line width in raster units if ind < 0. input units in 1/10000 of X size */ int X11_setlw(int ind) { static unsigned int line_width=0; /* 0 would be fast line of width 1 */ int line_style = LineSolid; /* If LineOnOffDash or LineDoubleDash, must set dashes */ int cap_style = CapRound; /* else CapNotLast, CapButt, or CapProjecting */ int join_style = JoinRound; /* else JoinMiter or JoinBevel */ ind=(float)vdevice.sizeX/10000.0*ind; if ( ind <= -1 ) { ind = 0; } line_width=abs(ind); XSetLineAttributes(display,theGC,line_width, line_style, cap_style, join_style); return(UNUSED); } /******************************************************************************/ /* change index i in the color map to the appropriate r, g, b, value. */ int X11_mapcolor(int i, int r, int g, int b) { int stat; XColor tmp; if (i >= CMAPSIZE) return(-1); /* * For Black and White. * If the index is 0 and r,g,b != 0 then we are remapping black. * If the index != 0 and r,g,b == 0 then we make it black. */ if (vdevice.depth == 1) { if ( GLOBAL_device == X11DEV){ if (i == 0 && (r != 0 || g != 0 || b != 0)) carray[i] = WhitePixel(display, screen); else if (i != 0 && r == 0 && g == 0 && b == 0) carray[i] = BlackPixel(display, screen); } else{ if (i == 0 && (r != 255 || g != 255 || b != 255)) carray[i] = BlackPixel(display, screen); else if (i != 0 && r == 255 && g == 255 && b == 255) carray[i] = WhitePixel(display, screen); } } else { tmp.red = (unsigned short)(r / 255.0 * 65535); tmp.green = (unsigned short)(g / 255.0 * 65535); tmp.blue = (unsigned short)(b / 255.0 * 65535); tmp.flags = 0; tmp.pixel = (unsigned long)i; if ((stat = XAllocColor(display, colormap, &tmp)) == 0) { fprintf(stderr, "XAllocColor failed (status = %d)\n", stat); /* exit(1); */ } else{ carray[i] = tmp.pixel; } } if (vdevice.sync){ XFlush(display); } return(0); } /******************************************************************************/ /* Set up a hardware font. Return 1 on success 0 otherwise. */ int X11_font(char *fontfile) { XGCValues xgcvals; if (font_id != (XFontStruct *)NULL) XFreeFont(display, font_id); if (strcmp(fontfile, "small") == 0) { if ((font_id = XLoadQueryFont(display, smallf)) == (XFontStruct *)NULL) { fprintf(stderr, "%s X11.c couldn't open small font '%s'\n", me, smallf); fprintf(stderr, "You'll have to redefine it....\n"); return(0); } else fontfile = smallf; } else if (strcmp(fontfile, "large") == 0) { if ((font_id = XLoadQueryFont(display, largef)) == (XFontStruct *)NULL) { fprintf(stderr, "%s X11.c couldn't open large font '%s'\n", me, largef); fprintf(stderr, "You'll have to redefine it....\n"); return(0); } fontfile = largef; } else { if ((font_id = XLoadQueryFont(display, fontfile)) == (XFontStruct *)NULL) { fprintf(stderr, "%s X11.c couldn't open fontfile '%s'\n", me, fontfile); return(0); } } vdevice.hheight = font_id->max_bounds.ascent + font_id->max_bounds.descent; vdevice.hwidth = font_id->max_bounds.width; xgcvals.font = XLoadFont(display, fontfile); XChangeGC(display, theGC, GCFont, &xgcvals); return(1); } /******************************************************************************/ /* outputs one char - is more complicated for other devices */ int X11_char(char c) { /* char *s = " "; */ char s[2]; s[0] = c; s[1]='\0'; XDrawString(display, theDrawable, theGC, vdevice.cpVx, (int)(vdevice.sizeSy - vdevice.cpVy), s, 1); if (vdevice.sync) XFlush(display); return(UNUSED); } /******************************************************************************/ int X11_string(char s[]) { /* Display a string at the current drawing position. */ XDrawString(display, theDrawable, theGC, vdevice.cpVx, (int)(vdevice.sizeSy - vdevice.cpVy), s, strlen(s)); if (vdevice.sync) XFlush(display); return(UNUSED); } /******************************************************************************/ int X11_fill(int n, int x[], int y[]) { /* fill a polygon */ char buf[BUFSIZ]; XPoint plist[MAXVERTS]; int i; if (n > MAXVERTS) { sprintf(buf, "%s: more than %d points in a polygon", me,MAXVERTS); vogle_verror(buf); } for (i = 0; i < n; i++) { plist[i].x = x[i]; plist[i].y = vdevice.sizeSy - y[i]; } XFillPolygon(display, theDrawable, theGC, plist, n, Nonconvex, CoordModeOrigin); vdevice.cpVx = x[n-1]; vdevice.cpVy = y[n-1]; if (vdevice.sync) XFlush(display); return(UNUSED); } /******************************************************************************/ /* Set up double buffering by allocating the back buffer and setting drawing into it. */ int X11_backbuf(void) { if (!back_used) bbuf = XCreatePixmap(display, (Drawable)winder, (unsigned int)vdevice.sizeSx + 1, (unsigned int)vdevice.sizeSy + 1, (unsigned int)vdevice.depth ); theDrawable = (Drawable)bbuf; back_used = 1; return(1); } /******************************************************************************/ /* Swap the back and from buffers. (Really, just copy the back buffer to the screen). */ int X11_swapbuf(void) { XCopyArea(display, theDrawable, winder, theGC, 0, 0, (unsigned int)vdevice.sizeSx + 1, (unsigned int)vdevice.sizeSy + 1, 0, 0 ); XSync(display, 0); /* Not XFlush */ return(UNUSED); } /******************************************************************************/ int X11_frontbuf(void) { /* Make sure we draw to the screen. */ theDrawable = (Drawable)winder; return(UNUSED); } /******************************************************************************/ int X11_sync(void) { /* Syncronise the display with what we think has been sent to it... */ XSync(display, 0); return(UNUSED); } /******************************************************************************/ /* * the device entry */ static DevEntry X11dev = { "X11", "large", "small", X11_backbuf, X11_char, X11_checkkey, X11_clear, X11_color, X11_draw, X11_exit, X11_fill, X11_font, X11_frontbuf, X11_getkey, X11_init, X11_locator, X11_mapcolor, X11_setlw, /* Set line width */ X11_string, X11_swapbuf, X11_sync }; /******************************************************************************/ /* * the device entry */ static DevEntry x11dev = { "x11", "large", "small", X11_backbuf, X11_char, X11_checkkey, X11_clear, X11_color, X11_draw, X11_exit, X11_fill, X11_font, X11_frontbuf, X11_getkey, X11_init, X11_locator, X11_mapcolor, X11_setlw, /* Set line width */ X11_string, X11_swapbuf, X11_sync }; /******************************************************************************/ int _X11_devcpy() { /* copy the X11 device into vdevice.dev. */ vdevice.dev = X11dev; GLOBAL_device = X11DEV; return(UNUSED); } /******************************************************************************/ int _x11_devcpy() { /* copy the x11 device into vdevice.dev. */ vdevice.dev = x11dev; GLOBAL_device = x11DEV; return(UNUSED); } /******************************************************************************/ int myhandler(Display *display,XErrorEvent *myerr) { /* myhandler */ char msg[80]; XGetErrorText(display,myerr->error_code,msg,80); fprintf(stderr,"*vogle* STOPPED code %s\n",msg); exit(1); return(UNUSED); } /******************************************************************************/