/* * Test program to test for two depressingly common cursor bugs related * to creating pixmap cursors with a mask of None. * * One: using a non-bitmap source pixmap with a None mask should draw a * BadMatch error, but instead is accepted, with the source treated as * a bitmap in a way that makes sense to someone familiar with the * ways such pixmaps are stored internally. The program will print a * message, one of three possibilities: * * Note: can't test for no-mask depth-check bug; depth is 1 * * This is printed if the screen's default depth is 1. * Testing this requires creating a pixmap of depth * greater than 1. (We could look for support for such * pixmaps even if the root depth is 1, but that's * unlikely to succeed and would complicate the code * significantly.) * * Note: display has the no-mask depth-check bug * * The bug is present: cursor creation succeeded when it * should have produced a BadMatch error. * * Good, the no-mask depth-check bug is absent * * The bug is absent: cursor creation as outlined above * did produce a BadMatch error. * * Two: when using a mask of None, the image bitmap is effectively * extended to the right with background pixels to a width of * (typically) 32 pixels. Since this is purely a display issue * (there's no way defined in the protocol to read back a window's * contents including the pointer cursor), it cannot be tested in an * entirely automated way. So, this program puts up a window of * geometry 100x300+100+100 with diagonal shading in alternating * directions for the top, middle, and bottom thirds (the shading is * to make it obvious where the boundaries are). It then sets up * different cursors for the three portions. The top portion shows * what the bug typically looks like, even if it is not actually * present (it specifies a width of 32 and extends the bitmaps on the * client side). The middle portion shows what the proper behaviour * is, even if the bug is present (it specifies a mask pixmap full of * 1s). The bottom portion is the test portion; it uses the * problematic kind of cursor. It should look like the cursor in the * middle third; on buggy servers, it gets mangled, typically looking * like the top third. This is not, strictly, contrary to the * protocol, since cursors "may be transformed arbitrarily to meet * display limitations", but, if the middle third is right (it should * be a one-pixel wide vertical white line, 18 pixels tall, with a * one-pixel black surround, for a total size of 3x20 pixels) then * there clearly is no actual need to mangle such cursors, so I think * calling it a bug is fair. * * This is not a particularly well-behaved X client; for example, it * does not set any window-manager properties. I was aiming for * minimal (and thus understandable) code rather than elaborate * interaction with window and session managers. */ #include #include #include #include static Display *disp; static Screen *scr; static int scrwidth; static int scrheight; static int depth; static Window rootwin; static GC gc; static GC bitgc; static unsigned long int wpix; static unsigned long int bpix; static Window w; static Window wa; static Window wb; static Window wc; static Pixmap pm_ac; static Pixmap pm_b; static Pixmap pm_bits_a; static Pixmap pm_bits_b; static Pixmap pm_bits_c; static Pixmap pm_mask_a; static Pixmap pm_mask_b; static Cursor c_a; static Cursor c_b; static Cursor c_c; static int (*olderr)(Display *, XErrorEvent *); static int gotmatcherr; static void setup_gcs(void) { Pixmap bitpm; gc = XDefaultGCOfScreen(scr); bitpm = XCreatePixmap(disp,rootwin,1,1,1); bitgc = XCreateGC(disp,bitpm,0,0); XFreePixmap(disp,bitpm); } /* * The Xlib doc doesn't say anything about the semantics of the return * value from the error handler. UTSLing reveals it is...completely * ignored, at least in the implementations I have at hand. But we * still have to return int, for the function's type to be correct. */ static int depthcheck_eh(Display *d, XErrorEvent *ee) { if (ee->error_code == BadMatch) { gotmatcherr = 1; return(0); } else { return((*olderr)(d,ee)); } } static void test_depth(void) { Pixmap ip; Cursor c; XColor colour; if (depth == 1) { printf("Note: can't test for no-mask depth-check bug; depth is 1\n"); return; } ip = XCreatePixmap(disp,rootwin,1,1,depth); XSetForeground(disp,gc,0); XDrawPoint(disp,ip,gc,0,0); XSync(disp,False); colour.red = 32768; colour.green = 32768; colour.blue = 32768; gotmatcherr = 0; olderr = XSetErrorHandler(&depthcheck_eh); c = XCreatePixmapCursor(disp,ip,None,&colour,&colour,0,0); XSync(disp,False); XSetErrorHandler(olderr); if (! gotmatcherr) { XFreeCursor(disp,c); printf("Note: display has the no-mask depth-check bug\n"); } else { printf("Good, the no-mask depth-check bug is absent\n"); } XFreePixmap(disp,ip); } static void setup_pixmaps(void) { pm_ac = XCreatePixmap(disp,rootwin,4,4,depth); XSetForeground(disp,gc,bpix); XFillRectangle(disp,pm_ac,gc,0,0,4,4); XSetForeground(disp,gc,wpix); XDrawPoint(disp,pm_ac,gc,0,0); XDrawPoint(disp,pm_ac,gc,2,2); XDrawPoint(disp,pm_ac,gc,0,1); XDrawPoint(disp,pm_ac,gc,1,0); XDrawPoint(disp,pm_ac,gc,3,2); XDrawPoint(disp,pm_ac,gc,2,3); XDrawPoint(disp,pm_ac,gc,3,1); XDrawPoint(disp,pm_ac,gc,1,3); pm_b = XCreatePixmap(disp,rootwin,4,4,depth); XSetForeground(disp,gc,bpix); XFillRectangle(disp,pm_b,gc,0,0,4,4); XSetForeground(disp,gc,wpix); XDrawPoint(disp,pm_b,gc,3,0); XDrawPoint(disp,pm_b,gc,1,2); XDrawPoint(disp,pm_b,gc,3,1); XDrawPoint(disp,pm_b,gc,2,0); XDrawPoint(disp,pm_b,gc,0,2); XDrawPoint(disp,pm_b,gc,1,3); XDrawPoint(disp,pm_b,gc,0,1); XDrawPoint(disp,pm_b,gc,2,3); } static void setup_cursors(void) { XColor fg; XColor bg; fg.red = 65535; fg.green = 65535; fg.blue = 65535; bg.red = 0; bg.green = 0; bg.blue = 0; pm_bits_a = XCreatePixmap(disp,rootwin,32,20,1); pm_mask_a = XCreatePixmap(disp,rootwin,32,20,1); XSetForeground(disp,bitgc,0); XFillRectangle(disp,pm_bits_a,bitgc,0,0,32,20); XSetForeground(disp,bitgc,1); XFillRectangle(disp,pm_bits_a,bitgc,1,1,1,18); XFillRectangle(disp,pm_mask_a,bitgc,0,0,32,20); c_a = XCreatePixmapCursor(disp,pm_bits_a,pm_mask_a,&fg,&bg,1,10); pm_bits_b = XCreatePixmap(disp,rootwin,3,20,1); pm_mask_b = XCreatePixmap(disp,rootwin,3,20,1); XSetForeground(disp,bitgc,0); XFillRectangle(disp,pm_bits_b,bitgc,0,0,3,20); XSetForeground(disp,bitgc,1); XFillRectangle(disp,pm_bits_b,bitgc,1,1,1,18); XFillRectangle(disp,pm_mask_b,bitgc,0,0,3,20); c_b = XCreatePixmapCursor(disp,pm_bits_b,pm_mask_b,&fg,&bg,1,10); pm_bits_c = XCreatePixmap(disp,rootwin,3,20,1); XSetForeground(disp,bitgc,0); XFillRectangle(disp,pm_bits_c,bitgc,0,0,3,20); XSetForeground(disp,bitgc,1); XFillRectangle(disp,pm_bits_c,bitgc,1,1,1,18); c_c = XCreatePixmapCursor(disp,pm_bits_c,None,&fg,&bg,1,10); } int main(void); int main(void) { disp = XOpenDisplay(0); if (disp == 0) { fprintf(stderr,"can't open display\n"); exit(1); } scr = XDefaultScreenOfDisplay(disp); scrwidth = XWidthOfScreen(scr); scrheight = XHeightOfScreen(scr); depth = XDefaultDepthOfScreen(scr); rootwin = XRootWindowOfScreen(scr); wpix = XWhitePixelOfScreen(scr); bpix = XBlackPixelOfScreen(scr); setup_gcs(); test_depth(); setup_pixmaps(); setup_cursors(); w = XCreateWindow(disp,rootwin,100,100,100,300,0,depth,InputOutput,CopyFromParent,0,0); wa = XCreateWindow(disp,w,0,0,100,100,0,depth,InputOutput,CopyFromParent,0,0); wb = XCreateWindow(disp,w,0,100,100,100,0,depth,InputOutput,CopyFromParent,0,0); wc = XCreateWindow(disp,w,0,200,100,100,0,depth,InputOutput,CopyFromParent,0,0); XSetWindowBackgroundPixmap(disp,wa,pm_ac); XSetWindowBackgroundPixmap(disp,wb,pm_b); XSetWindowBackgroundPixmap(disp,wc,pm_ac); XDefineCursor(disp,wa,c_a); XDefineCursor(disp,wb,c_b); XDefineCursor(disp,wc,c_c); XClearWindow(disp,wa); XClearWindow(disp,wb); XClearWindow(disp,wc); XMapWindow(disp,wa); XMapWindow(disp,wb); XMapWindow(disp,wc); XMapRaised(disp,w); while (1) { XEvent e; XNextEvent(disp,&e); } }