/* gcc -o short-segments `pkg-config --cflags x11` short-segments.c `pkg-config --libs x11` */ #include #include #include #define ARRAY_LENGTH(arr) (sizeof (arr) / sizeof (arr[0])) /* This is a test case written to explore the issue dscribed in the * following bug report: * * PolySegment draws one pixel short when x1/y1 > x2/y2 * http://bugs.freedesktop.org/show_bug.cgi?id=22770 * * In the below comments, the "xf86-video-intel implementation" * describes objectionable results from the point of view of the * original bug reporter, while the "vesa implementation" describes * desired results. So from that point of view, seeing any red pixels * in the result would be undesired, and also not seeing the green * pixels would be undesired. * * -Carl Worth */ static void xlib_draw (Display *dpy, Window window, int width, int height) { GC gc; unsigned long gc_mask = 0; XGCValues gc_values; XSegment segments[2] = { /* The original bug report included segments drawn from right * to left and from bottom to top like this: */ { 2, 2, 1, 2 }, { 2, 2, 2, 1 } /* If instead, the segments are drawn from left to right and * from top to bottom like this, then the vesa driver starts * behaving like the xf86-video-intel driver. { 1, 2, 2, 2 }, { 2, 1, 2, 2 } */ }; gc_mask = 0; gc_mask |= GCForeground; gc_values.foreground = 0x00ff0000; /* The original bug report draws segments with line_width at the * default value of 0. This triggers device-specific line drawing * for which either the undesired or desired result is * allowed. Instead, if the segments are drawn with line_width of * 1 like so: gc_mask |= GCLineWidth; gc_values.line_width = 1; * then an algorithm carefully specified in the X protocol will be * used consistently by either "vesa" or "xf86-video-intel". This * algorithm happens to yield the undesired behavior. But since * this is the only method of obtaining consistent results across * different implementations, I recommend line_width of 1 be used * and the coordinates be adjusted to obtain the desired results. */ gc = XCreateGC (dpy, window, gc_mask, &gc_values); /* A red pixel. The vesa implementation (with line_width 0) will * cover this with black. The xf86-video-intel implementation * (again with line_width 0) will leave this red pixel showing. */ XFillRectangle (dpy, window, gc, 2, 2, 1, 1); gc_mask = 0; gc_mask |= GCForeground; gc_values.foreground = 0x0000ff00; XChangeGC (dpy, gc, gc_mask, &gc_values); /* Two green pixels. The xf86-video-intel implementation will * convert these with black. The vesa implementation will leave these * green pixels showing. */ XFillRectangle (dpy, window, gc, 1, 2, 1, 1); XFillRectangle (dpy, window, gc, 2, 1, 1, 1); gc_mask = 0; gc_mask |= GCForeground; gc_values.foreground = 0; gc_mask |= GCCapStyle; gc_values.cap_style = CapNotLast; XChangeGC (dpy, gc, gc_mask, &gc_values); /* Finally, draw the two segments of interest. */ XDrawSegments (dpy, window, gc, segments, 2); XFreeGC (dpy, gc); } static void handle_events(Display *dpy, Window window, int width, int height) { XEvent xev; KeyCode quit_code = XKeysymToKeycode (dpy, XStringToKeysym("Q")); XNextEvent (dpy, &xev); while (1) { XNextEvent (dpy, &xev); switch (xev.type) { case KeyPress: if (xev.xkey.keycode == quit_code) return; break; case ConfigureNotify: width = xev.xconfigure.width; height = xev.xconfigure.height; break; case Expose: if (xev.xexpose.count == 0) xlib_draw (dpy, window, width, height); break; } } } int main (int argc, char *argv[]) { Display *dpy; Window root, window; int width = 4, height = 4; dpy = XOpenDisplay (NULL); if (dpy == NULL) { fprintf(stderr, "Failed to open display %s\n", XDisplayName(NULL)); return 1; } window = XCreateSimpleWindow(dpy, DefaultRootWindow (dpy), 0, 0, width, height, 0, WhitePixel(dpy, DefaultScreen (dpy)), WhitePixel(dpy, DefaultScreen (dpy))); XSelectInput(dpy, window, KeyPressMask | StructureNotifyMask | ExposureMask); XMapWindow (dpy, window); handle_events (dpy, window, width, height); XDestroyWindow (dpy, window); XCloseDisplay (dpy); return 0; }