xrectdraw

Draw geometry of a rectangular screen region
git clone https://noxz.tech/git/xrectdraw.git
Log | Files | README | LICENSE

xrectdraw.c
1/*
2 * MIT License
3 *
4 * © 2019 Chris Noxz <chris@noxz.tech>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24#include <stdio.h>
25#include <stdlib.h>
26#include <stdarg.h>
27#include <string.h>
28
29#include <X11/Xlib.h>
30#include <X11/Xutil.h>
31
32#include "config.def.h"
33
34struct Border {
35    int top;
36    int right;
37    int bottom;
38    int left;
39};
40
41static int screen, gfocus;
42static Display *dpy;
43static Window root, win[4];
44static XColor color[2];
45static struct Border border;
46
47void
48die(char *format, ...)
49{
50    va_list args;
51
52    va_start(args, format);
53    vprintf(format, args);
54    va_end(args);
55
56    exit(1);
57}
58
59void
60draw(int x, int y, int width, int height)
61{
62    int i, ix, iy, iw, ih;
63    XWindowAttributes wa;
64    XSetWindowAttributes swa;
65    XClassHint ch = {"xrectdraw", "xrectdraw"};
66
67    swa.border_pixel = 0;
68    swa.background_pixel = color[0].pixel;
69    swa.override_redirect = 1;
70    swa.event_mask = ExposureMask
71        | KeyPressMask
72        | ButtonPressMask
73        | FocusChangeMask;
74
75    XGetWindowAttributes(dpy, root, &wa);
76
77    for (i = 0; i < 4; i++) {
78        switch (i) {
79        case 0: // setup for left border
80            ix = x - border.left;
81            iy = y - border.top;
82            iw = border.left;
83            ih = height + border.bottom;
84            break;
85        case 1: // setup for right border
86            ix += (width + border.left);
87            iw = border.right;
88            break;
89        case 2: // setup for top border
90            ix -= (width + border.left);
91            iw = (width + border.left + border.right);
92            ih = border.top;
93            break;
94        case 3: // setup for bottom border
95            ih = border.bottom;
96            iy += (height + border.top);
97            break;
98        }
99
100        win[i] = XCreateWindow(dpy, root, ix, iy, iw, ih, 0,
101            CopyFromParent, CopyFromParent, CopyFromParent,
102            CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
103
104        XSetClassHint(dpy, win[i], &ch);
105        XMapRaised(dpy, win[i]);
106        XSetInputFocus(dpy, win[i], RevertToParent, CurrentTime);
107    }
108}
109
110int
111hex(const char c)
112{
113    if (c >= 0x30 && c <= 0x39)
114        return c - 0x30;
115    else if (c >= 0x41 && c <= 0x46)
116        return c - 0x41 + 10;
117    else if (c >= 0x61 && c <= 0x66)
118        return c - 0x61 + 10;
119    else
120        puts("error: cannot set color");
121    return 0;
122}
123
124void
125setcolor(const char *clr)
126{
127    int i, r;
128    const char *ptr;
129
130    color[0].flags = DoRed | DoGreen | DoBlue;
131    color[1].flags = DoRed | DoGreen | DoBlue;
132
133    for (i = 0; i < 3; i++) {
134        ptr = clr + (1 + i * 2);
135        r = (hex(ptr[0]) * 16 + hex(ptr[1])) * 256;
136
137        switch (i) {
138        case 0:
139            color[0].red = r;
140            color[1].red = r * dim_percentage;
141            break;
142        case 1:
143            color[0].green = r;
144            color[1].green = r * dim_percentage;
145            break;
146        case 2:
147            color[0].blue = r;
148            color[1].blue = r * dim_percentage;
149            break;
150        }
151    }
152
153    XAllocColor(dpy, DefaultColormap(dpy, screen), &color[0]);
154    XAllocColor(dpy, DefaultColormap(dpy, screen), &color[1]);
155}
156
157void
158toggle()
159{
160    if (!gfocus)
161        XSetInputFocus(dpy, win[0], RevertToParent, CurrentTime);
162    else
163        XSetInputFocus(dpy, None, RevertToParent, CurrentTime);
164}
165
166void
167highlight(int focus)
168{
169    unsigned long pixel = color[!(gfocus = focus)].pixel;
170
171    for (int i = 0; i < 4; i++) {
172        XSetWindowBackground(dpy, win[i], pixel);
173        XClearArea(dpy, win[i], 0, 0, 0, 0, True);
174    }
175}
176
177void
178setborders(const char *str)
179{
180    if (sscanf(str, "%d:%d:%d:%d",
181        &border.top,
182        &border.right,
183        &border.bottom,
184        &border.left) != 4)
185        die("error: borders should be entered in the format 't:r:b:l'");
186}
187
188int
189main(int argc, const char *argv[])
190{
191    int x, y, width, height;
192
193    border.top = BORDER_TOP;
194    border.right = BORDER_RIGHT;
195    border.bottom = BORDER_BOTTOM;
196    border.left = BORDER_LEFT;
197
198    if (argc == 7 && argc--)
199        setborders(argv[6]);
200
201    if (argc != 6
202        || strcmp(argv[1], "-h") == 0
203        || strcmp(argv[1], "--help") == 0)
204        die(
205            "usage: %s x y width height #RRGGBB [t:r:b:l]\n"
206            "  x        x coordinage on screen\n"
207            "  y        y coordinage on screen\n"
208            "  width    width of the rectangle\n"
209            "  height   height of the rectangle (downwards)\n"
210            "  #RRGGBB  hex border colors of the rectangle\n"
211            "  t:r:b:l  (optional) border widths: top, right, bottom & left\n"
212            "           e.g. 2:2:5:2. Default is 1:1:1:1\n\n"
213            "Use ESC key to kill the program when focused. Clicking on the\n"
214            "borders toggles the focus.\n"
215        , argv[0]);
216
217    if (!(dpy = XOpenDisplay(NULL)))
218        die("error: cannot open display");
219
220    screen = DefaultScreen(dpy);
221    root = RootWindow(dpy, screen);
222
223    x = atoi(argv[1]);
224    y = atoi(argv[2]);
225    width = atoi(argv[3]);
226    height = atoi(argv[4]);
227    setcolor(argv[5]);
228
229    draw(x, y, width, height);
230
231    XEvent ev;
232    for (;;) {
233        XNextEvent(dpy, &ev);
234        if (ev.type == Expose)
235            continue;
236        else if (ev.type == KeyPress && ev.xkey.keycode == exit_key)
237            break;
238        else if (ev.type == ButtonPress)
239            toggle();
240        else if (ev.type == FocusIn)
241            highlight(True);
242        else if (ev.type == FocusOut)
243            highlight(False);
244    }
245
246    XCloseDisplay(dpy);
247    return 0;
248}