#include #include #include #include #include #include "dat.h" #include "fns.h" #include "inc.h" #include "x.h" void xclose(void); static void plan9cmap(void); static int setupcmap(XWindow); static XGC xgc(XDrawable, int, int); Xconn xconn; 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; /* * BadDrawable happens in apps that get resized a LOT, * e.g. when KDE is configured to resize continuously * during a window drag. */ if(e->error_code == 9) /* BadDrawable */ return 0; fprint(2, "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); fprint(2, "%s\n", buf); return 0; } static int xioerror(XDisplay *d) { /*print("X I/O error\n"); */ exit(0); /*sysfatal("X I/O error\n");*/ abort(); return -1; } int xfd(void) { return xconn.fd; } /* * Connect to the X server. */ int xinit(void) { char *disp; int i, n, xrootid; XDrawable pmid; XPixmapFormatValues *pfmt; XVisualInfo xvi; if(xconn.display != nil) /* already connected */ return 0; /* * Connect to X server. */ xconn.display = XOpenDisplay(NULL); if(xconn.display == nil){ disp = getenv("DISPLAY"); werrstr("XOpenDisplay %s: %r", disp ? disp : ":0"); return -1; } xconn.fd = ConnectionNumber(xconn.display); XSetErrorHandler(xerror); XSetIOErrorHandler(xioerror); xrootid = DefaultScreen(xconn.display); xconn.root = DefaultRootWindow(xconn.display); /* * Figure out underlying screen format. */ if(XMatchVisualInfo(xconn.display, xrootid, 16, TrueColor, &xvi) || XMatchVisualInfo(xconn.display, xrootid, 16, DirectColor, &xvi)){ xconn.vis = xvi.visual; xconn.depth = 16; } else if(XMatchVisualInfo(xconn.display, xrootid, 15, TrueColor, &xvi) || XMatchVisualInfo(xconn.display, xrootid, 15, DirectColor, &xvi)){ xconn.vis = xvi.visual; xconn.depth = 15; } else if(XMatchVisualInfo(xconn.display, xrootid, 24, TrueColor, &xvi) || XMatchVisualInfo(xconn.display, xrootid, 24, DirectColor, &xvi)){ xconn.vis = xvi.visual; xconn.depth = 24; } else if(XMatchVisualInfo(xconn.display, xrootid, 8, PseudoColor, &xvi) || XMatchVisualInfo(xconn.display, xrootid, 8, StaticColor, &xvi)){ if(xconn.depth > 8){ werrstr("can't deal with colormapped depth %d screens", xconn.depth); xclose(); return -1; } xconn.vis = xvi.visual; xconn.depth = 8; } else{ xconn.depth = DefaultDepth(xconn.display, xrootid); if(xconn.depth != 8){ werrstr("can't understand depth %d screen", xconn.depth); xclose(); return -1; } xconn.vis = DefaultVisual(xconn.display, xrootid); } if(DefaultDepth(xconn.display, xrootid) == xconn.depth) xconn.usetable = 1; /* * xconn.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. */ xconn.chan = 0; pfmt = XListPixmapFormats(xconn.display, &n); for(i=0; iclass != StaticColor){ plan9cmap(); setupcmap(xconn.root); } /* * Set up screen rectangle. */ xconn.screenrect = Rect(0, 0, WidthOfScreen(xconn.screen), HeightOfScreen(xconn.screen)); /* * Set up InputOnly window for gcs, pixmaps and snarf. */ xconn.w = XCreateWindow( xconn.display, /* display */ xconn.root, /* parent */ 0, /* x */ 0, /* y */ 1, /* width */ 1, /* height */ 0, /* border width */ 0, /* depth */ InputOnly, /* class */ xconn.vis, /* visual */ 0, /* valuemask */ nil /* attributes (the above aren't?!) */ ); /* * Allocate some useful graphics contexts for the future. */ pmid = XCreatePixmap(xconn.display, xconn.w, 1, 1, xconn.depth); xconn.gcfill = xgc(pmid, FillSolid, -1); xconn.gccopy = xgc(pmid, -1, -1); xconn.gcsimplesrc = xgc(pmid, FillStippled, -1); xconn.gczero = xgc(pmid, -1, -1); xconn.gcreplsrc = xgc(pmid, FillTiled, -1); XFreePixmap(xconn.display, pmid); pmid = XCreatePixmap(xconn.display, xconn.w, 1, 1, 1); xconn.gcfill0 = xgc(pmid, FillSolid, 0); xconn.gccopy0 = xgc(pmid, -1, -1); xconn.gcsimplesrc0 = xgc(pmid, FillStippled, -1); xconn.gczero0 = xgc(pmid, -1, -1); xconn.gcreplsrc0 = xgc(pmid, FillTiled, -1); XFreePixmap(xconn.display, pmid); xinitclipboard(); return 0; } void xclose(void) { /* * Should do a better job of cleaning up here. */ /* * Clean up gcs and input window. */ XDestroyWindow(xconn.display, xconn.w); XFreeGC(xconn.display, xconn.gccopy); XFreeGC(xconn.display, xconn.gccopy0); XFreeGC(xconn.display, xconn.gcfill); XFreeGC(xconn.display, xconn.gcfill0); XFreeGC(xconn.display, xconn.gcreplsrc); XFreeGC(xconn.display, xconn.gcreplsrc0); XFreeGC(xconn.display, xconn.gcsimplesrc); XFreeGC(xconn.display, xconn.gcsimplesrc0); XFreeGC(xconn.display, xconn.gczero); XFreeGC(xconn.display, xconn.gczero0); XCloseDisplay(xconn.display); } /* * 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); xconn.map[idx].red = cr*0x0101; xconn.map[idx].green = cg*0x0101; xconn.map[idx].blue = cb*0x0101; xconn.map[idx].pixel = idx; xconn.map[idx].flags = DoRed|DoGreen|DoBlue; v7 = v >> 1; idx7 = r*32 + v7*16 + g*4 + b; if((v & 1) == v7){ xconn.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; } xconn.map7[idx7].red = cr*0x0101; xconn.map7[idx7].green = cg*0x0101; xconn.map7[idx7].blue = cb*0x0101; xconn.map7[idx7].pixel = idx7; xconn.map7[idx7].flags = DoRed|DoGreen|DoBlue; } else xconn.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(xconn.depth <= 1) return 0; if(xconn.depth >= 24) { if(xconn.usetable == 0) xconn.cmap = XCreateColormap(xconn.display, w, xconn.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 xconn.vis->byte_order but * some displays say MSB even though they run on LSB. * Besides, this is more anal. */ c = xconn.map[19]; /* known to have different R, G, B values */ if(!XAllocColor(xconn.display, xconn.cmap, &c)){ werrstr("XAllocColor: %r"); return -1; } p = c.pixel; pp = rgb2cmap((p>>16)&0xff,(p>>8)&0xff,p&0xff); if(pp != xconn.map[19].pixel) { /* check if endian is other way */ pp = rgb2cmap(p&0xff,(p>>8)&0xff,(p>>16)&0xff); if(pp != xconn.map[19].pixel){ werrstr("cannot detect X server byte order"); return -1; } switch(xconn.chan){ case RGB24: xconn.chan = BGR24; break; case XRGB32: xconn.chan = XBGR32; break; default: werrstr("cannot byteswap channel %s", chantostr(buf, xconn.chan)); break; } } }else if(xconn.vis->class == TrueColor || xconn.vis->class == DirectColor){ /* * Do nothing. We have no way to express a * mixed-endian 16-bit screen, so pretend they don't exist. */ if(xconn.usetable == 0) xconn.cmap = XCreateColormap(xconn.display, w, xconn.vis, AllocNone); }else if(xconn.vis->class == PseudoColor){ if(xconn.usetable == 0){ xconn.cmap = XCreateColormap(xconn.display, w, xconn.vis, AllocAll); XStoreColors(xconn.display, xconn.cmap, xconn.map, 256); for(i = 0; i < 256; i++){ xconn.tox11[i] = i; xconn.toplan9[i] = i; } }else{ for(i = 0; i < 128; i++){ c = xconn.map7[i]; if(!XAllocColor(xconn.display, xconn.cmap, &c)){ werrstr("can't allocate colors in 7-bit map"); return -1; } xconn.tox11[xconn.map7to8[i][0]] = c.pixel; xconn.tox11[xconn.map7to8[i][1]] = c.pixel; xconn.toplan9[c.pixel] = xconn.map7to8[i][0]; } } }else{ werrstr("unsupported visual class %d", xconn.vis->class); return -1; } 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(xconn.display, d, GCFunction|GCGraphicsExposures, &v); if(fillstyle != -1) XSetFillStyle(xconn.display, gc, fillstyle); if(foreground != -1) XSetForeground(xconn.display, gc, 0); return gc; }