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