progman.exe^H^H^H^H
1/*
2 * Copyright (c) 2021 joshua stein <jcs@jcs.org>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to
6 * deal in the Software without restriction, including without limitation the
7 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 * sell copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
18 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
19 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 */
21
22#include <stdlib.h>
23#include <err.h>
24#include "parser.h"
25#include "progman.h"
26
27struct program {
28 char *name;
29 action_t *action;
30 struct program *next;
31};
32
33Window launcher_win;
34XftDraw *launcher_xftdraw;
35struct program *program_head = NULL, *program_tail = NULL;
36int launcher_width = 0, launcher_height = 0, launcher_highlighted = 0,
37 launcher_item_height = 0, launcher_item_padding = 0;
38
39void launcher_reload(void);
40void launcher_redraw(void);
41
42void
43launcher_setup(void)
44{
45 XTextProperty name;
46 char *title = "Programs";
47 XSizeHints *hints;
48
49 launcher_reload();
50
51 launcher_win = XCreateWindow(dpy, root, 0, 0, launcher_width,
52 launcher_height, 0, DefaultDepth(dpy, screen), CopyFromParent,
53 DefaultVisual(dpy, screen), 0, NULL);
54 if (!launcher_win)
55 err(1, "XCreateWindow");
56
57 hints = XAllocSizeHints();
58 if (!hints)
59 err(1, "XAllocSizeHints");
60
61 hints->flags = PMinSize | PMaxSize;
62 hints->min_width = launcher_width;
63 hints->min_height = launcher_height;
64 hints->max_width = launcher_width;
65 hints->max_height = launcher_height;
66
67 XSetWMNormalHints(dpy, launcher_win, hints);
68
69 XFree(hints);
70
71 if (!XStringListToTextProperty(&title, 1, &name))
72 err(1, "XStringListToTextProperty");
73 XSetWMName(dpy, launcher_win, &name);
74
75 XSetWindowBackground(dpy, launcher_win, launcher_bg.pixel);
76
77 set_atoms(launcher_win, net_wm_state, XA_ATOM, &net_wm_state_above, 1);
78 set_atoms(launcher_win, net_wm_wintype, XA_ATOM, &net_wm_type_utility,
79 1);
80
81 launcher_xftdraw = XftDrawCreate(dpy, launcher_win,
82 DefaultVisual(dpy, screen), DefaultColormap(dpy, screen));
83}
84
85void
86launcher_reload(void)
87{
88 FILE *ini;
89 struct program *program = NULL;
90 XGlyphInfo extents;
91 action_t *action;
92 char *key, *val;
93 int tw;
94
95 launcher_programs_free();
96
97 launcher_width = 0;
98 launcher_height = 0;
99 launcher_item_padding = opt_pad * 2;
100 launcher_item_height = font->ascent + (launcher_item_padding * 2);
101
102 ini = open_ini(opt_config_file);
103
104 if (!find_ini_section(ini, "launcher"))
105 goto done;
106
107 while (get_ini_kv(ini, &key, &val)) {
108 action = parse_action(key, val);
109 if (action == NULL)
110 continue;
111
112 program = malloc(sizeof(struct program));
113 if (!program)
114 err(1, "malloc");
115
116 program->next = NULL;
117
118 if (program_tail) {
119 program_tail->next = program;
120 program_tail = program;
121 } else {
122 program_head = program;
123 program_tail = program;
124 }
125
126 XftTextExtentsUtf8(dpy, font, (FcChar8 *)key, strlen(key),
127 &extents);
128 tw = extents.xOff + (launcher_item_padding * 2);
129 if (tw > launcher_width)
130 launcher_width = tw;
131 launcher_height += launcher_item_height;
132
133 program->name = strdup(key);
134 program->action = action;
135
136 free(key);
137 free(val);
138 }
139
140done:
141 fclose(ini);
142}
143
144void
145launcher_show(XButtonEvent *e)
146{
147 client_t *c;
148 XEvent ev;
149 struct program *program;
150 int x, y, mx, my, prev_highlighted;
151
152 if (e) {
153 x = e->x_root;
154 y = e->y_root;
155 } else
156 get_pointer(&x, &y);
157
158 XMoveResizeWindow(dpy, launcher_win, x, y, launcher_width,
159 launcher_height);
160
161 c = new_client(launcher_win);
162 c->placed = 1;
163 c->desk = cur_desk;
164 map_client(c);
165 map_if_desk(c);
166
167 x = c->geom.x;
168 y = c->geom.y;
169
170 launcher_highlighted = prev_highlighted = -1;
171 launcher_redraw();
172
173 /*
174 * If we launched from a mouse button down event, grab the pointer to
175 * watch for it to be released, otherwise we launched from the keyboard
176 * and we'll dismiss on click
177 */
178 if (XGrabPointer(dpy, root, False, MouseMask, GrabModeAsync,
179 GrabModeAsync, root, None, CurrentTime) != GrabSuccess) {
180 warnx("failed grabbing pointer");
181 goto close_launcher;
182 }
183
184 for (;;) {
185 XMaskEvent(dpy, PointerMotionMask | ButtonPressMask |
186 ButtonReleaseMask, &ev);
187
188 switch (ev.type) {
189 case MotionNotify: {
190 XMotionEvent *xmv = (XMotionEvent *)&ev;
191 mx = xmv->x - x;
192 my = xmv->y - y;
193
194 if (mx < 0 || mx > launcher_width ||
195 my < 0 || my > launcher_height)
196 launcher_highlighted = -1;
197 else
198 launcher_highlighted = (my /
199 launcher_item_height);
200
201 if (launcher_highlighted != prev_highlighted)
202 launcher_redraw();
203
204 prev_highlighted = launcher_highlighted;
205 break;
206 }
207 case ButtonPress:
208 break;
209 case ButtonRelease:
210 goto close_launcher;
211 break;
212 }
213 }
214
215close_launcher:
216 XUngrabPointer(dpy, CurrentTime);
217 XUnmapWindow(dpy, launcher_win);
218 del_client(c, DEL_WITHDRAW);
219
220 if (launcher_highlighted < 0)
221 return;
222
223 for (x = 0, program = program_head; program;
224 program = program->next, x++) {
225 if (x != launcher_highlighted)
226 continue;
227
228 take_action(program->action);
229 break;
230 }
231}
232
233void
234launcher_programs_free(void)
235{
236 struct program *program;
237
238 for (program = program_head; program;) {
239 struct program *t = program;
240
241 if (program->name)
242 free(program->name);
243 if (program->action) {
244 if (program->action->sarg)
245 free(program->action->sarg);
246 free(program->action);
247 }
248 program = program->next;
249 free(t);
250 }
251
252 program_head = program_tail = NULL;
253}
254
255void
256launcher_redraw(void)
257{
258 struct program *program;
259 XftColor *color;
260 int i;
261
262 XClearWindow(dpy, launcher_win);
263
264 for (i = 0, program = program_head; program;
265 program = program->next, i++) {
266 if (launcher_highlighted == i) {
267 XSetForeground(dpy, DefaultGC(dpy, screen),
268 launcher_fg.pixel);
269 XFillRectangle(dpy, launcher_win,
270 DefaultGC(dpy, screen),
271 0, launcher_item_height * i,
272 launcher_width, launcher_item_height);
273 color = &xft_launcher_highlighted;
274 } else {
275 XSetForeground(dpy, DefaultGC(dpy, screen),
276 launcher_fg.pixel);
277 color = &xft_launcher;
278 }
279
280
281 XftDrawStringUtf8(launcher_xftdraw, color, font,
282 launcher_item_padding,
283 (launcher_item_height * (i + 1)) - launcher_item_padding,
284 (unsigned char *)program->name,
285 strlen(program->name));
286 }
287}