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}