Blame


1 15aae8ae 2023-05-28 aa #include <ctype.h>
2 15aae8ae 2023-05-28 aa #include <errno.h>
3 cb28fcb8 2023-05-26 aa #include <stdarg.h>
4 15aae8ae 2023-05-28 aa #include <stdbool.h>
5 cb28fcb8 2023-05-26 aa #include <stdio.h>
6 cb28fcb8 2023-05-26 aa #include <stdlib.h>
7 cb28fcb8 2023-05-26 aa #include <string.h>
8 15aae8ae 2023-05-28 aa #include <time.h>
9 cb28fcb8 2023-05-26 aa #include <unistd.h>
10 cb28fcb8 2023-05-26 aa
11 cb28fcb8 2023-05-26 aa #include <X11/Xlib.h>
12 cb28fcb8 2023-05-26 aa #include <X11/Xutil.h>
13 cb28fcb8 2023-05-26 aa #include <X11/Xos.h>
14 cb28fcb8 2023-05-26 aa #include <X11/Xatom.h>
15 cb28fcb8 2023-05-26 aa
16 7c7bbaee 2023-05-27 aa #define PROGNAME "xitems"
17 7c7bbaee 2023-05-27 aa
18 cb28fcb8 2023-05-26 aa #define LEN(A) (sizeof(A)/sizeof((A)[0]))
19 cb28fcb8 2023-05-26 aa
20 15aae8ae 2023-05-28 aa /* usage -- print usage information and die. */
21 cb28fcb8 2023-05-26 aa static void
22 7c7bbaee 2023-05-27 aa usage(void)
23 7c7bbaee 2023-05-27 aa {
24 15aae8ae 2023-05-28 aa printf(
25 15aae8ae 2023-05-28 aa "usage: " PROGNAME " [-font font] [-bg colour] [-fg colour]\n"
26 15aae8ae 2023-05-28 aa " [-sbg colour] [-sfg colour] [-bw width]\n"
27 15aae8ae 2023-05-28 aa " [-hp padding] [-vp padding] [-x x] [-y y]\n");
28 7c7bbaee 2023-05-27 aa exit(1);
29 7c7bbaee 2023-05-27 aa }
30 7c7bbaee 2023-05-27 aa
31 15aae8ae 2023-05-28 aa /* die -- print formatted string, and exit with the status eval. */
32 7c7bbaee 2023-05-27 aa static void
33 15aae8ae 2023-05-28 aa die(int eval, const char *fmt, ...)
34 cb28fcb8 2023-05-26 aa {
35 7c7bbaee 2023-05-27 aa fputs(PROGNAME ": ", stderr);
36 cb28fcb8 2023-05-26 aa if (fmt) {
37 cb28fcb8 2023-05-26 aa va_list argp;
38 cb28fcb8 2023-05-26 aa va_start(argp, fmt);
39 cb28fcb8 2023-05-26 aa vfprintf(stderr, fmt, argp);
40 cb28fcb8 2023-05-26 aa va_end(argp);
41 cb28fcb8 2023-05-26 aa }
42 cb28fcb8 2023-05-26 aa fputc('\n', stderr);
43 cb28fcb8 2023-05-26 aa exit(eval);
44 cb28fcb8 2023-05-26 aa }
45 cb28fcb8 2023-05-26 aa
46 6cdc5a3d 2023-05-28 aa enum direction {
47 6cdc5a3d 2023-05-28 aa DIR_UP,
48 6cdc5a3d 2023-05-28 aa DIR_DOWN
49 6cdc5a3d 2023-05-28 aa };
50 6cdc5a3d 2023-05-28 aa
51 cb28fcb8 2023-05-26 aa /* doubly-linked cyclic list */
52 cb28fcb8 2023-05-26 aa struct item {
53 cb28fcb8 2023-05-26 aa size_t len; /* length of string */
54 cb28fcb8 2023-05-26 aa char *s;
55 cb28fcb8 2023-05-26 aa KeySym *ks; /* NoSym-terminated array of keysyms */
56 cb28fcb8 2023-05-26 aa struct item *prev;
57 cb28fcb8 2023-05-26 aa struct item *next;
58 15aae8ae 2023-05-28 aa bool dirty; /* should be redrawn */
59 cb28fcb8 2023-05-26 aa };
60 cb28fcb8 2023-05-26 aa
61 15aae8ae 2023-05-28 aa static struct item *first = NULL; /* first member of the list */
62 cb28fcb8 2023-05-26 aa static struct item *selected = NULL;
63 15aae8ae 2023-05-28 aa
64 15aae8ae 2023-05-28 aa /* command-line options and X resources */
65 15aae8ae 2023-05-28 aa static char *o_font = NULL;
66 15aae8ae 2023-05-28 aa static char *o_bg = NULL, *o_fg = NULL, *o_sbg = NULL, *o_sfg = NULL;
67 15aae8ae 2023-05-28 aa static int o_x = -1, o_y = -1;
68 15aae8ae 2023-05-28 aa static int o_bw = -1;
69 15aae8ae 2023-05-28 aa static int o_hp = -1, o_vp = -1;
70 15aae8ae 2023-05-28 aa
71 15aae8ae 2023-05-28 aa /* X globals */
72 15aae8ae 2023-05-28 aa static Display *dpy = NULL;
73 cb28fcb8 2023-05-26 aa static int screen;
74 15aae8ae 2023-05-28 aa static Window win;
75 15aae8ae 2023-05-28 aa static GC gc_sel, gc_norm;
76 15aae8ae 2023-05-28 aa static Pixmap pm_sel, pm_norm; /* background pixmaps */
77 15aae8ae 2023-05-28 aa static XFontStruct *font;
78 15aae8ae 2023-05-28 aa static int height, width; /* height and width of one item */
79 cb28fcb8 2023-05-26 aa
80 6cdc5a3d 2023-05-28 aa /* newsel -- mark the item at position y (from top) as selected. */
81 6cdc5a3d 2023-05-28 aa static void
82 6cdc5a3d 2023-05-28 aa newsel(int y)
83 6cdc5a3d 2023-05-28 aa {
84 6cdc5a3d 2023-05-28 aa struct item *unselected = selected;
85 6cdc5a3d 2023-05-28 aa int top = 1;
86 6cdc5a3d 2023-05-28 aa
87 6cdc5a3d 2023-05-28 aa selected = first;
88 6cdc5a3d 2023-05-28 aa
89 6cdc5a3d 2023-05-28 aa if (y <= 0)
90 6cdc5a3d 2023-05-28 aa goto end;
91 6cdc5a3d 2023-05-28 aa
92 6cdc5a3d 2023-05-28 aa do {
93 6cdc5a3d 2023-05-28 aa if (y >= top && y < top+height)
94 6cdc5a3d 2023-05-28 aa break;
95 6cdc5a3d 2023-05-28 aa top += height;
96 6cdc5a3d 2023-05-28 aa selected = selected->next;
97 6cdc5a3d 2023-05-28 aa } while (selected != first->prev);
98 6cdc5a3d 2023-05-28 aa
99 6cdc5a3d 2023-05-28 aa end:
100 6cdc5a3d 2023-05-28 aa if (unselected != selected)
101 6cdc5a3d 2023-05-28 aa unselected->dirty = selected->dirty = true;
102 6cdc5a3d 2023-05-28 aa }
103 6cdc5a3d 2023-05-28 aa
104 15aae8ae 2023-05-28 aa /* redraw -- redraw the entire window */
105 15aae8ae 2023-05-28 aa static void
106 15aae8ae 2023-05-28 aa redraw(void)
107 15aae8ae 2023-05-28 aa {
108 15aae8ae 2023-05-28 aa struct item *it = first;
109 15aae8ae 2023-05-28 aa int y = 0;
110 15aae8ae 2023-05-28 aa
111 15aae8ae 2023-05-28 aa do {
112 15aae8ae 2023-05-28 aa if (it->dirty) {
113 15aae8ae 2023-05-28 aa GC gc;
114 15aae8ae 2023-05-28 aa Pixmap pm;
115 15aae8ae 2023-05-28 aa
116 15aae8ae 2023-05-28 aa if (it == selected) {
117 15aae8ae 2023-05-28 aa gc = gc_sel;
118 15aae8ae 2023-05-28 aa pm = pm_sel;
119 15aae8ae 2023-05-28 aa } else {
120 15aae8ae 2023-05-28 aa gc = gc_norm;
121 15aae8ae 2023-05-28 aa pm = pm_norm;
122 15aae8ae 2023-05-28 aa }
123 15aae8ae 2023-05-28 aa
124 15aae8ae 2023-05-28 aa XCopyArea(dpy, pm, win, gc, 0, 0, width, height, 0, y);
125 15aae8ae 2023-05-28 aa XDrawString(dpy, win, gc, o_hp, y + o_vp+font->ascent,
126 15aae8ae 2023-05-28 aa it->s, it->len);
127 15aae8ae 2023-05-28 aa
128 15aae8ae 2023-05-28 aa it->dirty = false;
129 15aae8ae 2023-05-28 aa }
130 15aae8ae 2023-05-28 aa
131 15aae8ae 2023-05-28 aa y += height;
132 15aae8ae 2023-05-28 aa it = it->next;
133 15aae8ae 2023-05-28 aa } while (it != first);
134 15aae8ae 2023-05-28 aa }
135 15aae8ae 2023-05-28 aa
136 6cdc5a3d 2023-05-28 aa /* succeed -- exit successfully. If print is true, also print selected item. */
137 6cdc5a3d 2023-05-28 aa static void
138 6cdc5a3d 2023-05-28 aa succeed(bool print)
139 6cdc5a3d 2023-05-28 aa {
140 6cdc5a3d 2023-05-28 aa if (print && selected)
141 6cdc5a3d 2023-05-28 aa puts(selected->s);
142 6cdc5a3d 2023-05-28 aa exit(0);
143 6cdc5a3d 2023-05-28 aa }
144 6cdc5a3d 2023-05-28 aa
145 6cdc5a3d 2023-05-28 aa /* scroll -- select the previous, or next item, depending on dir. */
146 6cdc5a3d 2023-05-28 aa static void
147 6cdc5a3d 2023-05-28 aa scroll(enum direction dir)
148 6cdc5a3d 2023-05-28 aa {
149 6cdc5a3d 2023-05-28 aa if (selected) {
150 6cdc5a3d 2023-05-28 aa selected->dirty = true;
151 6cdc5a3d 2023-05-28 aa selected = (dir == DIR_UP) ? selected->prev : selected->next;
152 6cdc5a3d 2023-05-28 aa } else
153 6cdc5a3d 2023-05-28 aa selected = (dir == DIR_UP) ? first->prev : first;
154 6cdc5a3d 2023-05-28 aa selected->dirty = true;
155 6cdc5a3d 2023-05-28 aa }
156 6cdc5a3d 2023-05-28 aa
157 15aae8ae 2023-05-28 aa /* proc -- body of the main event-reading loop. */
158 15aae8ae 2023-05-28 aa static void
159 15aae8ae 2023-05-28 aa proc(void)
160 15aae8ae 2023-05-28 aa {
161 15aae8ae 2023-05-28 aa XKeyEvent ke;
162 15aae8ae 2023-05-28 aa KeySym ks;
163 15aae8ae 2023-05-28 aa char *dummy = "";
164 15aae8ae 2023-05-28 aa XEvent ev;
165 15aae8ae 2023-05-28 aa
166 6cdc5a3d 2023-05-28 aa static bool inwin = false;
167 6cdc5a3d 2023-05-28 aa
168 15aae8ae 2023-05-28 aa XNextEvent(dpy, &ev);
169 15aae8ae 2023-05-28 aa
170 15aae8ae 2023-05-28 aa /* XXX try to avoid full redraws */
171 15aae8ae 2023-05-28 aa switch (ev.type) {
172 6cdc5a3d 2023-05-28 aa case EnterNotify:
173 6cdc5a3d 2023-05-28 aa inwin = true;
174 6cdc5a3d 2023-05-28 aa /* FALLTHRU */
175 6cdc5a3d 2023-05-28 aa case MotionNotify:
176 6cdc5a3d 2023-05-28 aa newsel(ev.xbutton.y);
177 6cdc5a3d 2023-05-28 aa /* FALLTHRU */
178 15aae8ae 2023-05-28 aa case Expose:
179 15aae8ae 2023-05-28 aa redraw();
180 15aae8ae 2023-05-28 aa break;
181 6cdc5a3d 2023-05-28 aa case LeaveNotify:
182 6cdc5a3d 2023-05-28 aa inwin = false;
183 6cdc5a3d 2023-05-28 aa break;
184 6cdc5a3d 2023-05-28 aa case ButtonPress:
185 6cdc5a3d 2023-05-28 aa if (ev.xbutton.button == Button4) {
186 6cdc5a3d 2023-05-28 aa scroll(DIR_UP);
187 6cdc5a3d 2023-05-28 aa redraw();
188 6cdc5a3d 2023-05-28 aa } else if (ev.xbutton.button == Button5) {
189 6cdc5a3d 2023-05-28 aa scroll(DIR_DOWN);
190 6cdc5a3d 2023-05-28 aa redraw();
191 6cdc5a3d 2023-05-28 aa } else if (inwin)
192 6cdc5a3d 2023-05-28 aa succeed(true);
193 6cdc5a3d 2023-05-28 aa else
194 6cdc5a3d 2023-05-28 aa succeed(false);
195 6cdc5a3d 2023-05-28 aa break;
196 15aae8ae 2023-05-28 aa case KeyPress:
197 15aae8ae 2023-05-28 aa ke = ev.xkey;
198 15aae8ae 2023-05-28 aa XLookupString(&ke, dummy, 0, &ks, NULL);
199 15aae8ae 2023-05-28 aa
200 15aae8ae 2023-05-28 aa if (ke.state & ControlMask) {
201 15aae8ae 2023-05-28 aa switch (ks) {
202 15aae8ae 2023-05-28 aa case XK_bracketleft:
203 15aae8ae 2023-05-28 aa case XK_C:
204 15aae8ae 2023-05-28 aa case XK_c:
205 15aae8ae 2023-05-28 aa ks = XK_Escape;
206 15aae8ae 2023-05-28 aa break;
207 15aae8ae 2023-05-28 aa case XK_M:
208 15aae8ae 2023-05-28 aa case XK_m:
209 15aae8ae 2023-05-28 aa case XK_J:
210 15aae8ae 2023-05-28 aa case XK_j:
211 15aae8ae 2023-05-28 aa ks = XK_Return;
212 15aae8ae 2023-05-28 aa break;
213 15aae8ae 2023-05-28 aa case XK_N:
214 15aae8ae 2023-05-28 aa case XK_n:
215 15aae8ae 2023-05-28 aa ks = XK_j;
216 15aae8ae 2023-05-28 aa return;
217 15aae8ae 2023-05-28 aa case XK_P:
218 15aae8ae 2023-05-28 aa case XK_p:
219 15aae8ae 2023-05-28 aa ks = XK_k;
220 15aae8ae 2023-05-28 aa return;
221 15aae8ae 2023-05-28 aa }
222 15aae8ae 2023-05-28 aa }
223 15aae8ae 2023-05-28 aa
224 15aae8ae 2023-05-28 aa switch (ks) {
225 15aae8ae 2023-05-28 aa case XK_j:
226 15aae8ae 2023-05-28 aa case XK_J:
227 15aae8ae 2023-05-28 aa case XK_Down:
228 6cdc5a3d 2023-05-28 aa scroll(DIR_DOWN);
229 15aae8ae 2023-05-28 aa redraw();
230 15aae8ae 2023-05-28 aa break;
231 15aae8ae 2023-05-28 aa case XK_k:
232 15aae8ae 2023-05-28 aa case XK_K:
233 15aae8ae 2023-05-28 aa case XK_Up:
234 6cdc5a3d 2023-05-28 aa scroll(DIR_UP);
235 15aae8ae 2023-05-28 aa redraw();
236 15aae8ae 2023-05-28 aa break;
237 15aae8ae 2023-05-28 aa case XK_Return:
238 6cdc5a3d 2023-05-28 aa succeed(true);
239 15aae8ae 2023-05-28 aa /* NOTREACHED */
240 15aae8ae 2023-05-28 aa case XK_Escape:
241 6cdc5a3d 2023-05-28 aa succeed(false);
242 15aae8ae 2023-05-28 aa /* NOTREACHED */
243 15aae8ae 2023-05-28 aa }
244 15aae8ae 2023-05-28 aa
245 15aae8ae 2023-05-28 aa break;
246 6cdc5a3d 2023-05-28 aa }
247 6cdc5a3d 2023-05-28 aa }
248 6cdc5a3d 2023-05-28 aa
249 6cdc5a3d 2023-05-28 aa /* grabptr -- try to grab pointer for a second */
250 6cdc5a3d 2023-05-28 aa static void
251 6cdc5a3d 2023-05-28 aa grabptr(void)
252 6cdc5a3d 2023-05-28 aa {
253 6cdc5a3d 2023-05-28 aa struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 };
254 6cdc5a3d 2023-05-28 aa int i;
255 6cdc5a3d 2023-05-28 aa
256 6cdc5a3d 2023-05-28 aa for (i = 0; i < 1000; ++i) {
257 6cdc5a3d 2023-05-28 aa if (XGrabPointer(dpy, RootWindow(dpy, screen), True,
258 6cdc5a3d 2023-05-28 aa ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None,
259 6cdc5a3d 2023-05-28 aa CurrentTime) == GrabSuccess)
260 6cdc5a3d 2023-05-28 aa return;
261 6cdc5a3d 2023-05-28 aa nanosleep(&ts, NULL);
262 15aae8ae 2023-05-28 aa }
263 6cdc5a3d 2023-05-28 aa die(1, "couldn't grab pointer");
264 15aae8ae 2023-05-28 aa }
265 15aae8ae 2023-05-28 aa
266 15aae8ae 2023-05-28 aa /* grabkb -- try to grab keyboard for a second */
267 15aae8ae 2023-05-28 aa static void
268 15aae8ae 2023-05-28 aa grabkb(void)
269 15aae8ae 2023-05-28 aa {
270 15aae8ae 2023-05-28 aa struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 };
271 15aae8ae 2023-05-28 aa int i;
272 15aae8ae 2023-05-28 aa
273 15aae8ae 2023-05-28 aa for (i = 0; i < 1000; ++i) {
274 15aae8ae 2023-05-28 aa if (XGrabKeyboard(dpy, RootWindow(dpy, screen), True,
275 15aae8ae 2023-05-28 aa GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess)
276 15aae8ae 2023-05-28 aa return;
277 15aae8ae 2023-05-28 aa nanosleep(&ts, NULL);
278 15aae8ae 2023-05-28 aa }
279 15aae8ae 2023-05-28 aa die(1, "couldn't grab keyboard");
280 15aae8ae 2023-05-28 aa }
281 15aae8ae 2023-05-28 aa
282 6cdc5a3d 2023-05-28 aa /* setfocus -- try setting focus to the menu for a second */
283 6cdc5a3d 2023-05-28 aa static void
284 6cdc5a3d 2023-05-28 aa setfocus(void)
285 6cdc5a3d 2023-05-28 aa {
286 6cdc5a3d 2023-05-28 aa struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 };
287 6cdc5a3d 2023-05-28 aa Window focuswin;
288 6cdc5a3d 2023-05-28 aa int i, dummy;
289 6cdc5a3d 2023-05-28 aa
290 6cdc5a3d 2023-05-28 aa for (i = 0; i < 1000; ++i) {
291 6cdc5a3d 2023-05-28 aa XGetInputFocus(dpy, &focuswin, &dummy);
292 6cdc5a3d 2023-05-28 aa if (focuswin == win)
293 6cdc5a3d 2023-05-28 aa return;
294 6cdc5a3d 2023-05-28 aa XSetInputFocus(dpy, win, RevertToParent, CurrentTime);
295 6cdc5a3d 2023-05-28 aa nanosleep(&ts, NULL);
296 6cdc5a3d 2023-05-28 aa }
297 6cdc5a3d 2023-05-28 aa die(1, "couldn't grab keyboard");
298 6cdc5a3d 2023-05-28 aa }
299 6cdc5a3d 2023-05-28 aa
300 15aae8ae 2023-05-28 aa /*
301 15aae8ae 2023-05-28 aa * setupx -- create and map a window for n items; assign values to the X
302 15aae8ae 2023-05-28 aa * globals.
303 15aae8ae 2023-05-28 aa */
304 15aae8ae 2023-05-28 aa static void
305 15aae8ae 2023-05-28 aa setupx(int n)
306 15aae8ae 2023-05-28 aa {
307 15aae8ae 2023-05-28 aa struct item *it;
308 15aae8ae 2023-05-28 aa XGCValues gcv;
309 15aae8ae 2023-05-28 aa XColor col, dummy;
310 15aae8ae 2023-05-28 aa XClassHint ch = {PROGNAME, PROGNAME};
311 15aae8ae 2023-05-28 aa XSetWindowAttributes swa = {
312 15aae8ae 2023-05-28 aa .override_redirect = True,
313 15aae8ae 2023-05-28 aa .save_under = True,
314 6cdc5a3d 2023-05-28 aa .event_mask = ExposureMask | StructureNotifyMask |
315 6cdc5a3d 2023-05-28 aa KeyPressMask | ButtonPressMask | ButtonReleaseMask |
316 6cdc5a3d 2023-05-28 aa PointerMotionMask | LeaveWindowMask | EnterWindowMask,
317 15aae8ae 2023-05-28 aa };
318 15aae8ae 2023-05-28 aa
319 15aae8ae 2023-05-28 aa if (!(font = XLoadQueryFont(dpy, o_font)))
320 15aae8ae 2023-05-28 aa die(1, "couldn't load font");
321 15aae8ae 2023-05-28 aa height = font->ascent + font->descent + o_vp;
322 15aae8ae 2023-05-28 aa
323 15aae8ae 2023-05-28 aa it = first;
324 15aae8ae 2023-05-28 aa do {
325 15aae8ae 2023-05-28 aa int w;
326 15aae8ae 2023-05-28 aa if ((w = XTextWidth(font, it->s, it->len) + o_hp*2) > width)
327 15aae8ae 2023-05-28 aa width = w;
328 15aae8ae 2023-05-28 aa it = it->next;
329 15aae8ae 2023-05-28 aa } while (it != first);
330 15aae8ae 2023-05-28 aa
331 15aae8ae 2023-05-28 aa XAllocNamedColor(dpy, DefaultColormap(dpy, screen), o_bg, &col, &dummy);
332 15aae8ae 2023-05-28 aa swa.background_pixel = col.pixel;
333 15aae8ae 2023-05-28 aa win = XCreateWindow(dpy, RootWindow(dpy, screen), o_x, o_y,
334 15aae8ae 2023-05-28 aa width, n*height, o_bw, CopyFromParent, CopyFromParent,
335 15aae8ae 2023-05-28 aa CopyFromParent, CWOverrideRedirect | CWBackPixel | CWEventMask |
336 15aae8ae 2023-05-28 aa CWSaveUnder, &swa);
337 15aae8ae 2023-05-28 aa XSetClassHint(dpy, win, &ch);
338 15aae8ae 2023-05-28 aa
339 15aae8ae 2023-05-28 aa /*
340 15aae8ae 2023-05-28 aa * Foreground here means the colour with which to draw the background
341 15aae8ae 2023-05-28 aa * pixmap, i.e. the actual background colour.
342 15aae8ae 2023-05-28 aa */
343 15aae8ae 2023-05-28 aa gcv.foreground = col.pixel;
344 15aae8ae 2023-05-28 aa gc_norm = XCreateGC(dpy, win, GCForeground, &gcv);
345 15aae8ae 2023-05-28 aa XAllocNamedColor(dpy, DefaultColormap(dpy, screen), o_sbg, &col,
346 15aae8ae 2023-05-28 aa &dummy);
347 15aae8ae 2023-05-28 aa gcv.foreground = col.pixel;
348 15aae8ae 2023-05-28 aa gc_sel = XCreateGC(dpy, win, GCForeground, &gcv);
349 15aae8ae 2023-05-28 aa
350 15aae8ae 2023-05-28 aa pm_sel = XCreatePixmap(dpy, win, width, height,
351 15aae8ae 2023-05-28 aa DefaultDepth(dpy, screen));
352 15aae8ae 2023-05-28 aa pm_norm = XCreatePixmap(dpy, win, width, height,
353 15aae8ae 2023-05-28 aa DefaultDepth(dpy, screen));
354 15aae8ae 2023-05-28 aa XFillRectangle(dpy, pm_sel, gc_sel, 0, 0, width, height);
355 15aae8ae 2023-05-28 aa XFillRectangle(dpy, pm_norm, gc_norm, 0, 0, width, height);
356 15aae8ae 2023-05-28 aa
357 15aae8ae 2023-05-28 aa /*
358 15aae8ae 2023-05-28 aa * Since the background pixmaps are already created, the GCs can be
359 15aae8ae 2023-05-28 aa * reused for text.
360 15aae8ae 2023-05-28 aa */
361 15aae8ae 2023-05-28 aa XAllocNamedColor(dpy, DefaultColormap(dpy, screen), o_fg, &col,
362 15aae8ae 2023-05-28 aa &dummy);
363 15aae8ae 2023-05-28 aa XSetForeground(dpy, gc_norm, col.pixel);
364 15aae8ae 2023-05-28 aa XAllocNamedColor(dpy, DefaultColormap(dpy, screen), o_sfg, &col,
365 15aae8ae 2023-05-28 aa &dummy);
366 15aae8ae 2023-05-28 aa XSetForeground(dpy, gc_sel, col.pixel);
367 15aae8ae 2023-05-28 aa
368 15aae8ae 2023-05-28 aa grabkb();
369 6cdc5a3d 2023-05-28 aa grabptr();
370 15aae8ae 2023-05-28 aa
371 15aae8ae 2023-05-28 aa XMapRaised(dpy, win);
372 6cdc5a3d 2023-05-28 aa
373 6cdc5a3d 2023-05-28 aa setfocus();
374 15aae8ae 2023-05-28 aa }
375 15aae8ae 2023-05-28 aa
376 15aae8ae 2023-05-28 aa /*
377 15aae8ae 2023-05-28 aa * Create a new struct item, and insert it a after the it item. The rest of the
378 15aae8ae 2023-05-28 aa * arguments are values that the new item receives. On success return pointer
379 15aae8ae 2023-05-28 aa * to the new item. Return NULL otherwise.
380 15aae8ae 2023-05-28 aa */
381 15aae8ae 2023-05-28 aa static struct item *
382 cb28fcb8 2023-05-26 aa insitem(struct item *it, char *s, size_t len) {
383 cb28fcb8 2023-05-26 aa struct item *new;
384 cb28fcb8 2023-05-26 aa if (!(new = calloc(1, sizeof *new)))
385 cb28fcb8 2023-05-26 aa return NULL;
386 cb28fcb8 2023-05-26 aa if (it) {
387 cb28fcb8 2023-05-26 aa new->prev = it;
388 cb28fcb8 2023-05-26 aa new->next = it->next;
389 cb28fcb8 2023-05-26 aa it->next->prev = new;
390 cb28fcb8 2023-05-26 aa it->next = new;
391 cb28fcb8 2023-05-26 aa } else
392 cb28fcb8 2023-05-26 aa new->prev = new->next = new;
393 cb28fcb8 2023-05-26 aa new->s = strdup(s);
394 cb28fcb8 2023-05-26 aa new->len = len;
395 cb28fcb8 2023-05-26 aa new->ks = NULL; /* TODO */
396 15aae8ae 2023-05-28 aa new->dirty = true;
397 cb28fcb8 2023-05-26 aa return new;
398 cb28fcb8 2023-05-26 aa }
399 cb28fcb8 2023-05-26 aa
400 15aae8ae 2023-05-28 aa /*
401 15aae8ae 2023-05-28 aa * mkitems -- create a list of items from stdin, and return pointer to the
402 15aae8ae 2023-05-28 aa * new list's first element. If np is not NULL, set its value to the number of
403 15aae8ae 2023-05-28 aa * elements in the list. Return NULL on error.
404 15aae8ae 2023-05-28 aa */
405 15aae8ae 2023-05-28 aa static struct item *
406 15aae8ae 2023-05-28 aa mkitems(int *np)
407 cb28fcb8 2023-05-26 aa {
408 15aae8ae 2023-05-28 aa struct item *last = NULL;
409 15aae8ae 2023-05-28 aa size_t n = 0;
410 15aae8ae 2023-05-28 aa char *line = NULL;
411 15aae8ae 2023-05-28 aa size_t linesize = 0;
412 15aae8ae 2023-05-28 aa ssize_t linelen;
413 cb28fcb8 2023-05-26 aa
414 15aae8ae 2023-05-28 aa /* XXX take keysyms into account */
415 15aae8ae 2023-05-28 aa while ((linelen = getline(&line, &linesize, stdin)) != -1) {
416 15aae8ae 2023-05-28 aa struct item *it;
417 15aae8ae 2023-05-28 aa line[--linelen] = '\0'; /* get rid of '\n' */
418 15aae8ae 2023-05-28 aa if (!(it = insitem(last, line, linelen)))
419 15aae8ae 2023-05-28 aa die(1, "couldn't insert new item");
420 15aae8ae 2023-05-28 aa n++;
421 15aae8ae 2023-05-28 aa last = it;
422 15aae8ae 2023-05-28 aa }
423 15aae8ae 2023-05-28 aa free(line);
424 cb28fcb8 2023-05-26 aa
425 15aae8ae 2023-05-28 aa if (np)
426 15aae8ae 2023-05-28 aa *np = n;
427 cb28fcb8 2023-05-26 aa
428 15aae8ae 2023-05-28 aa return last ? last->next : NULL;
429 cb28fcb8 2023-05-26 aa }
430 cb28fcb8 2023-05-26 aa
431 15aae8ae 2023-05-28 aa /*
432 15aae8ae 2023-05-28 aa * {s,i}default -- return the {char *,int} value of the X default, or def
433 15aae8ae 2023-05-28 aa * otherwise.
434 15aae8ae 2023-05-28 aa */
435 15aae8ae 2023-05-28 aa static char *
436 7c7bbaee 2023-05-27 aa sdefault(char *opt, char *def)
437 7c7bbaee 2023-05-27 aa {
438 7c7bbaee 2023-05-27 aa char *val = XGetDefault(dpy, PROGNAME, opt);
439 7c7bbaee 2023-05-27 aa return val ? val : def;
440 7c7bbaee 2023-05-27 aa }
441 7c7bbaee 2023-05-27 aa
442 15aae8ae 2023-05-28 aa static int
443 7c7bbaee 2023-05-27 aa idefault(char *opt, int def)
444 cb28fcb8 2023-05-26 aa {
445 7c7bbaee 2023-05-27 aa char *val = XGetDefault(dpy, PROGNAME, opt);
446 7c7bbaee 2023-05-27 aa return val ? atoi(val) : def;
447 7c7bbaee 2023-05-27 aa }
448 cb28fcb8 2023-05-26 aa
449 15aae8ae 2023-05-28 aa /* nextarg -- safely skip the current command-line option */
450 15aae8ae 2023-05-28 aa static void
451 15aae8ae 2023-05-28 aa nextarg(int *argcp, char **argvp[])
452 7c7bbaee 2023-05-27 aa {
453 7c7bbaee 2023-05-27 aa if (--*argcp <= 0)
454 7c7bbaee 2023-05-27 aa usage();
455 7c7bbaee 2023-05-27 aa ++*argvp;
456 15aae8ae 2023-05-28 aa }
457 15aae8ae 2023-05-28 aa
458 15aae8ae 2023-05-28 aa /*
459 15aae8ae 2023-05-28 aa * {s,i}arg -- return the {char *,int} argument of the current command-line
460 15aae8ae 2023-05-28 aa * option.
461 15aae8ae 2023-05-28 aa */
462 15aae8ae 2023-05-28 aa static char *
463 15aae8ae 2023-05-28 aa sarg(int *argcp, char **argvp[])
464 15aae8ae 2023-05-28 aa {
465 15aae8ae 2023-05-28 aa nextarg(argcp, argvp);
466 7c7bbaee 2023-05-27 aa return **argvp;
467 7c7bbaee 2023-05-27 aa }
468 7c7bbaee 2023-05-27 aa
469 15aae8ae 2023-05-28 aa static int
470 7c7bbaee 2023-05-27 aa iarg(int *argcp, char **argvp[])
471 7c7bbaee 2023-05-27 aa {
472 15aae8ae 2023-05-28 aa nextarg(argcp, argvp);
473 7c7bbaee 2023-05-27 aa return atoi(**argvp);
474 7c7bbaee 2023-05-27 aa }
475 7c7bbaee 2023-05-27 aa
476 15aae8ae 2023-05-28 aa /*
477 15aae8ae 2023-05-28 aa * xitems -- pop-up menu for X, constructed from stdin, and printing user choice
478 15aae8ae 2023-05-28 aa * to stdout.
479 15aae8ae 2023-05-28 aa */
480 7c7bbaee 2023-05-27 aa int
481 7c7bbaee 2023-05-27 aa main(int argc, char *argv[])
482 7c7bbaee 2023-05-27 aa {
483 15aae8ae 2023-05-28 aa int n = 0;
484 cb28fcb8 2023-05-26 aa
485 7c7bbaee 2023-05-27 aa for (--argc, ++argv; argc > 0; --argc, ++argv)
486 7c7bbaee 2023-05-27 aa if (argv[0][0] == '-')
487 7c7bbaee 2023-05-27 aa switch (argv[0][1]) {
488 7c7bbaee 2023-05-27 aa case 'b':
489 7c7bbaee 2023-05-27 aa switch (argv[0][2]) {
490 7c7bbaee 2023-05-27 aa case 'g': /* -bg */
491 15aae8ae 2023-05-28 aa o_bg = sarg(&argc, &argv);
492 7c7bbaee 2023-05-27 aa break;
493 7c7bbaee 2023-05-27 aa case 'w': /* -bw */
494 15aae8ae 2023-05-28 aa o_bw = iarg(&argc, &argv);
495 7c7bbaee 2023-05-27 aa break;
496 7c7bbaee 2023-05-27 aa default:
497 7c7bbaee 2023-05-27 aa usage();
498 7c7bbaee 2023-05-27 aa /* NOTREACHED */
499 7c7bbaee 2023-05-27 aa }
500 7c7bbaee 2023-05-27 aa break;
501 7c7bbaee 2023-05-27 aa case 'f':
502 7c7bbaee 2023-05-27 aa switch (argv[0][2]) {
503 7c7bbaee 2023-05-27 aa case 'g': /* -fg */
504 15aae8ae 2023-05-28 aa o_fg = sarg(&argc, &argv);
505 7c7bbaee 2023-05-27 aa break;
506 7c7bbaee 2023-05-27 aa case 'o': /* -font */
507 15aae8ae 2023-05-28 aa o_font = sarg(&argc, &argv);
508 7c7bbaee 2023-05-27 aa break;
509 7c7bbaee 2023-05-27 aa default:
510 7c7bbaee 2023-05-27 aa usage();
511 7c7bbaee 2023-05-27 aa /* NOTREACHED */
512 7c7bbaee 2023-05-27 aa }
513 7c7bbaee 2023-05-27 aa break;
514 15aae8ae 2023-05-28 aa case 'h': /* -hp */
515 15aae8ae 2023-05-28 aa o_hp = iarg(&argc, &argv);
516 15aae8ae 2023-05-28 aa break;
517 7c7bbaee 2023-05-27 aa case 's':
518 7c7bbaee 2023-05-27 aa switch (argv[0][2]) {
519 7c7bbaee 2023-05-27 aa case 'b': /* -sbg */
520 15aae8ae 2023-05-28 aa o_sbg = sarg(&argc, &argv);
521 7c7bbaee 2023-05-27 aa break;
522 7c7bbaee 2023-05-27 aa case 'f': /* -sfg */
523 15aae8ae 2023-05-28 aa o_sfg = sarg(&argc, &argv);
524 7c7bbaee 2023-05-27 aa break;
525 7c7bbaee 2023-05-27 aa default:
526 7c7bbaee 2023-05-27 aa usage();
527 7c7bbaee 2023-05-27 aa /* NOTREACHED */
528 7c7bbaee 2023-05-27 aa }
529 7c7bbaee 2023-05-27 aa break;
530 15aae8ae 2023-05-28 aa case 'v': /* -vp */
531 15aae8ae 2023-05-28 aa o_vp = iarg(&argc, &argv);
532 15aae8ae 2023-05-28 aa break;
533 7c7bbaee 2023-05-27 aa case 'x': /* -x */
534 15aae8ae 2023-05-28 aa o_x = iarg(&argc, &argv);
535 7c7bbaee 2023-05-27 aa break;
536 7c7bbaee 2023-05-27 aa case 'y': /* -y */
537 15aae8ae 2023-05-28 aa o_y = iarg(&argc, &argv);
538 7c7bbaee 2023-05-27 aa break;
539 7c7bbaee 2023-05-27 aa default:
540 7c7bbaee 2023-05-27 aa usage();
541 7c7bbaee 2023-05-27 aa /* NOTREACHED */
542 7c7bbaee 2023-05-27 aa }
543 7c7bbaee 2023-05-27 aa
544 7c7bbaee 2023-05-27 aa if (!(dpy = XOpenDisplay(NULL)))
545 15aae8ae 2023-05-28 aa die(1, "couldn't open display");
546 7c7bbaee 2023-05-27 aa screen = DefaultScreen(dpy);
547 7c7bbaee 2023-05-27 aa
548 15aae8ae 2023-05-28 aa if (!o_bg)
549 15aae8ae 2023-05-28 aa o_bg = sdefault("background", "white");
550 15aae8ae 2023-05-28 aa if (!o_fg)
551 15aae8ae 2023-05-28 aa o_fg = sdefault("foreground", "black");
552 15aae8ae 2023-05-28 aa if (!o_font)
553 15aae8ae 2023-05-28 aa o_font = sdefault("font", "fixed");
554 15aae8ae 2023-05-28 aa if (!o_sbg)
555 15aae8ae 2023-05-28 aa o_sbg = sdefault("selectedBackground", "black");
556 15aae8ae 2023-05-28 aa if (!o_sfg)
557 15aae8ae 2023-05-28 aa o_sfg = sdefault("selectedForeground", "white");
558 15aae8ae 2023-05-28 aa if (o_bw == -1)
559 15aae8ae 2023-05-28 aa o_bw = idefault("borderWidth", 1);
560 15aae8ae 2023-05-28 aa if (o_hp == -1)
561 15aae8ae 2023-05-28 aa o_hp = idefault("horizontalPadding", 2);
562 15aae8ae 2023-05-28 aa if (o_vp == -1)
563 15aae8ae 2023-05-28 aa o_vp = idefault("verticalPadding", 1);
564 7c7bbaee 2023-05-27 aa
565 15aae8ae 2023-05-28 aa if (o_x == -1 || o_y == -1) {
566 7c7bbaee 2023-05-27 aa Window w;
567 7c7bbaee 2023-05-27 aa int i;
568 7c7bbaee 2023-05-27 aa unsigned int ui;
569 7c7bbaee 2023-05-27 aa int *xp, *yp;
570 7c7bbaee 2023-05-27 aa
571 15aae8ae 2023-05-28 aa xp = (o_x == -1) ? &o_x : &i;
572 15aae8ae 2023-05-28 aa yp = (o_y == -1) ? &o_y : &i;
573 7c7bbaee 2023-05-27 aa
574 7c7bbaee 2023-05-27 aa XQueryPointer(dpy, RootWindow(dpy, screen), &w, &w, xp, yp, &i,
575 7c7bbaee 2023-05-27 aa &i, &ui);
576 7c7bbaee 2023-05-27 aa }
577 7c7bbaee 2023-05-27 aa
578 15aae8ae 2023-05-28 aa if (!(first = selected = mkitems(&n))) {
579 15aae8ae 2023-05-28 aa if (n)
580 15aae8ae 2023-05-28 aa die(1, "coulnd't create items list");
581 15aae8ae 2023-05-28 aa else
582 15aae8ae 2023-05-28 aa exit(0);
583 cb28fcb8 2023-05-26 aa }
584 15aae8ae 2023-05-28 aa setupx(n);
585 cb28fcb8 2023-05-26 aa
586 15aae8ae 2023-05-28 aa for (;;)
587 15aae8ae 2023-05-28 aa proc();
588 cb28fcb8 2023-05-26 aa /* NOTREACHED */
589 cb28fcb8 2023-05-26 aa }