1 /*
2  * calmwm - the calm window manager
3  *
4  * Copyright (c) 2004 Marius Aamodt Eriksen <marius@monkey.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  *
18  * $OpenBSD$
19  */
20 
21 #include <sys/types.h>
22 #include "queue.h"
23 
24 #include <err.h>
25 #include <errno.h>
26 #include <limits.h>
27 #include <pwd.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 
33 #include "calmwm.h"
34 
35 static const char	*conf_bind_mask(const char *, unsigned int *);
36 static void		 conf_unbind_key(struct conf *, struct bind_ctx *);
37 static void		 conf_unbind_mouse(struct conf *, struct bind_ctx *);
38 
39 static const struct {
40 	int		 num;
41 	const char	*name;
42 } group_binds[] = {
43 	{ 0, "nogroup" },
44 	{ 1, "one" },
45 	{ 2, "two" },
46 	{ 3, "three" },
47 	{ 4, "four" },
48 	{ 5, "five" },
49 	{ 6, "six" },
50 	{ 7, "seven" },
51 	{ 8, "eight" },
52 	{ 9, "nine" },
53 };
54 static int cursor_binds[] = {
55 	XC_left_ptr,		/* CF_NORMAL */
56 	XC_fleur,		/* CF_MOVE */
57 	XC_bottom_right_corner,	/* CF_RESIZE */
58 	XC_question_arrow,	/* CF_QUESTION */
59 };
60 static const char *color_binds[] = {
61 	"#CCCCCC",		/* CWM_COLOR_BORDER_ACTIVE */
62 	"#666666",		/* CWM_COLOR_BORDER_INACTIVE */
63 	"#FC8814",		/* CWM_COLOR_BORDER_URGENCY */
64 	"blue",			/* CWM_COLOR_BORDER_GROUP */
65 	"red",			/* CWM_COLOR_BORDER_UNGROUP */
66 	"black",		/* CWM_COLOR_MENU_FG */
67 	"white",		/* CWM_COLOR_MENU_BG */
68 	"black",		/* CWM_COLOR_MENU_FONT */
69 	"",			/* CWM_COLOR_MENU_FONT_SEL */
70 };
71 static const struct {
72 	const char	*tag;
73 	void		 (*handler)(void *, struct cargs *);
74 	enum context	 context;
75 	int		 flag;
76 } name_to_func[] = {
77 #define FUNC_CC(t, h, n) \
78 	#t, kbfunc_ ## h, CWM_CONTEXT_CC, n
79 #define FUNC_SC(t, h, n) \
80 	#t, kbfunc_ ## h, CWM_CONTEXT_SC, n
81 
82 	{ FUNC_CC(window-lower, client_lower, 0) },
83 	{ FUNC_CC(window-raise, client_raise, 0) },
84 	{ FUNC_CC(window-hide, client_hide, 0) },
85 	{ FUNC_CC(window-close, client_close, 0) },
86 	{ FUNC_CC(window-delete, client_close, 0) },
87 	{ FUNC_CC(window-htile, client_htile, 0) },
88 	{ FUNC_CC(window-vtile, client_vtile, 0) },
89 	{ FUNC_CC(window-stick, client_toggle_sticky, 0) },
90 	{ FUNC_CC(window-fullscreen, client_toggle_fullscreen, 0) },
91 	{ FUNC_CC(window-maximize, client_toggle_maximize, 0) },
92 	{ FUNC_CC(window-vmaximize, client_toggle_vmaximize, 0) },
93 	{ FUNC_CC(window-hmaximize, client_toggle_hmaximize, 0) },
94 	{ FUNC_CC(window-freeze, client_toggle_freeze, 0) },
95 	{ FUNC_CC(window-group, client_toggle_group, 0) },
96 	{ FUNC_CC(window-movetogroup-1, client_movetogroup, 1) },
97 	{ FUNC_CC(window-movetogroup-2, client_movetogroup, 2) },
98 	{ FUNC_CC(window-movetogroup-3, client_movetogroup, 3) },
99 	{ FUNC_CC(window-movetogroup-4, client_movetogroup, 4) },
100 	{ FUNC_CC(window-movetogroup-5, client_movetogroup, 5) },
101 	{ FUNC_CC(window-movetogroup-6, client_movetogroup, 6) },
102 	{ FUNC_CC(window-movetogroup-7, client_movetogroup, 7) },
103 	{ FUNC_CC(window-movetogroup-8, client_movetogroup, 8) },
104 	{ FUNC_CC(window-movetogroup-9, client_movetogroup, 9) },
105 	{ FUNC_CC(window-snap-up, client_snap, (CWM_UP)) },
106 	{ FUNC_CC(window-snap-down, client_snap, (CWM_DOWN)) },
107 	{ FUNC_CC(window-snap-right, client_snap, (CWM_RIGHT)) },
108 	{ FUNC_CC(window-snap-left, client_snap, (CWM_LEFT)) },
109 	{ FUNC_CC(window-snap-up-right, client_snap, (CWM_UP_RIGHT)) },
110 	{ FUNC_CC(window-snap-up-left, client_snap, (CWM_UP_LEFT)) },
111 	{ FUNC_CC(window-snap-down-right, client_snap, (CWM_DOWN_RIGHT)) },
112 	{ FUNC_CC(window-snap-down-left, client_snap, (CWM_DOWN_LEFT)) },
113 	{ FUNC_CC(window-move, client_move, 0) },
114 	{ FUNC_CC(window-move-up, client_move, (CWM_UP)) },
115 	{ FUNC_CC(window-move-down, client_move, (CWM_DOWN)) },
116 	{ FUNC_CC(window-move-right, client_move, (CWM_RIGHT)) },
117 	{ FUNC_CC(window-move-left, client_move, (CWM_LEFT)) },
118 	{ FUNC_CC(window-move-up-big, client_move, (CWM_UP_BIG)) },
119 	{ FUNC_CC(window-move-down-big, client_move, (CWM_DOWN_BIG)) },
120 	{ FUNC_CC(window-move-right-big, client_move, (CWM_RIGHT_BIG)) },
121 	{ FUNC_CC(window-move-left-big, client_move, (CWM_LEFT_BIG)) },
122 	{ FUNC_CC(window-resize, client_resize, 0) },
123 	{ FUNC_CC(window-resize-up, client_resize, (CWM_UP)) },
124 	{ FUNC_CC(window-resize-down, client_resize, (CWM_DOWN)) },
125 	{ FUNC_CC(window-resize-right, client_resize, (CWM_RIGHT)) },
126 	{ FUNC_CC(window-resize-left, client_resize, (CWM_LEFT)) },
127 	{ FUNC_CC(window-resize-up-big, client_resize, (CWM_UP_BIG)) },
128 	{ FUNC_CC(window-resize-down-big, client_resize, (CWM_DOWN_BIG)) },
129 	{ FUNC_CC(window-resize-right-big, client_resize, (CWM_RIGHT_BIG)) },
130 	{ FUNC_CC(window-resize-left-big, client_resize, (CWM_LEFT_BIG)) },
131 	{ FUNC_CC(window-menu-label, client_menu_label, 0) },
132 
133 	{ FUNC_SC(window-cycle, client_cycle, (CWM_CYCLE_FORWARD)) },
134 	{ FUNC_SC(window-rcycle, client_cycle, (CWM_CYCLE_REVERSE)) },
135 	{ FUNC_SC(window-cycle-ingroup, client_cycle,
136 		(CWM_CYCLE_FORWARD | CWM_CYCLE_INGROUP)) },
137 	{ FUNC_SC(window-rcycle-ingroup, client_cycle,
138 		(CWM_CYCLE_REVERSE | CWM_CYCLE_INGROUP)) },
139 
140 	{ FUNC_SC(group-cycle, group_cycle, (CWM_CYCLE_FORWARD)) },
141 	{ FUNC_SC(group-rcycle, group_cycle, (CWM_CYCLE_REVERSE)) },
142 	{ FUNC_SC(group-toggle-all, group_toggle_all, 0) },
143 	{ FUNC_SC(group-toggle-1, group_toggle, 1) },
144 	{ FUNC_SC(group-toggle-2, group_toggle, 2) },
145 	{ FUNC_SC(group-toggle-3, group_toggle, 3) },
146 	{ FUNC_SC(group-toggle-4, group_toggle, 4) },
147 	{ FUNC_SC(group-toggle-5, group_toggle, 5) },
148 	{ FUNC_SC(group-toggle-6, group_toggle, 6) },
149 	{ FUNC_SC(group-toggle-7, group_toggle, 7) },
150 	{ FUNC_SC(group-toggle-8, group_toggle, 8) },
151 	{ FUNC_SC(group-toggle-9, group_toggle, 9) },
152 	{ FUNC_SC(group-only-1, group_only, 1) },
153 	{ FUNC_SC(group-only-2, group_only, 2) },
154 	{ FUNC_SC(group-only-3, group_only, 3) },
155 	{ FUNC_SC(group-only-4, group_only, 4) },
156 	{ FUNC_SC(group-only-5, group_only, 5) },
157 	{ FUNC_SC(group-only-6, group_only, 6) },
158 	{ FUNC_SC(group-only-7, group_only, 7) },
159 	{ FUNC_SC(group-only-8, group_only, 8) },
160 	{ FUNC_SC(group-only-9, group_only, 9) },
161 	{ FUNC_SC(group-close-1, group_close, 1) },
162 	{ FUNC_SC(group-close-2, group_close, 2) },
163 	{ FUNC_SC(group-close-3, group_close, 3) },
164 	{ FUNC_SC(group-close-4, group_close, 4) },
165 	{ FUNC_SC(group-close-5, group_close, 5) },
166 	{ FUNC_SC(group-close-6, group_close, 6) },
167 	{ FUNC_SC(group-close-7, group_close, 7) },
168 	{ FUNC_SC(group-close-8, group_close, 8) },
169 	{ FUNC_SC(group-close-9, group_close, 9) },
170 
171 	{ FUNC_SC(pointer-move-up, ptrmove, (CWM_UP)) },
172 	{ FUNC_SC(pointer-move-down, ptrmove, (CWM_DOWN)) },
173 	{ FUNC_SC(pointer-move-left, ptrmove, (CWM_LEFT)) },
174 	{ FUNC_SC(pointer-move-right, ptrmove, (CWM_RIGHT)) },
175 	{ FUNC_SC(pointer-move-up-big, ptrmove, (CWM_UP_BIG)) },
176 	{ FUNC_SC(pointer-move-down-big, ptrmove, (CWM_DOWN_BIG)) },
177 	{ FUNC_SC(pointer-move-left-big, ptrmove, (CWM_LEFT_BIG)) },
178 	{ FUNC_SC(pointer-move-right-big, ptrmove, (CWM_RIGHT_BIG)) },
179 
180 	{ FUNC_SC(menu-cmd, menu_cmd, 0) },
181 	{ FUNC_SC(menu-group, menu_group, 0) },
182 	{ FUNC_SC(menu-ssh, menu_ssh, 0) },
183 	{ FUNC_SC(menu-window, menu_client, CWM_MENU_WINDOW_ALL) },
184 	{ FUNC_SC(menu-window-hidden, menu_client, CWM_MENU_WINDOW_HIDDEN) },
185 	{ FUNC_SC(menu-exec, menu_exec, 0) },
186 	{ FUNC_SC(menu-exec-wm, menu_wm, 0) },
187 
188 	{ FUNC_SC(terminal, exec_term, 0) },
189 	{ FUNC_SC(lock, exec_lock, 0) },
190 	{ FUNC_SC(restart, cwm_status, CWM_EXEC_WM) },
191 	{ FUNC_SC(quit, cwm_status, CWM_QUIT) },
192 };
193 static unsigned int ignore_mods[] = {
194 	0, LockMask, Mod2Mask, Mod2Mask | LockMask
195 };
196 static const struct {
197 	const char	ch;
198 	int		mask;
199 } bind_mods[] = {
200 	{ 'S',	ShiftMask },
201 	{ 'C',	ControlMask },
202 	{ 'M',	Mod1Mask },
203 	{ '4',	Mod4Mask },
204 	{ '5',	Mod5Mask },
205 };
206 static const struct {
207 	const char	*key;
208 	const char	*func;
209 } key_binds[] = {
210 	{ "CM-Return",	"terminal" },
211 	{ "CM-Delete",	"lock" },
212 	{ "M-question",	"menu-exec" },
213 	{ "CM-w",	"menu-exec-wm" },
214 	{ "M-period",	"menu-ssh" },
215 	{ "M-Return",	"window-hide" },
216 	{ "M-Down",	"window-lower" },
217 	{ "M-Up",	"window-raise" },
218 	{ "M-slash",	"menu-window" },
219 	{ "C-slash",	"menu-cmd" },
220 	{ "M-Tab",	"window-cycle" },
221 	{ "MS-Tab",	"window-rcycle" },
222 	{ "CM-n",	"window-menu-label" },
223 	{ "CM-x",	"window-close" },
224 	{ "CM-a",	"group-toggle-all" },
225 	{ "CM-0",	"group-toggle-all" },
226 	{ "CM-1",	"group-toggle-1" },
227 	{ "CM-2",	"group-toggle-2" },
228 	{ "CM-3",	"group-toggle-3" },
229 	{ "CM-4",	"group-toggle-4" },
230 	{ "CM-5",	"group-toggle-5" },
231 	{ "CM-6",	"group-toggle-6" },
232 	{ "CM-7",	"group-toggle-7" },
233 	{ "CM-8",	"group-toggle-8" },
234 	{ "CM-9",	"group-toggle-9" },
235 	{ "M-Right",	"group-cycle" },
236 	{ "M-Left",	"group-rcycle" },
237 	{ "CM-g",	"window-group" },
238 	{ "CM-f",	"window-fullscreen" },
239 	{ "CM-m",	"window-maximize" },
240 	{ "CM-s",	"window-stick" },
241 	{ "CM-equal",	"window-vmaximize" },
242 	{ "CMS-equal",	"window-hmaximize" },
243 	{ "CMS-f",	"window-freeze" },
244 	{ "CMS-r",	"restart" },
245 	{ "CMS-q",	"quit" },
246 	{ "M-h",	"window-move-left" },
247 	{ "M-j",	"window-move-down" },
248 	{ "M-k",	"window-move-up" },
249 	{ "M-l",	"window-move-right" },
250 	{ "MS-h",	"window-move-left-big" },
251 	{ "MS-j",	"window-move-down-big" },
252 	{ "MS-k",	"window-move-up-big" },
253 	{ "MS-l",	"window-move-right-big" },
254 	{ "CM-h",	"window-resize-left" },
255 	{ "CM-j",	"window-resize-down" },
256 	{ "CM-k",	"window-resize-up" },
257 	{ "CM-l",	"window-resize-right" },
258 	{ "CMS-h",	"window-resize-left-big" },
259 	{ "CMS-j",	"window-resize-down-big" },
260 	{ "CMS-k",	"window-resize-up-big" },
261 	{ "CMS-l",	"window-resize-right-big" },
262 },
263 mouse_binds[] = {
264 	{ "1",		"menu-window" },
265 	{ "2",		"menu-group" },
266 	{ "3",		"menu-cmd" },
267 	{ "M-1",	"window-move" },
268 	{ "CM-1",	"window-group" },
269 	{ "M-2",	"window-resize" },
270 	{ "M-3",	"window-lower" },
271 	{ "CMS-3",	"window-hide" },
272 };
273 
274 void
conf_init(struct conf * c)275 conf_init(struct conf *c)
276 {
277 	const char	*home;
278 	struct passwd	*pw;
279 	unsigned int	i;
280 
281 	c->stickygroups = 0;
282 	c->bwidth = 1;
283 	c->mamount = 1;
284 	c->htile = 50;
285 	c->vtile = 50;
286 	c->snapdist = 0;
287 	c->ngroups = 0;
288 	c->nameqlen = 5;
289 
290 	TAILQ_INIT(&c->ignoreq);
291 	TAILQ_INIT(&c->autogroupq);
292 	TAILQ_INIT(&c->keybindq);
293 	TAILQ_INIT(&c->mousebindq);
294 	TAILQ_INIT(&c->cmdq);
295 	TAILQ_INIT(&c->wmq);
296 
297 	for (i = 0; i < nitems(key_binds); i++)
298 		conf_bind_key(c, key_binds[i].key, key_binds[i].func);
299 
300 	for (i = 0; i < nitems(mouse_binds); i++)
301 		conf_bind_mouse(c, mouse_binds[i].key, mouse_binds[i].func);
302 
303 	for (i = 0; i < nitems(color_binds); i++)
304 		c->color[i] = xstrdup(color_binds[i]);
305 
306 	conf_cmd_add(c, "lock", "xlock");
307 	conf_cmd_add(c, "term", "xterm");
308 	conf_wm_add(c, "cwm", "cwm");
309 
310 	c->font = xstrdup("sans-serif:pixelsize=14:bold");
311 	c->wmname = xstrdup("CWM");
312 
313 	home = getenv("HOME");
314 	if ((home == NULL) || (*home == '\0')) {
315 		pw = getpwuid(getuid());
316 		if (pw != NULL && pw->pw_dir != NULL && *pw->pw_dir != '\0')
317 			home = pw->pw_dir;
318 		else
319 			home = "/";
320 	}
321 	xasprintf(&c->conf_file, "%s/%s", home, ".cwmrc");
322 	xasprintf(&c->known_hosts, "%s/%s", home, ".ssh/known_hosts");
323 }
324 
325 void
conf_clear(struct conf * c)326 conf_clear(struct conf *c)
327 {
328 	struct autogroup	*ag;
329 	struct bind_ctx		*kb, *mb;
330 	struct winname		*wn;
331 	struct cmd_ctx		*cmd, *wm;
332 	int			 i;
333 
334 	while ((cmd = TAILQ_FIRST(&c->cmdq)) != NULL) {
335 		TAILQ_REMOVE(&c->cmdq, cmd, entry);
336 		free(cmd->name);
337 		free(cmd->path);
338 		free(cmd);
339 	}
340 	while ((wm = TAILQ_FIRST(&c->wmq)) != NULL) {
341 		TAILQ_REMOVE(&c->wmq, wm, entry);
342 		free(wm->name);
343 		free(wm->path);
344 		free(wm);
345 	}
346 	while ((kb = TAILQ_FIRST(&c->keybindq)) != NULL) {
347 		TAILQ_REMOVE(&c->keybindq, kb, entry);
348 		free(kb);
349 	}
350 	while ((ag = TAILQ_FIRST(&c->autogroupq)) != NULL) {
351 		TAILQ_REMOVE(&c->autogroupq, ag, entry);
352 		free(ag->class);
353 		free(ag->name);
354 		free(ag);
355 	}
356 	while ((wn = TAILQ_FIRST(&c->ignoreq)) != NULL) {
357 		TAILQ_REMOVE(&c->ignoreq, wn, entry);
358 		free(wn->name);
359 		free(wn);
360 	}
361 	while ((mb = TAILQ_FIRST(&c->mousebindq)) != NULL) {
362 		TAILQ_REMOVE(&c->mousebindq, mb, entry);
363 		free(mb);
364 	}
365 	for (i = 0; i < CWM_COLOR_NITEMS; i++)
366 		free(c->color[i]);
367 
368 	free(c->conf_file);
369 	free(c->known_hosts);
370 	free(c->font);
371 	free(c->wmname);
372 }
373 
374 void
conf_cmd_add(struct conf * c,const char * name,const char * path)375 conf_cmd_add(struct conf *c, const char *name, const char *path)
376 {
377 	struct cmd_ctx	*cmd, *cmdtmp = NULL, *cmdnxt;
378 
379 	cmd = xmalloc(sizeof(*cmd));
380 	cmd->name = xstrdup(name);
381 	cmd->path = xstrdup(path);
382 
383 	TAILQ_FOREACH_SAFE(cmdtmp, &c->cmdq, entry, cmdnxt) {
384 		if (strcmp(cmdtmp->name, name) == 0) {
385 			TAILQ_REMOVE(&c->cmdq, cmdtmp, entry);
386 			free(cmdtmp->name);
387 			free(cmdtmp->path);
388 			free(cmdtmp);
389 		}
390 	}
391 	TAILQ_INSERT_TAIL(&c->cmdq, cmd, entry);
392 }
393 
394 void
conf_wm_add(struct conf * c,const char * name,const char * path)395 conf_wm_add(struct conf *c, const char *name, const char *path)
396 {
397 	struct cmd_ctx	*wm, *wmtmp = NULL, *wmnxt;
398 
399 	wm = xmalloc(sizeof(*wm));
400 	wm->name = xstrdup(name);
401 	wm->path = xstrdup(path);
402 
403 	TAILQ_FOREACH_SAFE(wmtmp, &c->cmdq, entry, wmnxt) {
404 		if (strcmp(wmtmp->name, name) == 0) {
405 			TAILQ_REMOVE(&c->wmq, wmtmp, entry);
406 			free(wmtmp->name);
407 			free(wmtmp->path);
408 			free(wmtmp);
409 		}
410 	}
411 	TAILQ_INSERT_TAIL(&c->wmq, wm, entry);
412 }
413 
414 void
conf_autogroup(struct conf * c,int num,const char * name,const char * class)415 conf_autogroup(struct conf *c, int num, const char *name, const char *class)
416 {
417 	struct autogroup	*ag;
418 	char			*p;
419 
420 	ag = xmalloc(sizeof(*ag));
421 	if ((p = strchr(class, ',')) == NULL) {
422 		if (name == NULL)
423 			ag->name = NULL;
424 		else
425 			ag->name = xstrdup(name);
426 
427 		ag->class = xstrdup(class);
428 	} else {
429 		*(p++) = '\0';
430 		if (name == NULL)
431 			ag->name = xstrdup(class);
432 		else
433 			ag->name = xstrdup(name);
434 
435 		ag->class = xstrdup(p);
436 	}
437 	ag->num = num;
438 	TAILQ_INSERT_TAIL(&c->autogroupq, ag, entry);
439 }
440 
441 void
conf_ignore(struct conf * c,const char * name)442 conf_ignore(struct conf *c, const char *name)
443 {
444 	struct winname	*wn;
445 
446 	wn = xmalloc(sizeof(*wn));
447 	wn->name = xstrdup(name);
448 	TAILQ_INSERT_TAIL(&c->ignoreq, wn, entry);
449 }
450 
451 void
conf_cursor(struct conf * c)452 conf_cursor(struct conf *c)
453 {
454 	unsigned int	 i;
455 
456 	for (i = 0; i < nitems(cursor_binds); i++)
457 		c->cursor[i] = XCreateFontCursor(X_Dpy, cursor_binds[i]);
458 }
459 
460 void
conf_client(struct client_ctx * cc)461 conf_client(struct client_ctx *cc)
462 {
463 	struct winname	*wn;
464 
465 	TAILQ_FOREACH(wn, &Conf.ignoreq, entry) {
466 		if (strncasecmp(wn->name, cc->name, strlen(wn->name)) == 0) {
467 			cc->flags |= CLIENT_IGNORE;
468 			break;
469 		}
470 	}
471 }
472 
473 void
conf_screen(struct screen_ctx * sc)474 conf_screen(struct screen_ctx *sc)
475 {
476 	unsigned int	 i;
477 	XftColor	 xc;
478 
479 	sc->gap = Conf.gap;
480 	sc->snapdist = Conf.snapdist;
481 
482 	sc->xftfont = XftFontOpenXlfd(X_Dpy, sc->which, Conf.font);
483 	if (sc->xftfont == NULL) {
484 		sc->xftfont = XftFontOpenName(X_Dpy, sc->which, Conf.font);
485 		if (sc->xftfont == NULL)
486 			errx(1, "%s: XftFontOpenName: %s", __func__, Conf.font);
487 	}
488 
489 	for (i = 0; i < nitems(color_binds); i++) {
490 		if (i == CWM_COLOR_MENU_FONT_SEL && *Conf.color[i] == '\0') {
491 			xu_xorcolor(sc->xftcolor[CWM_COLOR_MENU_BG],
492 			    sc->xftcolor[CWM_COLOR_MENU_FG], &xc);
493 			xu_xorcolor(sc->xftcolor[CWM_COLOR_MENU_FONT], xc, &xc);
494 			if (!XftColorAllocValue(X_Dpy, sc->visual, sc->colormap,
495 			    &xc.color, &sc->xftcolor[CWM_COLOR_MENU_FONT_SEL]))
496 				warnx("XftColorAllocValue: %s", Conf.color[i]);
497 			break;
498 		}
499 		if (!XftColorAllocName(X_Dpy, sc->visual, sc->colormap,
500 		    Conf.color[i], &sc->xftcolor[i])) {
501 			warnx("XftColorAllocName: %s", Conf.color[i]);
502 			XftColorAllocName(X_Dpy, sc->visual, sc->colormap,
503 			    color_binds[i], &sc->xftcolor[i]);
504 		}
505 	}
506 
507 	conf_grab_kbd(sc->rootwin);
508 }
509 
510 void
conf_group(struct screen_ctx * sc)511 conf_group(struct screen_ctx *sc)
512 {
513 	unsigned int	 i;
514 
515 	for (i = 0; i < nitems(group_binds); i++) {
516 		group_init(sc, group_binds[i].num, group_binds[i].name);
517 		Conf.ngroups++;
518 	}
519 }
520 
521 static const char *
conf_bind_mask(const char * name,unsigned int * mask)522 conf_bind_mask(const char *name, unsigned int *mask)
523 {
524 	char		*dash;
525 	const char	*ch;
526 	unsigned int 	 i;
527 
528 	*mask = 0;
529 	if ((dash = strchr(name, '-')) == NULL)
530 		return name;
531 	for (i = 0; i < nitems(bind_mods); i++) {
532 		if ((ch = strchr(name, bind_mods[i].ch)) != NULL && ch < dash)
533 			*mask |= bind_mods[i].mask;
534 	}
535 	/* Skip past modifiers. */
536 	return (dash + 1);
537 }
538 
539 int
conf_bind_key(struct conf * c,const char * bind,const char * cmd)540 conf_bind_key(struct conf *c, const char *bind, const char *cmd)
541 {
542 	struct bind_ctx	*kb;
543 	struct cargs	*cargs;
544 	const char	*key;
545 	unsigned int	 i;
546 
547 	if ((strcmp(bind, "all") == 0) && (cmd == NULL)) {
548 		conf_unbind_key(c, NULL);
549 		return 1;
550 	}
551 	kb = xmalloc(sizeof(*kb));
552 	key = conf_bind_mask(bind, &kb->modmask);
553 	kb->press.keysym = XStringToKeysym(key);
554 	if (kb->press.keysym == NoSymbol) {
555 		warnx("unknown symbol: %s", key);
556 		free(kb);
557 		return 0;
558 	}
559 	conf_unbind_key(c, kb);
560 	if (cmd == NULL) {
561 		free(kb);
562 		return 1;
563 	}
564 	cargs = xcalloc(1, sizeof(*cargs));
565 	for (i = 0; i < nitems(name_to_func); i++) {
566 		if (strcmp(name_to_func[i].tag, cmd) != 0)
567 			continue;
568 		kb->callback = name_to_func[i].handler;
569 		kb->context = name_to_func[i].context;
570 		cargs->flag = name_to_func[i].flag;
571 		goto out;
572 	}
573 	kb->callback = kbfunc_exec_cmd;
574 	kb->context = CWM_CONTEXT_NONE;
575 	cargs->flag = 0;
576 	cargs->cmd = xstrdup(cmd);
577 out:
578 	kb->cargs = cargs;
579 	TAILQ_INSERT_TAIL(&c->keybindq, kb, entry);
580 	return 1;
581 }
582 
583 static void
conf_unbind_key(struct conf * c,struct bind_ctx * unbind)584 conf_unbind_key(struct conf *c, struct bind_ctx *unbind)
585 {
586 	struct bind_ctx	*key = NULL, *keynxt;
587 
588 	TAILQ_FOREACH_SAFE(key, &c->keybindq, entry, keynxt) {
589 		if ((unbind == NULL) ||
590 		    ((key->modmask == unbind->modmask) &&
591 		     (key->press.keysym == unbind->press.keysym))) {
592 			TAILQ_REMOVE(&c->keybindq, key, entry);
593 			free(key->cargs->cmd);
594 			free(key->cargs);
595 			free(key);
596 		}
597 	}
598 }
599 
600 int
conf_bind_mouse(struct conf * c,const char * bind,const char * cmd)601 conf_bind_mouse(struct conf *c, const char *bind, const char *cmd)
602 {
603 	struct bind_ctx	*mb;
604 	struct cargs	*cargs;
605 	const char	*button, *errstr;
606 	unsigned int	 i;
607 
608 	if ((strcmp(bind, "all") == 0) && (cmd == NULL)) {
609 		conf_unbind_mouse(c, NULL);
610 		return 1;
611 	}
612 	mb = xmalloc(sizeof(*mb));
613 	button = conf_bind_mask(bind, &mb->modmask);
614 	mb->press.button = strtonum(button, Button1, Button5, &errstr);
615 	if (errstr) {
616 		warnx("button number is %s: %s", errstr, button);
617 		free(mb);
618 		return 0;
619 	}
620 	conf_unbind_mouse(c, mb);
621 	if (cmd == NULL) {
622 		free(mb);
623 		return 1;
624 	}
625 	cargs = xcalloc(1, sizeof(*cargs));
626 	for (i = 0; i < nitems(name_to_func); i++) {
627 		if (strcmp(name_to_func[i].tag, cmd) != 0)
628 			continue;
629 		mb->callback = name_to_func[i].handler;
630 		mb->context = name_to_func[i].context;
631 		cargs->flag = name_to_func[i].flag;
632 		goto out;
633 	}
634 	mb->callback = kbfunc_exec_cmd;
635 	mb->context = CWM_CONTEXT_NONE;
636 	cargs->flag = 0;
637 	cargs->cmd = xstrdup(cmd);
638 out:
639 	mb->cargs = cargs;
640 	TAILQ_INSERT_TAIL(&c->mousebindq, mb, entry);
641 	return 1;
642 }
643 
644 static void
conf_unbind_mouse(struct conf * c,struct bind_ctx * unbind)645 conf_unbind_mouse(struct conf *c, struct bind_ctx *unbind)
646 {
647 	struct bind_ctx		*mb = NULL, *mbnxt;
648 
649 	TAILQ_FOREACH_SAFE(mb, &c->mousebindq, entry, mbnxt) {
650 		if ((unbind == NULL) ||
651 		    ((mb->modmask == unbind->modmask) &&
652 		     (mb->press.button == unbind->press.button))) {
653 			TAILQ_REMOVE(&c->mousebindq, mb, entry);
654 			free(mb->cargs->cmd);
655 			free(mb->cargs);
656 			free(mb);
657 		}
658 	}
659 }
660 
661 void
conf_grab_kbd(Window win)662 conf_grab_kbd(Window win)
663 {
664 	struct bind_ctx	*kb;
665 	KeyCode		 kc;
666 	unsigned int	 i;
667 
668 	XUngrabKey(X_Dpy, AnyKey, AnyModifier, win);
669 
670 	TAILQ_FOREACH(kb, &Conf.keybindq, entry) {
671 		kc = XKeysymToKeycode(X_Dpy, kb->press.keysym);
672 		if ((XkbKeycodeToKeysym(X_Dpy, kc, 0, 0) != kb->press.keysym) &&
673 		    (XkbKeycodeToKeysym(X_Dpy, kc, 0, 1) == kb->press.keysym))
674 			kb->modmask |= ShiftMask;
675 
676 		for (i = 0; i < nitems(ignore_mods); i++)
677 			XGrabKey(X_Dpy, kc, (kb->modmask | ignore_mods[i]), win,
678 			    True, GrabModeAsync, GrabModeAsync);
679 	}
680 }
681 
682 void
conf_grab_mouse(Window win)683 conf_grab_mouse(Window win)
684 {
685 	struct bind_ctx	*mb;
686 	unsigned int	 i;
687 
688 	XUngrabButton(X_Dpy, AnyButton, AnyModifier, win);
689 
690 	TAILQ_FOREACH(mb, &Conf.mousebindq, entry) {
691 		if (mb->context != CWM_CONTEXT_CC)
692 			continue;
693 		for (i = 0; i < nitems(ignore_mods); i++) {
694 			XGrabButton(X_Dpy, mb->press.button,
695 			    (mb->modmask | ignore_mods[i]), win, False,
696 			    BUTTONMASK, GrabModeAsync, GrabModeSync,
697 			    None, None);
698 		}
699 	}
700 }
701