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);
382 char **init_argv = argv;
383 XSetWindowAttributes attr;
386 unsigned int width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT;
388 int border_width = 1;
389 XSizeHints sizeHints;
391 XClassHint classHints;
394 #ifdef MATCH_ARGUMENT
398 #ifdef COMPRESS_EXPOSE
403 XTextProperty wm_name, icon_name;
404 Atom wm_delete_window;
405 int has_fg_pixel = 0, has_bg_pixel = 0;
406 int has_colormap = 0;
407 unsigned long gc_mask;
408 char quit_string[10];
409 unsigned long window_mask;
412 rop_name = DEFAULT_ROP;
413 wm_name.value = (unsigned char *) argv[0];
414 wm_name.encoding = XA_STRING;
416 wm_name.nitems = strlen (wm_name.value) + 1;
420 if (!strcmp (*argv, "-display"))
422 else if (!strcmp (*argv, "-visual"))
423 vid = strtol(*++argv, NULL, 0);
424 else if (!strcmp (*argv, "-cmap"))
426 colormap = strtol(*++argv, NULL, 0);
429 else if (!strcmp (*argv, "-rop"))
431 else if (!strcmp (*argv, "-fg"))
433 else if (!strcmp (*argv, "-bg"))
435 else if (!strcmp (*argv, "-fg_pixel"))
437 fg_pixel = strtol(*++argv, NULL, 0);
440 else if (!strcmp (*argv, "-bg_pixel"))
442 bg_pixel = strtol(*++argv, NULL, 0);
445 else if (!strcmp (*argv, "-fn"))
447 else if (!strcmp (*argv, "-pm"))
448 planemask = strtol(*++argv, NULL, 0);
449 else if (!strcmp (*argv, "-geometry"))
450 geometryMask = XParseGeometry (*++argv, &x, &y, &width, &height);
451 else if (!strcmp (*argv, "-sync"))
453 else if (!strcmp (*argv, "-bw"))
454 border_width = strtol(*++argv, NULL, 0);
455 else if (!strcmp (*argv, "-lw"))
456 line_width = strtod(*++argv, NULL);
457 else if (!strcmp (*argv, "-cap"))
459 else if (!strcmp (*argv, "-join"))
461 else if (!strcmp (*argv, "-fill"))
463 else if (!strcmp (*argv, "-linestyle"))
465 else if (!strcmp (*argv, "-bitmap"))
466 bitmap_file = *++argv;
467 else if (!strcmp (*argv, "-dashes"))
471 while (*argv && isdigit (**argv))
472 dashes[ndashes++] = atoi(*argv++);
475 #ifdef MATCH_ARGUMENT
476 else if (i = MatchArgument (argv))
479 else if (!strcmp (*argv, "-root"))
480 root = strtol (*++argv, NULL, 0);
485 wmHints.flags = InputHint;
486 wmHints.input = True;
487 classHints.res_name = init_argv[0];
488 classHints.res_class = init_argv[0];
489 dpy = XOpenDisplay (dpy_name);
491 Error ("Can't open display", "");
493 XSynchronize (dpy, sync);
494 screen = DefaultScreen (dpy);
496 root = RootWindow (dpy, screen);
497 window_mask = CWBackPixel|CWBorderPixel|CWEventMask;
499 colormap = DefaultColormap (dpy, screen);
502 window_mask |= CWColormap;
503 attr.colormap = colormap;
505 visual = DefaultVisual (dpy, screen);
506 depth = DefaultDepth (dpy, screen);
509 XVisualInfo vi, *vi_ret;
514 vi_ret = XGetVisualInfo (dpy, VisualIDMask|VisualScreenMask,
518 visual = vi_ret->visual;
521 colormap = XCreateColormap (dpy, root, visual, AllocNone);
522 window_mask |= CWColormap;
523 attr.colormap = colormap;
525 depth = vi_ret->depth;
529 fg_pixel = BlackPixel (dpy, screen);
531 bg_pixel = WhitePixel (dpy, screen);
534 if (!XAllocNamedColor (dpy, colormap, fg, &hard, &exact))
535 Error ("Can't allocate fg pixel %s", fg);
536 fg_pixel = hard.pixel;
540 if (!XAllocNamedColor (dpy, colormap, bg, &hard, &exact))
541 Error ("Can't allocate bg pixel %s", bg);
542 bg_pixel = hard.pixel;
544 attr.background_pixel = bg_pixel;
545 attr.border_pixel = fg_pixel;
547 ExposureMask|KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask|MOTION|EXTRA_EVENTS;
548 // attr.override_redirect = True;
549 // window_mask |= CWOverrideRedirect;
550 wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
551 win = XCreateWindow (dpy, root, x, y, width, height, border_width,
559 XSetWMProperties (dpy, win,
560 &wm_name, &icon_name,
562 &sizeHints, &wmHints, 0);
563 XSetWMProtocols (dpy, win, &wm_delete_window, 1);
564 default_rop = RopNameToRop (rop_name);
567 gcv.line_width = (int) line_width;
568 gc_mask |= GCLineWidth;
572 gcv.cap_style = MatchCapStyle (cap_name);
573 gc_mask |= GCCapStyle;
576 gcv.fill_style = MatchFillStyle (fill_name);
577 gc_mask |= GCFillStyle;
580 gcv.line_style = MatchLineStyle (line_name);
581 gc_mask |= GCLineStyle;
584 gcv.join_style = MatchJoinStyle (join_name);
585 gc_mask |= GCJoinStyle;
588 default_font = XLoadQueryFont (dpy, font_name);
590 gcv.font = default_font->fid;
593 gcv.function = default_rop;
594 gcv.foreground = fg_pixel;
595 gcv.background = bg_pixel;
596 gcv.plane_mask = planemask;
597 gc_mask |= GCFunction|GCForeground|GCBackground|GCPlaneMask;
600 unsigned int width, height;
602 Pixmap bitmap, pixmap;
604 ret = XReadBitmapFile (dpy, win, bitmap_file, &width, &height,
605 &bitmap, (int *) 0, (int *) 0);
607 case BitmapOpenFailed:
608 Error ("Can't open bitmap file %s", bitmap_file);
609 case BitmapFileInvalid:
610 Error ("Bitmap file %s invalid", bitmap_file);
612 Error ("Out of memory reading bitmap file %s", bitmap_file);
616 switch (gcv.fill_style) {
618 pixmap = XCreatePixmap (dpy, win, width, height, DefaultDepth (dpy, screen));
619 gc = XCreateGC (dpy, pixmap, GCForeground|GCBackground, &gcv);
620 XCopyPlane (dpy, bitmap, pixmap, gc, 0, 0, width, height, 0, 0, 1);
622 XFreePixmap (dpy, bitmap);
627 case FillOpaqueStippled:
628 gcv.stipple = bitmap;
629 gc_mask |= GCStipple;
633 gc = XCreateGC (dpy, win, gc_mask, &gcv);
635 default_font = XQueryFont (dpy, XGContextFromGC(gc));
637 XSetDashes (dpy, gc, 0, dashes, ndashes);
638 XMapWindow (dpy, win);
640 XNextEvent (dpy, &ev);
642 if (HasMotion && ev.type != MotionNotify) {
644 HandleMotionNotify (dpy, win, gc, &mev);
647 #ifdef COMPRESS_EXPOSE
648 if (HasExpose && ev.type != Expose) {
650 HandleExpose (dpy, eev.xexpose.window, gc);
655 #ifdef COMPRESS_EXPOSE
659 } else if (ev.xexpose.count == 0)
662 HandleExpose (dpy, ev.xexpose.window, gc);
666 HandleKeyPress (dpy, ev.xkey.window, gc, &ev);
669 HandleKeyRelease (dpy, ev.xkey.window, gc, &ev);
673 if (XLookupString ((XKeyEvent *) &ev, quit_string, sizeof (quit_string), 0, 0) == 1) {
674 switch (quit_string[0]) {
678 XClearArea (dpy, ev.xkey.window, 0, 0, 0, 0, True);
686 HandleButtonPress (dpy, ev.xbutton.window, gc, &ev);
689 HandleButtonRelease (dpy, ev.xbutton.window, gc, &ev);
696 HandleMotionNotify (dpy, ev.xmotion.window, gc, &ev);
704 HandleOtherEvent (dpy, win, gc, &ev);
710 #include <X11/extensions/Xrender.h>
712 XRenderColor renderBlack = { 0, 0, 0, 0xffff };
713 XRenderColor renderWhite = { 0xffff, 0xffff, 0xffff, 0xffff };
714 XRenderColor renderRed = { 0xffff, 0, 0, 0xffff };
715 XRenderColor renderGreen = { 0, 0xffff, 0, 0xffff };
716 XRenderColor renderBlue = { 0, 0, 0xffff, 0xffff };
718 XRenderColor renderClearRed = { 0x8000, 0, 0, 0x8000 };
719 XRenderColor renderClearGreen = { 0, 0x8000, 0, 0x8000 };
720 XRenderColor renderClearBlue = { 0, 0, 0x8000, 0x8000 };
723 GetPicture (Display *dpy, Window w)
728 p = XRenderCreatePicture (dpy, w,
729 XRenderFindVisualFormat (dpy, visual),
735 GetSrc (Display *dpy, XRenderColor *color)
739 static XRenderColor lastColor;
740 XRenderPictFormat *f;
741 XRenderPictureAttributes attr;
743 if (p && !memcmp (color, &lastColor, sizeof (XRenderColor)))
747 f = XRenderFindStandardFormat (dpy, PictStandardARGB32);
748 pix = XCreatePixmap (dpy, RootWindow (dpy, DefaultScreen (dpy)),
751 p = XRenderCreatePicture (dpy, pix, f, CPRepeat, &attr);
752 XFreePixmap (dpy, pix);
754 XRenderFillRectangle (dpy, PictOpSrc, p, color, 0, 0, 1, 1);
760 GetMask (Display *dpy)
762 return XRenderFindStandardFormat (dpy, PictStandardA8);