diff options
author | rsc <devnull@localhost> | 2006-06-25 19:18:39 +0000 |
---|---|---|
committer | rsc <devnull@localhost> | 2006-06-25 19:18:39 +0000 |
commit | c66b52501b630f6c0cdee1ed6cf81ad153e1183f (patch) | |
tree | ec935772fd77c3aa2337598c25a6c71ba84a9275 /src/cmd/devdraw/x11-init.c | |
parent | f97f53744067400bec4dbd7aae5eafde54e99b7f (diff) | |
download | plan9port-c66b52501b630f6c0cdee1ed6cf81ad153e1183f.tar.gz plan9port-c66b52501b630f6c0cdee1ed6cf81ad153e1183f.tar.bz2 plan9port-c66b52501b630f6c0cdee1ed6cf81ad153e1183f.zip |
new draw server
Diffstat (limited to 'src/cmd/devdraw/x11-init.c')
-rw-r--r-- | src/cmd/devdraw/x11-init.c | 788 |
1 files changed, 788 insertions, 0 deletions
diff --git a/src/cmd/devdraw/x11-init.c b/src/cmd/devdraw/x11-init.c new file mode 100644 index 00000000..9820ea24 --- /dev/null +++ b/src/cmd/devdraw/x11-init.c @@ -0,0 +1,788 @@ +/* + * Some of the stuff in this file is not X-dependent and should be elsewhere. + */ +#include <u.h> +#include "x11-inc.h" +#include <libc.h> +#include <draw.h> +#include <memdraw.h> +#include <keyboard.h> +#include <mouse.h> +#include <cursor.h> +#include "x11-memdraw.h" + +static int parsewinsize(char*, Rectangle*, int*); + +static void plan9cmap(void); +static int setupcmap(XWindow); +static XGC xgc(XDrawable, int, int); + +Xprivate _x; + +static int +xerror(XDisplay *d, XErrorEvent *e) +{ + char buf[200]; + + if(e->request_code == 42) /* XSetInputFocus */ + return 0; + if(e->request_code == 18) /* XChangeProperty */ + return 0; + + print("X error: error_code=%d, request_code=%d, minor=%d disp=%p\n", + e->error_code, e->request_code, e->minor_code, d); + XGetErrorText(d, e->error_code, buf, sizeof buf); + print("%s\n", buf); + return 0; +} + +static int +xioerror(XDisplay *d) +{ + /*print("X I/O error\n"); */ + sysfatal("X I/O error\n"); + abort(); + return -1; +} + + +Memimage* +_xattach(char *label, char *winsize) +{ + char *argv[2], *disp; + int i, havemin, height, mask, n, width, x, xrootid, y; + Rectangle r; + XClassHint classhint; + XDrawable pmid; + XPixmapFormatValues *pfmt; + XScreen *xscreen; + XSetWindowAttributes attr; + XSizeHints normalhint; + XTextProperty name; + XVisualInfo xvi; + XWindow xrootwin; + XWindowAttributes wattr; + XWMHints hint; + Atom atoms[2]; + + /* + if(XInitThreads() == 0){ + fprint(2, "XInitThreads failed\n"); + abort(); + } + */ + + /* + * Connect to X server. + */ + _x.display = XOpenDisplay(NULL); + if(_x.display == nil){ + disp = getenv("DISPLAY"); + werrstr("XOpenDisplay %s: %r", disp ? disp : ":0"); + free(disp); + return nil; + } + _x.fd = ConnectionNumber(_x.display); + XSetErrorHandler(xerror); + XSetIOErrorHandler(xioerror); + xrootid = DefaultScreen(_x.display); + xrootwin = DefaultRootWindow(_x.display); + + /* + * Figure out underlying screen format. + */ + if(XMatchVisualInfo(_x.display, xrootid, 16, TrueColor, &xvi) + || XMatchVisualInfo(_x.display, xrootid, 16, DirectColor, &xvi)){ + _x.vis = xvi.visual; + _x.depth = 16; + } + else + if(XMatchVisualInfo(_x.display, xrootid, 15, TrueColor, &xvi) + || XMatchVisualInfo(_x.display, xrootid, 15, DirectColor, &xvi)){ + _x.vis = xvi.visual; + _x.depth = 15; + } + else + if(XMatchVisualInfo(_x.display, xrootid, 24, TrueColor, &xvi) + || XMatchVisualInfo(_x.display, xrootid, 24, DirectColor, &xvi)){ + _x.vis = xvi.visual; + _x.depth = 24; + } + else + if(XMatchVisualInfo(_x.display, xrootid, 8, PseudoColor, &xvi) + || XMatchVisualInfo(_x.display, xrootid, 8, StaticColor, &xvi)){ + if(_x.depth > 8){ + werrstr("can't deal with colormapped depth %d screens", + _x.depth); + goto err0; + } + _x.vis = xvi.visual; + _x.depth = 8; + } + else{ + _x.depth = DefaultDepth(_x.display, xrootid); + if(_x.depth != 8){ + werrstr("can't understand depth %d screen", _x.depth); + goto err0; + } + _x.vis = DefaultVisual(_x.display, xrootid); + } + + if(DefaultDepth(_x.display, xrootid) == _x.depth) + _x.usetable = 1; + + /* + * _x.depth is only the number of significant pixel bits, + * not the total number of pixel bits. We need to walk the + * display list to find how many actual bits are used + * per pixel. + */ + _x.chan = 0; + pfmt = XListPixmapFormats(_x.display, &n); + for(i=0; i<n; i++){ + if(pfmt[i].depth == _x.depth){ + switch(pfmt[i].bits_per_pixel){ + case 1: /* untested */ + _x.chan = GREY1; + break; + case 2: /* untested */ + _x.chan = GREY2; + break; + case 4: /* untested */ + _x.chan = GREY4; + break; + case 8: + _x.chan = CMAP8; + break; + case 15: + _x.chan = RGB15; + break; + case 16: /* how to tell RGB15? */ + _x.chan = RGB16; + break; + case 24: /* untested (impossible?) */ + _x.chan = RGB24; + break; + case 32: + _x.chan = XRGB32; + break; + } + } + } + if(_x.chan == 0){ + werrstr("could not determine screen pixel format"); + goto err0; + } + + /* + * Set up color map if necessary. + */ + xscreen = DefaultScreenOfDisplay(_x.display); + _x.cmap = DefaultColormapOfScreen(xscreen); + if(_x.vis->class != StaticColor){ + plan9cmap(); + setupcmap(xrootwin); + } + + /* + * We get to choose the initial rectangle size. + * This is arbitrary. In theory we should read the + * command line and allow the traditional X options. + */ + mask = 0; + x = 0; + y = 0; + if(winsize && winsize[0]){ + if(parsewinsize(winsize, &r, &havemin) < 0) + sysfatal("%r"); + }else{ + /* + * Parse the various X resources. Thanks to Peter Canning. + */ + char *screen_resources, *display_resources, *geom, + *geomrestype, *home, *file; + XrmDatabase database; + XrmValue geomres; + + database = XrmGetDatabase(_x.display); + screen_resources = XScreenResourceString(xscreen); + if(screen_resources != nil){ + XrmCombineDatabase(XrmGetStringDatabase(screen_resources), &database, False); + XFree(screen_resources); + } + + display_resources = XResourceManagerString(_x.display); + if(display_resources == nil){ + home = getenv("HOME"); + if(home!=nil && (file=smprint("%s/.Xdefaults", home)) != nil){ + XrmCombineFileDatabase(file, &database, False); + free(file); + } + free(home); + }else + XrmCombineDatabase(XrmGetStringDatabase(display_resources), &database, False); + + geom = smprint("%s.geometry", label); + if(geom && XrmGetResource(database, geom, nil, &geomrestype, &geomres)) + mask = XParseGeometry(geomres.addr, &x, &y, (uint*)&width, (uint*)&height); + free(geom); + + if((mask & WidthValue) && (mask & HeightValue)){ + r = Rect(0, 0, width, height); + }else{ + r = Rect(0, 0, WidthOfScreen(xscreen)*3/4, + HeightOfScreen(xscreen)*3/4); + if(Dx(r) > Dy(r)*3/2) + r.max.x = r.min.x + Dy(r)*3/2; + if(Dy(r) > Dx(r)*3/2) + r.max.y = r.min.y + Dx(r)*3/2; + } + if(mask & XNegative){ + x += WidthOfScreen(xscreen); + } + if(mask & YNegative){ + y += HeightOfScreen(xscreen); + } + havemin = 0; + } + + memset(&attr, 0, sizeof attr); + attr.colormap = _x.cmap; + attr.background_pixel = ~0; + attr.border_pixel = 0; + _x.drawable = XCreateWindow( + _x.display, /* display */ + xrootwin, /* parent */ + x, /* x */ + y, /* y */ + Dx(r), /* width */ + Dy(r), /* height */ + 0, /* border width */ + _x.depth, /* depth */ + InputOutput, /* class */ + _x.vis, /* visual */ + /* valuemask */ + CWBackPixel|CWBorderPixel|CWColormap, + &attr /* attributes (the above aren't?!) */ + ); + + /* + * Label and other properties required by ICCCCM. + */ + memset(&name, 0, sizeof name); + if(label == nil) + label = "pjw-face-here"; + name.value = (uchar*)label; + name.encoding = XA_STRING; + name.format = 8; + name.nitems = strlen((char*)name.value); + + memset(&normalhint, 0, sizeof normalhint); + normalhint.flags = PSize|PMaxSize; + if(winsize && winsize[0]){ + normalhint.flags &= ~PSize; + normalhint.flags |= USSize; + normalhint.width = Dx(r); + normalhint.height = Dy(r); + }else{ + if((mask & WidthValue) && (mask & HeightValue)){ + normalhint.flags &= ~PSize; + normalhint.flags |= USSize; + normalhint.width = width; + normalhint.height = height; + } + if((mask & WidthValue) && (mask & HeightValue)){ + normalhint.flags |= USPosition; + normalhint.x = x; + normalhint.y = y; + } + } + + normalhint.max_width = WidthOfScreen(xscreen); + normalhint.max_height = HeightOfScreen(xscreen); + + memset(&hint, 0, sizeof hint); + hint.flags = InputHint|StateHint; + hint.input = 1; + hint.initial_state = NormalState; + + memset(&classhint, 0, sizeof classhint); + classhint.res_name = label; + classhint.res_class = label; + + argv[0] = label; + argv[1] = nil; + + XSetWMProperties( + _x.display, /* display */ + _x.drawable, /* window */ + &name, /* XA_WM_NAME property */ + &name, /* XA_WM_ICON_NAME property */ + argv, /* XA_WM_COMMAND */ + 1, /* argc */ + &normalhint, /* XA_WM_NORMAL_HINTS */ + &hint, /* XA_WM_HINTS */ + &classhint /* XA_WM_CLASSHINTS */ + ); + XFlush(_x.display); + + if(havemin){ + XWindowChanges ch; + + memset(&ch, 0, sizeof ch); + ch.x = r.min.x; + ch.y = r.min.y; + XConfigureWindow(_x.display, _x.drawable, CWX|CWY, &ch); + /* + * Must pretend origin is 0,0 for X. + */ + r = Rect(0,0,Dx(r),Dy(r)); + } + /* + * Look up clipboard atom. + */ + _x.clipboard = XInternAtom(_x.display, "CLIPBOARD", False); + _x.utf8string = XInternAtom(_x.display, "UTF8_STRING", False); + _x.targets = XInternAtom(_x.display, "TARGETS", False); + _x.text = XInternAtom(_x.display, "TEXT", False); + _x.compoundtext = XInternAtom(_x.display, "COMPOUND_TEXT", False); + _x.takefocus = XInternAtom(_x.display, "WM_TAKE_FOCUS", False); + _x.losefocus = XInternAtom(_x.display, "_9WM_LOSE_FOCUS", False); + _x.wmprotos = XInternAtom(_x.display, "WM_PROTOCOLS", False); + + atoms[0] = _x.takefocus; + atoms[1] = _x.losefocus; + XChangeProperty(_x.display, _x.drawable, _x.wmprotos, XA_ATOM, 32, + PropModeReplace, (uchar*)atoms, 2); + + /* + * Put the window on the screen, check to see what size we actually got. + */ + XMapWindow(_x.display, _x.drawable); + XSync(_x.display, False); + + if(!XGetWindowAttributes(_x.display, _x.drawable, &wattr)) + fprint(2, "XGetWindowAttributes failed\n"); + else if(wattr.width && wattr.height){ + if(wattr.width != Dx(r) || wattr.height != Dy(r)){ + r.max.x = wattr.width; + r.max.y = wattr.height; + } + }else + fprint(2, "XGetWindowAttributes: bad attrs\n"); + + /* + * Allocate our local backing store. + */ + _x.screenr = r; + _x.screenpm = XCreatePixmap(_x.display, _x.drawable, Dx(r), Dy(r), _x.depth); + _x.nextscreenpm = _x.screenpm; + _x.screenimage = _xallocmemimage(r, _x.chan, _x.screenpm); + + /* + * Allocate some useful graphics contexts for the future. + */ + _x.gcfill = xgc(_x.screenpm, FillSolid, -1); + _x.gccopy = xgc(_x.screenpm, -1, -1); + _x.gcsimplesrc = xgc(_x.screenpm, FillStippled, -1); + _x.gczero = xgc(_x.screenpm, -1, -1); + _x.gcreplsrc = xgc(_x.screenpm, FillTiled, -1); + + pmid = XCreatePixmap(_x.display, _x.drawable, 1, 1, 1); + _x.gcfill0 = xgc(pmid, FillSolid, 0); + _x.gccopy0 = xgc(pmid, -1, -1); + _x.gcsimplesrc0 = xgc(pmid, FillStippled, -1); + _x.gczero0 = xgc(pmid, -1, -1); + _x.gcreplsrc0 = xgc(pmid, FillTiled, -1); + XFreePixmap(_x.display, pmid); + + return _x.screenimage; + +err0: + /* + * Should do a better job of cleaning up here. + */ + XCloseDisplay(_x.display); + return nil; +} + +int +_xsetlabel(char *label) +{ + XTextProperty name; + + /* + * Label and other properties required by ICCCCM. + */ + memset(&name, 0, sizeof name); + if(label == nil) + label = "pjw-face-here"; + name.value = (uchar*)label; + name.encoding = XA_STRING; + name.format = 8; + name.nitems = strlen((char*)name.value); + + XSetWMProperties( + _x.display, /* display */ + _x.drawable, /* window */ + &name, /* XA_WM_NAME property */ + &name, /* XA_WM_ICON_NAME property */ + nil, /* XA_WM_COMMAND */ + 0, /* argc */ + nil, /* XA_WM_NORMAL_HINTS */ + nil, /* XA_WM_HINTS */ + nil /* XA_WM_CLASSHINTS */ + ); + XFlush(_x.display); + return 0; +} + +/* + * Create a GC with a particular fill style and XXX. + * Disable generation of GraphicsExpose/NoExpose events in the GC. + */ +static XGC +xgc(XDrawable d, int fillstyle, int foreground) +{ + XGC gc; + XGCValues v; + + memset(&v, 0, sizeof v); + v.function = GXcopy; + v.graphics_exposures = False; + gc = XCreateGC(_x.display, d, GCFunction|GCGraphicsExposures, &v); + if(fillstyle != -1) + XSetFillStyle(_x.display, gc, fillstyle); + if(foreground != -1) + XSetForeground(_x.display, gc, 0); + return gc; +} + + +/* + * Initialize map with the Plan 9 rgbv color map. + */ +static void +plan9cmap(void) +{ + int r, g, b, cr, cg, cb, v, num, den, idx, v7, idx7; + static int once; + + if(once) + return; + once = 1; + + for(r=0; r!=4; r++) + for(g = 0; g != 4; g++) + for(b = 0; b!=4; b++) + for(v = 0; v!=4; v++){ + den=r; + if(g > den) + den=g; + if(b > den) + den=b; + /* divide check -- pick grey shades */ + if(den==0) + cr=cg=cb=v*17; + else { + num=17*(4*den+v); + cr=r*num/den; + cg=g*num/den; + cb=b*num/den; + } + idx = r*64 + v*16 + ((g*4 + b + v - r) & 15); + _x.map[idx].red = cr*0x0101; + _x.map[idx].green = cg*0x0101; + _x.map[idx].blue = cb*0x0101; + _x.map[idx].pixel = idx; + _x.map[idx].flags = DoRed|DoGreen|DoBlue; + + v7 = v >> 1; + idx7 = r*32 + v7*16 + g*4 + b; + if((v & 1) == v7){ + _x.map7to8[idx7][0] = idx; + if(den == 0) { /* divide check -- pick grey shades */ + cr = ((255.0/7.0)*v7)+0.5; + cg = cr; + cb = cr; + } + else { + num=17*15*(4*den+v7*2)/14; + cr=r*num/den; + cg=g*num/den; + cb=b*num/den; + } + _x.map7[idx7].red = cr*0x0101; + _x.map7[idx7].green = cg*0x0101; + _x.map7[idx7].blue = cb*0x0101; + _x.map7[idx7].pixel = idx7; + _x.map7[idx7].flags = DoRed|DoGreen|DoBlue; + } + else + _x.map7to8[idx7][1] = idx; + } +} + +/* + * Initialize and install the rgbv color map as a private color map + * for this application. It gets the best colors when it has the + * cursor focus. + * + * We always choose the best depth possible, but that might not + * be the default depth. On such "suboptimal" systems, we have to allocate an + * empty color map anyway, according to Axel Belinfante. + */ +static int +setupcmap(XWindow w) +{ + char buf[30]; + int i; + u32int p, pp; + XColor c; + + if(_x.depth <= 1) + return 0; + + if(_x.depth >= 24) { + if(_x.usetable == 0) + _x.cmap = XCreateColormap(_x.display, w, _x.vis, AllocNone); + + /* + * The pixel value returned from XGetPixel needs to + * be converted to RGB so we can call rgb2cmap() + * to translate between 24 bit X and our color. Unfortunately, + * the return value appears to be display server endian + * dependant. Therefore, we run some heuristics to later + * determine how to mask the int value correctly. + * Yeah, I know we can look at _x.vis->byte_order but + * some displays say MSB even though they run on LSB. + * Besides, this is more anal. + */ + c = _x.map[19]; /* known to have different R, G, B values */ + if(!XAllocColor(_x.display, _x.cmap, &c)){ + werrstr("XAllocColor: %r"); + return -1; + } + p = c.pixel; + pp = rgb2cmap((p>>16)&0xff,(p>>8)&0xff,p&0xff); + if(pp != _x.map[19].pixel) { + /* check if endian is other way */ + pp = rgb2cmap(p&0xff,(p>>8)&0xff,(p>>16)&0xff); + if(pp != _x.map[19].pixel){ + werrstr("cannot detect X server byte order"); + return -1; + } + + switch(_x.chan){ + case RGB24: + _x.chan = BGR24; + break; + case XRGB32: + _x.chan = XBGR32; + break; + default: + werrstr("cannot byteswap channel %s", + chantostr(buf, _x.chan)); + break; + } + } + }else if(_x.vis->class == TrueColor || _x.vis->class == DirectColor){ + /* + * Do nothing. We have no way to express a + * mixed-endian 16-bit screen, so pretend they don't exist. + */ + if(_x.usetable == 0) + _x.cmap = XCreateColormap(_x.display, w, _x.vis, AllocNone); + }else if(_x.vis->class == PseudoColor){ + if(_x.usetable == 0){ + _x.cmap = XCreateColormap(_x.display, w, _x.vis, AllocAll); + XStoreColors(_x.display, _x.cmap, _x.map, 256); + for(i = 0; i < 256; i++){ + _x.tox11[i] = i; + _x.toplan9[i] = i; + } + }else{ + for(i = 0; i < 128; i++){ + c = _x.map7[i]; + if(!XAllocColor(_x.display, _x.cmap, &c)){ + werrstr("can't allocate colors in 7-bit map"); + return -1; + } + _x.tox11[_x.map7to8[i][0]] = c.pixel; + _x.tox11[_x.map7to8[i][1]] = c.pixel; + _x.toplan9[c.pixel] = _x.map7to8[i][0]; + } + } + }else{ + werrstr("unsupported visual class %d", _x.vis->class); + return -1; + } + return 0; +} + +void +_flushmemscreen(Rectangle r) +{ + if(_x.nextscreenpm != _x.screenpm){ + qlock(&_x.screenlock); + XSync(_x.display, False); + XFreePixmap(_x.display, _x.screenpm); + _x.screenpm = _x.nextscreenpm; + qunlock(&_x.screenlock); + } + + if(r.min.x >= r.max.x || r.min.y >= r.max.y) + return; + XCopyArea(_x.display, _x.screenpm, _x.drawable, _x.gccopy, r.min.x, r.min.y, + Dx(r), Dy(r), r.min.x, r.min.y); + XFlush(_x.display); +} + +void +_xexpose(XEvent *e) +{ + XExposeEvent *xe; + Rectangle r; + + qlock(&_x.screenlock); + if(_x.screenpm != _x.nextscreenpm){ + qunlock(&_x.screenlock); + return; + } + xe = (XExposeEvent*)e; + r.min.x = xe->x; + r.min.y = xe->y; + r.max.x = xe->x+xe->width; + r.max.y = xe->y+xe->height; + XCopyArea(_x.display, _x.screenpm, _x.drawable, _x.gccopy, r.min.x, r.min.y, + Dx(r), Dy(r), r.min.x, r.min.y); + XSync(_x.display, False); + qunlock(&_x.screenlock); +} + +int +_xdestroy(XEvent *e) +{ + XDestroyWindowEvent *xe; + + xe = (XDestroyWindowEvent*)e; + if(xe->window == _x.drawable){ + _x.destroyed = 1; + return 1; + } + return 0; +} + +int +_xconfigure(XEvent *e) +{ + Rectangle r; + XConfigureEvent *xe = (XConfigureEvent*)e; + + if(xe->width == Dx(_x.screenr) && xe->height == Dy(_x.screenr)) + return 0; + if(xe->width==0 || xe->height==0) + fprint(2, "ignoring resize to %dx%d\n", xe->width, xe->height); + r = Rect(0, 0, xe->width, xe->height); + qlock(&_x.screenlock); + if(_x.screenpm != _x.nextscreenpm){ + XCopyArea(_x.display, _x.screenpm, _x.drawable, _x.gccopy, r.min.x, r.min.y, + Dx(r), Dy(r), r.min.x, r.min.y); + XSync(_x.display, False); + } + qunlock(&_x.screenlock); + _x.newscreenr = r; + return 1; +} + +int +_xreplacescreenimage(void) +{ + Memimage *m; + XDrawable pixmap; + Rectangle r; + + r = _x.newscreenr; + if(eqrect(_x.screenr, r)) + return 0; + + pixmap = XCreatePixmap(_x.display, _x.drawable, Dx(r), Dy(r), _x.depth); + m = _xallocmemimage(r, _x.chan, pixmap); + if(_x.nextscreenpm != _x.screenpm) + XFreePixmap(_x.display, _x.nextscreenpm); + _x.nextscreenpm = pixmap; + _x.screenr = r; + _drawreplacescreenimage(m); + return 1; +} + +static int +parsewinsize(char *s, Rectangle *r, int *havemin) +{ + char c, *os; + int i, j, k, l; + + os = s; + *havemin = 0; + *r = Rect(0,0,0,0); + if(!isdigit((uchar)*s)) + goto oops; + i = strtol(s, &s, 0); + if(*s == 'x'){ + s++; + if(!isdigit((uchar)*s)) + goto oops; + j = strtol(s, &s, 0); + r->max.x = i; + r->max.y = j; + if(*s == 0) + return 0; + if(*s != '@') + goto oops; + + s++; + if(!isdigit((uchar)*s)) + goto oops; + i = strtol(s, &s, 0); + if(*s != ',' && *s != ' ') + goto oops; + s++; + if(!isdigit((uchar)*s)) + goto oops; + j = strtol(s, &s, 0); + if(*s != 0) + goto oops; + *r = rectaddpt(*r, Pt(i,j)); + *havemin = 1; + return 0; + } + + c = *s; + if(c != ' ' && c != ',') + goto oops; + s++; + if(!isdigit((uchar)*s)) + goto oops; + j = strtol(s, &s, 0); + if(*s != c) + goto oops; + s++; + if(!isdigit((uchar)*s)) + goto oops; + k = strtol(s, &s, 0); + if(*s != c) + goto oops; + s++; + if(!isdigit((uchar)*s)) + goto oops; + l = strtol(s, &s, 0); + if(*s != 0) + goto oops; + *r = Rect(i,j,k,l); + *havemin = 1; + return 0; + +oops: + werrstr("bad syntax in window size '%s'", os); + return -1; +} |