2 * Copyright © 2023 Keith Packard <keithp@keithp.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
24 #include <X11/Xutil.h>
25 #include <X11/Xatom.h>
27 #ifndef DEFAULT_VISUAL
28 #define DEFAULT_VISUAL 0
32 #define DEFAULT_ROP "copy"
36 #define DEFAULT_WIDTH 200
39 #ifndef DEFAULT_HEIGHT
40 #define DEFAULT_HEIGHT 200
44 #define DEFAULT_FONT 0
47 #ifndef DEFAULT_LINE_WIDTH
48 #define DEFAULT_LINE_WIDTH 0
51 #ifndef DEFAULT_LINE_STYLE
52 #define DEFAULT_LINE_STYLE NULL
55 #ifndef DEFAULT_FILL_STYLE
56 #define DEFAULT_FILL_STYLE NULL
59 #ifndef DEFAULT_CAP_STYLE
60 #define DEFAULT_CAP_STYLE NULL
63 #ifndef DEFAULT_JOIN_STYLE
64 #define DEFAULT_JOIN_STYLE NULL
67 #ifndef DEFAULT_DASHES
68 #define DEFAULT_DASHES 0
69 #define DEFAULT_NUM_DASHES 0
73 #define MOTION PointerMotionMask
75 #define MOTION ButtonMotionMask
79 #define EXTRA_EVENTS 0
82 unsigned long fg_pixel;
83 unsigned long bg_pixel;
85 XFontStruct *default_font;
87 typedef struct _RopNames { char *name; int rop; } RopNameRec, *RopNamePtr;
89 RopNameRec ropNames[] = {
90 { "clear", GXclear, }, /* 0 */
91 { "and", GXand, }, /* src AND dst */
92 { "andReverse", GXandReverse, }, /* src AND NOT dst */
93 { "copy", GXcopy, }, /* src */
94 { "andInverted", GXandInverted, }, /* NOT src AND dst */
95 { "noop", GXnoop, }, /* dst */
96 { "xor", GXxor, }, /* src XOR dst */
97 { "or", GXor, }, /* src OR dst */
98 { "nor", GXnor, }, /* NOT src AND NOT dst */
99 { "equiv", GXequiv, }, /* NOT src XOR dst */
100 { "invert", GXinvert, }, /* NOT dst */
101 { "orReverse", GXorReverse, }, /* src OR NOT dst */
102 { "copyInverted", GXcopyInverted, }, /* NOT src */
103 { "orInverted", GXorInverted, }, /* NOT src OR dst */
104 { "nand", GXnand, }, /* NOT src OR NOT dst */
105 { "set", GXset, }, /* 1 */
109 RopNameToRop (char *name)
113 for (rop = 0; rop < 16; rop++) {
114 if (!strcmp (name, ropNames[rop].name)) {
115 return ropNames[rop].rop;
122 Error (char *s, char *a)
124 fprintf (stderr, s, a);
125 fprintf (stderr, "\n");
130 MatchCapStyle (char *s)
132 if (!strcmp (s, "round"))
134 if (!strcmp (s, "projecting"))
135 return CapProjecting;
136 if (!strcmp (s, "butt"))
138 if (!strcmp (s, "notlast"))
140 Error ("Unknown cap style %s", s);
145 MatchJoinStyle (char *s)
147 if (!strcmp (s, "round"))
149 if (!strcmp (s, "miter"))
151 if (!strcmp (s, "bevel"))
153 Error ("Unknown join style %s", s);
158 MatchFillStyle(char *s)
160 if (!strcmp (s, "solid"))
162 if (!strcmp (s, "tiled"))
164 if (!strcmp (s, "stippled"))
166 if (!strcmp (s, "opaquestippled"))
167 return FillOpaqueStippled;
168 Error ("Unknown fill style %s", s);
173 MatchLineStyle(char *s)
175 if (!strcmp (s, "solid"))
177 if (!strcmp (s, "onoff"))
178 return LineOnOffDash;
179 if (!strcmp (s, "double"))
180 return LineDoubleDash;
181 Error ("Unknown line style %s", s);
187 typedef struct _Position {
189 int start_x, start_y;
192 } PositionRec, *PositionPtr;
194 PositionRec positions[5];
197 UpdatePositions (state, x, y)
202 for (i = 0; i < 5; i++)
204 if (state & (1 << i)) {
205 positions[i].cur_x = x;
206 positions[i].cur_y = y;
212 HandleButtonPress (dpy, win, gc, ev)
218 XButtonEvent *bev = (XButtonEvent *) ev;
220 Undraw (dpy, win, gc, positions);
221 positions[bev->button - 1].seen = 1;
222 positions[bev->button - 1].start_x = bev->x;
223 positions[bev->button - 1].start_y = bev->y;
224 positions[bev->button - 1].cur_x = bev->x;
225 positions[bev->button - 1].cur_y = bev->y;
226 positions[bev->button - 1].end_x = bev->x;
227 positions[bev->button - 1].end_y = bev->y;
228 UpdatePositions (bev->state, bev->x, bev->y);
229 Draw (dpy, win, gc, positions);
233 HandleButtonRelease (dpy, win, gc, ev)
239 XButtonEvent *bev = (XButtonEvent *) ev;
241 Undraw (dpy, win, gc, positions);
242 UpdatePositions (bev->state, bev->x, bev->y);
243 positions[bev->button - 1].end_x = bev->x;
244 positions[bev->button - 1].end_y = bev->y;
245 Draw (dpy, win, gc, positions);
249 HandleMotionNotify (dpy, win, gc, ev)
255 XMotionEvent *mev = (XMotionEvent *) ev;
257 Undraw (dpy, win, gc, positions);
258 UpdatePositions (mev->state, mev->x, mev->y);
259 Draw (dpy, win, gc, positions);
263 DisplayPositions (dpy, win, gc, positions)
267 PositionRec positions[5];
269 static char text[1024];
273 int dir, font_ascent, font_descent;
276 XTextExtents (default_font , text, strlen(text),
277 &dir, &font_ascent, &font_descent, &overall);
279 XClearArea (dpy, win, 0, 0, overall.width, font_ascent + font_descent, False);
281 for (i = 0; i < 5; i++)
283 if (positions[i].seen)
287 sprintf (text + pos, "%1d: (%4d,%4d),(%4d,%4d)",
288 i, positions[i].start_x, positions[i].start_y,
289 positions[i].cur_x, positions[i].cur_y);
293 XDrawString (dpy, win, gc, 0, font_ascent, text, pos);
304 XSetForeground (dpy, gc, fg_pixel);
305 XSetFunction (dpy, gc, default_rop);
313 XSetForeground (dpy, gc, bg_pixel);
314 XSetFunction (dpy, gc, GXcopy);
322 fprintf (stderr, "Usage: %s\n", program);
323 fprintf (stderr, "\t-display <display-name>\n");
324 fprintf (stderr, "\t-rop {");
325 for (i = 0; i < 16; i++) {
326 fprintf (stderr, "%s", ropNames[i].name);
328 fprintf (stderr, "}\n");
330 fprintf (stderr, ",\n\t\t");
332 fprintf (stderr, ", ");
334 fprintf (stderr, "\t-fg <fg color name>\n");
335 fprintf (stderr, "\t-bg <bg color name>\n");
336 fprintf (stderr, "\t-fg_pixel <fg pixel value>\n");
337 fprintf (stderr, "\t-bg_pixel <bg pixel value>\n");
338 fprintf (stderr, "\t-fn <font name>\n");
339 fprintf (stderr, "\t-geometry <geometry>\n");
340 fprintf (stderr, "\t-lw <line width>\n");
341 fprintf (stderr, "\t-cap {round, projecting, butt, notlast}\n");
342 fprintf (stderr, "\t-join {round, miter, bevel}\n");
343 fprintf (stderr, "\t-fill {solid, tiled, stippled, opaquestippled}\n");
344 fprintf (stderr, "\t-linestyle {solid, onoff, double}\n");
345 fprintf (stderr, "\t-bitmap <bitmap file name>\n");
346 fprintf (stderr, "\t-dashes [<dash value> ...]\n");
355 char *cap_name = DEFAULT_CAP_STYLE;
356 char *join_name = DEFAULT_JOIN_STYLE;
357 char *fill_name = DEFAULT_FILL_STYLE;
358 char *line_name = DEFAULT_LINE_STYLE;
359 char *font_name = DEFAULT_FONT;
360 double line_width = DEFAULT_LINE_WIDTH;
361 char dashes[256] = { DEFAULT_DASHES };
362 int ndashes = DEFAULT_NUM_DASHES;
363 VisualID vid = DEFAULT_VISUAL;
364 unsigned long planemask = ~0;
372 HandleExpose(Display *dpy, Window win, GC gc);
375 HandleKeyPress(Display *dpy, Window win, GC gc, XEvent *ev);
378 HandleKeyRelease(Display *dpy, Window win, GC gc, XEvent *ev);
388 char **init_argv = argv;
389 XSetWindowAttributes attr;
392 unsigned int width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT;
394 int border_width = 1;
395 XSizeHints sizeHints;
397 XClassHint classHints;
400 #ifdef MATCH_ARGUMENT
404 #ifdef COMPRESS_EXPOSE
409 XTextProperty wm_name, icon_name;
410 Atom wm_delete_window;
411 int has_fg_pixel = 0, has_bg_pixel = 0;
412 int has_colormap = 0;
413 unsigned long gc_mask;
414 char quit_string[10];
415 unsigned long window_mask;
418 rop_name = DEFAULT_ROP;
419 wm_name.value = (unsigned char *) argv[0];
420 wm_name.encoding = XA_STRING;
422 wm_name.nitems = strlen (wm_name.value) + 1;
426 if (!strcmp (*argv, "-display"))
428 else if (!strcmp (*argv, "-visual"))
429 vid = strtol(*++argv, NULL, 0);
430 else if (!strcmp (*argv, "-cmap"))
432 colormap = strtol(*++argv, NULL, 0);
435 else if (!strcmp (*argv, "-rop"))
437 else if (!strcmp (*argv, "-fg"))
439 else if (!strcmp (*argv, "-bg"))
441 else if (!strcmp (*argv, "-fg_pixel"))
443 fg_pixel = strtol(*++argv, NULL, 0);
446 else if (!strcmp (*argv, "-bg_pixel"))
448 bg_pixel = strtol(*++argv, NULL, 0);
451 else if (!strcmp (*argv, "-fn"))
453 else if (!strcmp (*argv, "-pm"))
454 planemask = strtol(*++argv, NULL, 0);
455 else if (!strcmp (*argv, "-geometry"))
456 geometryMask = XParseGeometry (*++argv, &x, &y, &width, &height);
457 else if (!strcmp (*argv, "-sync"))
459 else if (!strcmp (*argv, "-bw"))
460 border_width = strtol(*++argv, NULL, 0);
461 else if (!strcmp (*argv, "-lw"))
462 line_width = strtod(*++argv, NULL);
463 else if (!strcmp (*argv, "-cap"))
465 else if (!strcmp (*argv, "-join"))
467 else if (!strcmp (*argv, "-fill"))
469 else if (!strcmp (*argv, "-linestyle"))
471 else if (!strcmp (*argv, "-bitmap"))
472 bitmap_file = *++argv;
473 else if (!strcmp (*argv, "-dashes"))
477 while (*argv && isdigit (**argv))
478 dashes[ndashes++] = atoi(*argv++);
481 #ifdef MATCH_ARGUMENT
482 else if (i = MatchArgument (argv))
485 else if (!strcmp (*argv, "-root"))
486 root = strtol (*++argv, NULL, 0);
491 wmHints.flags = InputHint;
492 wmHints.input = True;
493 classHints.res_name = init_argv[0];
494 classHints.res_class = init_argv[0];
495 dpy = XOpenDisplay (dpy_name);
497 Error ("Can't open display", "");
499 XSynchronize (dpy, sync);
500 screen = DefaultScreen (dpy);
502 root = RootWindow (dpy, screen);
503 window_mask = CWBackPixel|CWBorderPixel|CWEventMask;
505 colormap = DefaultColormap (dpy, screen);
508 window_mask |= CWColormap;
509 attr.colormap = colormap;
511 visual = DefaultVisual (dpy, screen);
512 depth = DefaultDepth (dpy, screen);
515 XVisualInfo vi, *vi_ret;
520 vi_ret = XGetVisualInfo (dpy, VisualIDMask|VisualScreenMask,
524 visual = vi_ret->visual;
527 colormap = XCreateColormap (dpy, root, visual, AllocNone);
528 window_mask |= CWColormap;
529 attr.colormap = colormap;
531 depth = vi_ret->depth;
535 fg_pixel = BlackPixel (dpy, screen);
537 bg_pixel = WhitePixel (dpy, screen);
540 if (!XAllocNamedColor (dpy, colormap, fg, &hard, &exact))
541 Error ("Can't allocate fg pixel %s", fg);
542 fg_pixel = hard.pixel;
546 if (!XAllocNamedColor (dpy, colormap, bg, &hard, &exact))
547 Error ("Can't allocate bg pixel %s", bg);
548 bg_pixel = hard.pixel;
550 attr.background_pixel = bg_pixel;
551 attr.border_pixel = fg_pixel;
553 ExposureMask|KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask|MOTION|EXTRA_EVENTS;
554 // attr.override_redirect = True;
555 // window_mask |= CWOverrideRedirect;
556 wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
557 win = XCreateWindow (dpy, root, x, y, width, height, border_width,
565 XSetWMProperties (dpy, win,
566 &wm_name, &icon_name,
568 &sizeHints, &wmHints, 0);
569 XSetWMProtocols (dpy, win, &wm_delete_window, 1);
570 default_rop = RopNameToRop (rop_name);
573 gcv.line_width = (int) line_width;
574 gc_mask |= GCLineWidth;
578 gcv.cap_style = MatchCapStyle (cap_name);
579 gc_mask |= GCCapStyle;
582 gcv.fill_style = MatchFillStyle (fill_name);
583 gc_mask |= GCFillStyle;
586 gcv.line_style = MatchLineStyle (line_name);
587 gc_mask |= GCLineStyle;
590 gcv.join_style = MatchJoinStyle (join_name);
591 gc_mask |= GCJoinStyle;
594 default_font = XLoadQueryFont (dpy, font_name);
596 gcv.font = default_font->fid;
599 gcv.function = default_rop;
600 gcv.foreground = fg_pixel;
601 gcv.background = bg_pixel;
602 gcv.plane_mask = planemask;
603 gc_mask |= GCFunction|GCForeground|GCBackground|GCPlaneMask;
606 unsigned int width, height;
608 Pixmap bitmap, pixmap;
610 ret = XReadBitmapFile (dpy, win, bitmap_file, &width, &height,
611 &bitmap, (int *) 0, (int *) 0);
613 case BitmapOpenFailed:
614 Error ("Can't open bitmap file %s", bitmap_file);
615 case BitmapFileInvalid:
616 Error ("Bitmap file %s invalid", bitmap_file);
618 Error ("Out of memory reading bitmap file %s", bitmap_file);
622 switch (gcv.fill_style) {
624 pixmap = XCreatePixmap (dpy, win, width, height, DefaultDepth (dpy, screen));
625 gc = XCreateGC (dpy, pixmap, GCForeground|GCBackground, &gcv);
626 XCopyPlane (dpy, bitmap, pixmap, gc, 0, 0, width, height, 0, 0, 1);
628 XFreePixmap (dpy, bitmap);
633 case FillOpaqueStippled:
634 gcv.stipple = bitmap;
635 gc_mask |= GCStipple;
639 gc = XCreateGC (dpy, win, gc_mask, &gcv);
641 default_font = XQueryFont (dpy, XGContextFromGC(gc));
643 XSetDashes (dpy, gc, 0, dashes, ndashes);
644 XMapWindow (dpy, win);
646 XNextEvent (dpy, &ev);
648 if (HasMotion && ev.type != MotionNotify) {
650 HandleMotionNotify (dpy, win, gc, &mev);
653 #ifdef COMPRESS_EXPOSE
654 if (HasExpose && ev.type != Expose) {
656 HandleExpose (dpy, eev.xexpose.window, gc);
661 #ifdef COMPRESS_EXPOSE
665 } else if (ev.xexpose.count == 0)
668 HandleExpose (dpy, ev.xexpose.window, gc);
672 HandleKeyPress (dpy, ev.xkey.window, gc, &ev);
675 HandleKeyRelease (dpy, ev.xkey.window, gc, &ev);
679 if (XLookupString ((XKeyEvent *) &ev, quit_string, sizeof (quit_string), 0, 0) == 1) {
680 switch (quit_string[0]) {
684 XClearArea (dpy, ev.xkey.window, 0, 0, 0, 0, True);
692 HandleButtonPress (dpy, ev.xbutton.window, gc, &ev);
695 HandleButtonRelease (dpy, ev.xbutton.window, gc, &ev);
702 HandleMotionNotify (dpy, ev.xmotion.window, gc, &ev);
710 HandleOtherEvent (dpy, win, gc, &ev);
716 #include <X11/extensions/Xrender.h>
718 XRenderColor renderBlack = { 0, 0, 0, 0xffff };
719 XRenderColor renderWhite = { 0xffff, 0xffff, 0xffff, 0xffff };
720 XRenderColor renderRed = { 0xffff, 0, 0, 0xffff };
721 XRenderColor renderGreen = { 0, 0xffff, 0, 0xffff };
722 XRenderColor renderBlue = { 0, 0, 0xffff, 0xffff };
724 XRenderColor renderClearRed = { 0x8000, 0, 0, 0x8000 };
725 XRenderColor renderClearGreen = { 0, 0x8000, 0, 0x8000 };
726 XRenderColor renderClearBlue = { 0, 0, 0x8000, 0x8000 };
729 GetPicture (Display *dpy, Window w)
734 p = XRenderCreatePicture (dpy, w,
735 XRenderFindVisualFormat (dpy, visual),
741 GetSrc (Display *dpy, XRenderColor *color)
745 static XRenderColor lastColor;
746 XRenderPictFormat *f;
747 XRenderPictureAttributes attr;
749 if (p && !memcmp (color, &lastColor, sizeof (XRenderColor)))
753 f = XRenderFindStandardFormat (dpy, PictStandardARGB32);
754 pix = XCreatePixmap (dpy, RootWindow (dpy, DefaultScreen (dpy)),
757 p = XRenderCreatePicture (dpy, pix, f, CPRepeat, &attr);
758 XFreePixmap (dpy, pix);
760 XRenderFillRectangle (dpy, PictOpSrc, p, color, 0, 0, 1, 1);
766 GetMask (Display *dpy)
768 return XRenderFindStandardFormat (dpy, PictStandardA8);