dheller@cory.Berkeley.EDU (Dan Heller) (06/01/88)
I have gone over the paint program distributed with the X11R2 code enough to make it work as it was probably intended by the original author (except that I added rubberbanding). The changes included here are intended to patch only -- no additional features are added and serious attempts at buidling a draw program should not be based on this model. See the modified README after applying these patches. -------------------------------------------------------------------- *** paint.orig/README Thu Feb 25 23:01:33 1988 --- README Tue May 31 15:29:00 1988 *************** *** 1,12 **** Read carefully; this is the only documentation there is! The paint program takes no paramaters. It makes a connection to ! $DISPLAY, and opens a window near the upper-left corner of the screen. Paint is very modal. There are keyboard commands to change the modes. Type the commands at the window on the X11 display. The current mode ! is displayed on stdout. (I would like to display the current mode on ! the X11 window, but I couldn't get fonts to work.) Some of the keyboard commands take an integer argument. To specify an argument, type a number (with an optional minus sign) before the key --- 1,22 ---- + This program has been updated by Dan Heller <island!argv@sun.com> + to make it work with X11R2 and in the manner probably desired by + the original author. Reader's note: "paint" is a misnomer because + this package maintains a display list of objects. Paint packages + are not object based, but pixel based. This is really a simplified + draw package intended to show examples of how some of the graphics + rendering machanisms work. What follows is the original README file + modified to reflect my changes. + --Dan Heller 5/31/88 <island!argv@sun.com> + -------- + Read carefully; this is the only documentation there is! The paint program takes no paramaters. It makes a connection to ! $DISPLAY. Paint is very modal. There are keyboard commands to change the modes. Type the commands at the window on the X11 display. The current mode ! is displayed on the top of the window. Some of the keyboard commands take an integer argument. To specify an argument, type a number (with an optional minus sign) before the key *************** *** 14,23 **** a Draw an arc. When the user drags through a rectangle with the mouse, an arc will be inscribed in that rectangle (from angle1 to angle2). A Draw a filled arc. Same as above, but arc will be filled. ! C Clear the window. f Change the alu function. This takes an argument from 0 to 15. --- 24,34 ---- a Draw an arc. When the user drags through a rectangle with the mouse, an arc will be inscribed in that rectangle (from angle1 to angle2). + Default set to 360 degrees and 0 degrees (a complete ellipse). A Draw a filled arc. Same as above, but arc will be filled. ! C Clear the window (also clears display list). f Change the alu function. This takes an argument from 0 to 15. *************** *** 24,34 **** l Draw lines. When the user drags through the window, a line will be drawn from where he presses the button to where he releases it. ! p Draw polygons. Specify several points on the screen by ! clicking with the left button. Specify the last point with the right ! button. A polygon will be drawn connecting the points in order. (This ! isn't a real polygon; it won't automatically connect the first and last ! points for you. This is just a test for XLines().) P Draw filled polygons. Same as above, but the polygon will be filled. (The first and last points are connected by the server.) --- 35,41 ---- l Draw lines. When the user drags through the window, a line will be drawn from where he presses the button to where he releases it. ! p Draw open polygon (freehand draw). Release button to complete polygon. P Draw filled polygons. Same as above, but the polygon will be filled. (The first and last points are connected by the server.) *************** *** 41,52 **** t Change the thickness (a.k.a. "line width"). The line width is set to the argument. ! , Set the value of angle1 to the argument. The units are ! degrees. (This was supposed to be a '<', but because I have to work to ! detect shifted characters, ',' was easier.) ! . Set the value of angle2 to the argument. (As above, this was ! supposed to be a '>'.) ! Paint will also refresh itself on expose windows. The code to do this ! correctly with clipping rectangles is currently commented out. --- 48,61 ---- t Change the thickness (a.k.a. "line width"). The line width is set to the argument. ! < or , Set the value of angle1 to the argument. The units are in degrees. ! > or . Set the value of angle2 to the argument. The units are in degrees. ! q quit program. ! ! Paint will also refresh itself on expose windows. This action redraws each ! object in the display list -- so it is very slow (esp on a sun). ! ! The Polygon routines leave screen dust (stray pixels) because of Xor-ing. ! This is due to the method used to "rubberband" freehand curves. *** paint.orig/main.c Thu Feb 25 23:01:20 1988 --- main.c Tue May 31 16:32:26 1988 *************** *** 36,42 **** Display *dpy; ! int shiftbit = FALSE; HandleKey(event) XEvent *event; --- 36,42 ---- Display *dpy; ! int save_func, save_width; HandleKey(event) XEvent *event; *************** *** 47,60 **** static int arg = 0; static int argval = 1; static int sgn = 0; ! code = event->xkey.keycode; ! if (code == 174) { ! shiftbit = TRUE; return; ! } ! c = GetCharFromCode(code); ! if (c >= 'a' && c <= 'z' && shiftbit) ! c += 'A' - 'a'; switch (c) { case 'C': XClearWindow(dpy, window); --- 47,60 ---- static int arg = 0; static int argval = 1; static int sgn = 0; ! KeySym k; ! char b[2]; ! ! (void) XLookupString(event, b, 2, &k, NULL); ! c = b[0]; ! if (IsModifierKey(k)) return; ! switch (c) { case 'C': XClearWindow(dpy, window); *************** *** 75,85 **** break; case 'p': stat.mode = polygon; - npoints = 0; break; case 'P': stat.mode = filledpolygon; - npoints = 0; break; case 'a': stat.mode = arc; --- 75,83 ---- *************** *** 88,102 **** stat.mode = filledarc; break; case 'f': ! stat.func = arg; ! if (arg != argval) stat.func = GXinvert; - gcvalues.function = stat.func; - XChangeGC(dpy, gc, GCFunction, &gcvalues); break; case ',': stat.angle1 = arg * 64; break; case '.': stat.angle2 = arg * 64; break; --- 86,99 ---- stat.mode = filledarc; break; case 'f': ! if ((stat.func = arg) < GXclear || arg > GXset) stat.func = GXinvert; break; + case '<': case ',': stat.angle1 = arg * 64; break; + case '>': case '.': stat.angle2 = arg * 64; break; *************** *** 120,129 **** return; case 't': stat.thickness = argval; - gcvalues.line_width = stat.thickness; - XChangeGC(dpy, gc, GCLineWidth, &gcvalues); break; default: printf("Unknown code %d (%c)\n", event->xkey.keycode, c); return; } --- 117,127 ---- return; case 't': stat.thickness = argval; break; + case 'q': + exit(0); default: + XBell(dpy, 50); printf("Unknown code %d (%c)\n", event->xkey.keycode, c); return; } *************** *** 133,175 **** sgn = 1; } - HandleKeyUp(event) - XEvent *event; - { - if (event->xkey.keycode == 174) - shiftbit = FALSE; - } - DisplayMode() { ! char str[500]; ! sprintf(str, ! "Mode %s; Thickness %d; Function %s; angle1 %.2f; angle2 %.2f", ! StringForMode(stat.mode), stat.thickness, StringForFunction(stat.func), ! stat.angle1 / 64.0, stat.angle2/64.0); ! XDrawImageString(dpy, window, gc, 0, 0, str, strlen(str)); ! printf("%s\n", str); } - HandleButtonDown(event) - XEvent *event; - { - switch (stat.mode) { - case line: - case rect: - case filledrect: - case arc: - case filledarc: - curx = event->xbutton.x; - cury = event->xbutton.y; - break; - } - } - ImagePtr NewImage() { ImagePtr image; ! image = (ImagePtr) malloc(sizeof(ImageRec)); image->stat = stat; image->npoints = 0; image->points = (XPoint *) malloc(1); --- 131,154 ---- sgn = 1; } DisplayMode() { ! char str[500], *p; ! sprintf(str, "Mode %s, Thickness %d, Function %s", ! StringForMode(stat.mode), stat.thickness, StringForFunction(stat.func)); ! if (stat.mode == arc || stat.mode == filledarc) { ! p = str + strlen(str); ! sprintf(p, ", angle1 %g, angle2 %g", ! stat.angle1 / 64.0, stat.angle2 / 64.0); ! } else ! strcat(str, " "); /* to erase angles string */ ! XDrawImageString(dpy, window, gc, 5, 10, str, strlen(str)); } ImagePtr NewImage() { ImagePtr image; ! image = (ImagePtr) calloc((unsigned)1, sizeof(ImageRec)); image->stat = stat; image->npoints = 0; image->points = (XPoint *) malloc(1); *************** *** 180,209 **** ImagePtr image; int x, y; { ! npoints = image->npoints++; image->points = (XPoint *) realloc(image->points, sizeof(XPoint) * image->npoints); ! image->points[npoints].x = x; ! image->points[npoints].y = y; ! image->next = NULL; ! image->drawn = FALSE; } ! DrawImage(image) ImagePtr image; { XGCValues gcvalues; int x1, x2, y1, y2, x, y, width, height; gcvalues.function = image->stat.func; gcvalues.line_width = image->stat.thickness; XChangeGC(dpy, gc, GCFunction | GCLineWidth, &gcvalues); ! if (!image->drawn) { if (lastimage) lastimage->next = image; else firstimage = image; lastimage = image; - image->drawn = TRUE; } if (image->npoints == 2) { x1 = image->points[0].x; --- 159,186 ---- ImagePtr image; int x, y; { ! image->npoints++; image->points = (XPoint *) realloc(image->points, sizeof(XPoint) * image->npoints); ! image->points[image->npoints - 1].x = x; ! image->points[image->npoints - 1].y = y; } ! DrawImage(image, append) ImagePtr image; { XGCValues gcvalues; int x1, x2, y1, y2, x, y, width, height; + gcvalues.function = image->stat.func; gcvalues.line_width = image->stat.thickness; XChangeGC(dpy, gc, GCFunction | GCLineWidth, &gcvalues); ! if (append) { if (lastimage) lastimage->next = image; else firstimage = image; lastimage = image; } if (image->npoints == 2) { x1 = image->points[0].x; *************** *** 244,253 **** } } ! ! HandleButtonUp(event) XEvent *event; { int newx, newy; newx = event->xbutton.x; newy = event->xbutton.y; --- 221,243 ---- } } ! HandleButtonDown(event) XEvent *event; { + XGCValues gcvalues; + + curx = event->xbutton.x; + cury = event->xbutton.y; + save_func = stat.func; + save_width = stat.thickness; + gcvalues.function = stat.func = GXxor; + gcvalues.line_width = stat.thickness = 1; + XChangeGC(dpy, gc, GCFunction | GCLineWidth, &gcvalues); + } + + ButtonMove(event) + XEvent *event; + { int newx, newy; newx = event->xbutton.x; newy = event->xbutton.y; *************** *** 257,280 **** case filledrect: case arc: case filledarc: ! image = NewImage(); ! StuffPoint(image, curx, cury); ! StuffPoint(image, newx, newy); ! DrawImage(image); break; case polygon: case filledpolygon: ! if (image == NULL || image->stat.mode != stat.mode) image = NewImage(); StuffPoint(image, newx, newy); ! if (event->xbutton.button == Button3) { ! DrawImage(image); } } curx = newx; cury = newy; - printf("*"); - fflush(stdout); } XRectangle rects[500]; --- 247,321 ---- case filledrect: case arc: case filledarc: ! if (!image) { ! image = NewImage(); ! StuffPoint(image, curx, cury); ! StuffPoint(image, newx, newy); ! } else { ! DrawImage(image, FALSE); /* erase old image */ ! image->points[1].x = newx; ! image->points[1].y = newy; ! } ! DrawImage(image, FALSE); break; case polygon: case filledpolygon: ! if (image == NULL) { image = NewImage(); + StuffPoint(image, curx, cury); + } StuffPoint(image, newx, newy); ! /* build line piece by piece, don't rerender the whole polyline */ ! XDrawLine(dpy, window, gc, curx, cury, newx, newy); ! curx = newx; ! cury = newy; ! } ! } ! ! HandleButtonUp(event) ! XEvent *event; ! { ! int newx, newy; ! newx = event->xbutton.x; ! newy = event->xbutton.y; ! if (image) { ! if (stat.mode == filledpolygon) ! image->stat.mode = polygon; ! DrawImage(image, FALSE); ! if (stat.mode == filledpolygon) ! image->stat.mode = filledpolygon; ! image->stat.func = save_func; ! image->stat.thickness = save_width; ! } ! /* change from xor/single linewidth mode to real render mode */ ! stat.func = save_func; ! stat.thickness = save_width; ! if (!image) /* a single click without movement won't create an object */ ! return; ! switch (stat.mode) { ! case line: ! case rect: ! case filledrect: ! case arc: ! case filledarc: ! if (image->npoints > 1) { ! image->points[1].x = newx; ! image->points[1].y = newy; ! break; } + case polygon: + case filledpolygon: + StuffPoint(image, newx, newy); + /* + puts("polygon points:"); + for (newx = 0; newx < image->npoints; newx++) + printf("%d, %d\n", image->points[newx].x, image->points[newx].y); + */ } + DrawImage(image, TRUE); + image = NULL; /* reinitialize */ curx = newx; cury = newy; } XRectangle rects[500]; *************** *** 295,301 **** XSetClipRectangles(dpy, gc, 0, 0, rects, numrects, Unsorted); numrects = 0; for (image = firstimage; image; image = image->next) ! DrawImage(image); rects[0].x = 0; rects[0].y = 0; rects[0].width = 9999; --- 336,342 ---- XSetClipRectangles(dpy, gc, 0, 0, rects, numrects, Unsorted); numrects = 0; for (image = firstimage; image; image = image->next) ! DrawImage(image, FALSE); rects[0].x = 0; rects[0].y = 0; rects[0].width = 9999; *************** *** 311,327 **** Visual visual; XSetWindowAttributes attributes; XGCValues gcvalues; ! if ((dpy = XOpenDisplay("")) == NULL) Punt("Couldn't open display!"); - InitUtil(); windowwidth = 400; windowheight = 400; stat.thickness = 1; ! stat.func = GXinvert; stat.angle1 = 0; stat.angle2 = 360 * 64; ! foreground = WhitePixel(dpy, DefaultScreen(dpy)); ! background = BlackPixel(dpy, DefaultScreen(dpy)); font = XLoadFont(dpy, "fixed"); visual.visualid = CopyFromParent; attributes.background_pixel = background; --- 352,367 ---- Visual visual; XSetWindowAttributes attributes; XGCValues gcvalues; ! if ((dpy = XOpenDisplay(NULL)) == NULL) Punt("Couldn't open display!"); windowwidth = 400; windowheight = 400; stat.thickness = 1; ! save_func = stat.func = GXinvert; stat.angle1 = 0; stat.angle2 = 360 * 64; ! background = WhitePixel(dpy, DefaultScreen(dpy)); ! foreground = BlackPixel(dpy, DefaultScreen(dpy)); font = XLoadFont(dpy, "fixed"); visual.visualid = CopyFromParent; attributes.background_pixel = background; *************** *** 328,340 **** attributes.border_pixel = foreground; attributes.backing_store = Always; window = XCreateWindow(dpy, RootWindow(dpy, DefaultScreen(dpy)), ! 20, 20, windowwidth, windowheight, 1, ! DefaultDepth(dpy, DefaultScreen(dpy)), CopyFromParent, &visual, ! CWBackPixel | CWBorderPixel /*| CWBackingStore */, ! &attributes); XChangeProperty(dpy, window, XA_WM_NAME, XA_STRING, 8, PropModeReplace, "Paint", 5); ! MyXSelectInput(dpy, window, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | ExposureMask); XMapWindow(dpy, window); gcvalues.foreground = foreground; --- 368,379 ---- attributes.border_pixel = foreground; attributes.backing_store = Always; window = XCreateWindow(dpy, RootWindow(dpy, DefaultScreen(dpy)), ! 20, 20, windowwidth, windowheight, 1, ! DefaultDepth(dpy, DefaultScreen(dpy)), CopyFromParent, &visual, ! CWBackPixel | CWBorderPixel /*| CWBackingStore */, &attributes); XChangeProperty(dpy, window, XA_WM_NAME, XA_STRING, 8, PropModeReplace, "Paint", 5); ! MyXSelectInput(dpy, window, KeyPressMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | ExposureMask); XMapWindow(dpy, window); gcvalues.foreground = foreground; *************** *** 348,363 **** &gcvalues); stat.mode = line; image = firstimage = lastimage = NULL; ! DisplayMode(); ! while (1) { XNextEvent(dpy, &event); switch(event.type) { case KeyPress: HandleKey(&event); break; - case KeyRelease: - HandleKeyUp(&event); - break; case ButtonPress: HandleButtonDown(&event); break; --- 387,398 ---- &gcvalues); stat.mode = line; image = firstimage = lastimage = NULL; ! for (;;) { XNextEvent(dpy, &event); switch(event.type) { case KeyPress: HandleKey(&event); break; case ButtonPress: HandleButtonDown(&event); break; *************** *** 365,373 **** --- 400,410 ---- HandleButtonUp(&event); break; case MotionNotify: + ButtonMove(&event); break; case Expose: RepaintDisplay(&event); + DisplayMode(); break; } } *** paint.orig/paint.h Thu Feb 25 23:01:20 1988 --- paint.h Fri May 27 15:55:41 1988 *************** *** 5,10 **** --- 5,12 ---- #include <stdio.h> #include "X11/Xlib.h" #include "X11/Xatom.h" + #include "X11/Xutil.h" + #include "X11/keysym.h" /*********************************************************** Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts, *************** *** 33,39 **** #define max(x, y) ((x) > (y) ? (x) : (y)) #define abs(x) ((x) < 0 ? (-(x)) : (x)) ! typedef enum {line, rect, filledrect, arc, filledarc, polygon, filledpolygon} ModeType; typedef struct { ModeType mode; --- 35,49 ---- #define max(x, y) ((x) > (y) ? (x) : (y)) #define abs(x) ((x) < 0 ? (-(x)) : (x)) ! typedef enum { ! line, ! rect, ! filledrect, ! arc, ! filledarc, ! polygon, ! filledpolygon ! } ModeType; typedef struct { ModeType mode; *************** *** 47,53 **** int npoints; XPoint *points; struct _ImageRec *next; - int drawn; } ImageRec, *ImagePtr; #ifndef TRUE --- 57,62 ---- *************** *** 68,76 **** ext Window window; ext int windowheight, windowwidth; ext long foreground, background; - - ext XPoint points[1000]; - ext int npoints; extern GC XCreateGC(); extern XFontStruct *XFont(); --- 77,82 ---- *** paint.orig/util.c Thu Feb 25 23:01:21 1988 --- util.c Fri May 27 15:53:07 1988 *************** *** 48,92 **** } - InitUtil() - { - int i; - for (i=0 ; i<256 ; i++) ChrToCode[i] = 0; - ChrToCode['a'] = 194; ChrToCode['b'] = 217; ChrToCode['c'] = 206; - ChrToCode['d'] = 205; ChrToCode['e'] = 204; ChrToCode['f'] = 210; - ChrToCode['g'] = 216; ChrToCode['h'] = 221; ChrToCode['i'] = 230; - ChrToCode['j'] = 226; ChrToCode['k'] = 231; ChrToCode['l'] = 236; - ChrToCode['m'] = 227; ChrToCode['n'] = 222; ChrToCode['o'] = 235; - ChrToCode['p'] = 240; ChrToCode['q'] = 193; ChrToCode['r'] = 209; - ChrToCode['s'] = 199; ChrToCode['t'] = 215; ChrToCode['u'] = 225; - ChrToCode['v'] = 211; ChrToCode['w'] = 198; ChrToCode['x'] = 200; - ChrToCode['y'] = 220; ChrToCode['z'] = 195; ChrToCode['0'] = 239; - ChrToCode['1'] = 192; ChrToCode['2'] = 197; ChrToCode['3'] = 203; - ChrToCode['4'] = 208; ChrToCode['5'] = 214; ChrToCode['6'] = 219; - ChrToCode['7'] = 224; ChrToCode['8'] = 229; ChrToCode['9'] = 234; - ChrToCode['`'] = 191; ChrToCode['~'] = 191; ChrToCode['!'] = 192; - ChrToCode['#'] = 203; ChrToCode['$'] = 208; - ChrToCode['%'] = 214; ChrToCode['&'] = 224; - ChrToCode['*'] = 229; ChrToCode['('] = 234; ChrToCode[')'] = 239; - ChrToCode['-'] = 249; ChrToCode['='] = 245; - ChrToCode['+'] = 245; ChrToCode['['] = 250; ChrToCode['{'] = 250; - ChrToCode[']'] = 246; ChrToCode['}'] = 246; ChrToCode[';'] = 242; - ChrToCode[':'] = 242; ChrToCode['\''] = 251; ChrToCode['"'] = 251; - ChrToCode['\\'] = 247; ChrToCode['|'] = 247; ChrToCode[','] = 232; - ChrToCode['.'] = 237; ChrToCode['/'] = 243; ChrToCode['?'] = 243; - ChrToCode['<'] = 201; ChrToCode['>'] = 201; ChrToCode['\t'] = 190; - ChrToCode[' '] = 212; - for (i=0 ; i<256 ; i++) CodeToChr[ChrToCode[i]] = i; - } - - - GetCharFromCode(n) - int n; - { - return CodeToChr[n]; - } - - char *StringForMode(mode) ModeType mode; { --- 48,53 ---- Dan Heller <island!argv@sun.com>