2 e573cb09 2020-09-27 op * Copyright (c) 2020 Omar Polo <op@omarpolo.com>
4 e573cb09 2020-09-27 op * Permission to use, copy, modify, and distribute this software for any
5 e573cb09 2020-09-27 op * purpose with or without fee is hereby granted, provided that the above
6 e573cb09 2020-09-27 op * copyright notice and this permission notice appear in all copies.
8 e573cb09 2020-09-27 op * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 e573cb09 2020-09-27 op * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 e573cb09 2020-09-27 op * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 e573cb09 2020-09-27 op * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 e573cb09 2020-09-27 op * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 e573cb09 2020-09-27 op * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 e573cb09 2020-09-27 op * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 e573cb09 2020-09-27 op #include "star-platinum.h"
19 e573cb09 2020-09-27 op #include <err.h>
20 e0041ee5 2020-10-01 op #include <signal.h>
21 13cc0916 2020-09-30 op #include <stdarg.h>
22 e573cb09 2020-09-27 op #include <stdio.h>
23 e573cb09 2020-09-27 op #include <string.h>
24 e573cb09 2020-09-27 op #include <unistd.h>
26 e573cb09 2020-09-27 op #include <X11/XKBlib.h>
27 e573cb09 2020-09-27 op #include <X11/Xutil.h>
29 e573cb09 2020-09-27 op #ifndef __OpenBSD__
30 e573cb09 2020-09-27 op # define pledge(a, b) 0
33 e573cb09 2020-09-27 op extern FILE *yyin;
36 e573cb09 2020-09-27 op extern int yylineno;
37 e573cb09 2020-09-27 op int yyparse(void);
39 e573cb09 2020-09-27 op struct group *config;
40 e573cb09 2020-09-27 op int goterror = 0;
44 e573cb09 2020-09-27 op int ignored_modifiers[] = {
45 ecc6fa05 2020-09-29 op 0, /* no modifiers */
46 e573cb09 2020-09-27 op LockMask, /* caps lock */
47 e573cb09 2020-09-27 op Mod2Mask, /* num lock */
48 e573cb09 2020-09-27 op Mod3Mask, /* scroll lock */
49 e573cb09 2020-09-27 op Mod5Mask, /* ? */
52 e573cb09 2020-09-27 op #define IGNORE_MODIFIERS (LockMask | Mod2Mask | Mod3Mask | Mod5Mask)
54 13cc0916 2020-09-30 op __attribute__((__format__ (__printf__, 1, 2)))
56 13cc0916 2020-09-30 op yyerror(const char *fmt, ...)
61 13cc0916 2020-09-30 op va_start(ap, fmt);
64 13cc0916 2020-09-30 op fprintf(stderr, "%s:%d ", fname, yylineno);
65 13cc0916 2020-09-30 op vfprintf(stderr, fmt, ap);
67 13cc0916 2020-09-30 op l = strlen(fmt);
68 13cc0916 2020-09-30 op if (l > 0 && fmt[l-1] != '\n')
69 13cc0916 2020-09-30 op fprintf(stderr, "\n");
77 e573cb09 2020-09-27 op char *home, *xdg, *t;
78 e573cb09 2020-09-27 op int free_xdg = 0;
80 e573cb09 2020-09-27 op home = getenv("HOME");
81 e573cb09 2020-09-27 op xdg = getenv("XDG_CONFIG_HOME");
83 e573cb09 2020-09-27 op if (home == NULL && xdg == NULL)
86 e573cb09 2020-09-27 op /* file in home directory >>>>>> XDG shit */
87 e573cb09 2020-09-27 op if (home != NULL) {
88 e573cb09 2020-09-27 op if (asprintf(&t, "%s/.star-platinum.conf", home) == -1)
90 e573cb09 2020-09-27 op if (access(t, R_OK) == 0)
94 e573cb09 2020-09-27 op /* continue to search */
97 e573cb09 2020-09-27 op /* sanitize XDG_CONFIG_HOME */
98 e573cb09 2020-09-27 op if (xdg == NULL) {
100 e573cb09 2020-09-27 op if (asprintf(&xdg, "%s/.config", home) == -1)
104 e573cb09 2020-09-27 op if (asprintf(&t, "%s/star-platinum.conf", xdg) == -1)
107 e573cb09 2020-09-27 op if (access(t, R_OK) == 0)
112 e573cb09 2020-09-27 op if (free_xdg)
119 e573cb09 2020-09-27 op main(int argc, char **argv)
121 e573cb09 2020-09-27 op int status = 0, dump_config = 0, conftest = 0, ch;
122 e573cb09 2020-09-27 op struct group *g;
123 e573cb09 2020-09-27 op struct rule *r;
127 e0041ee5 2020-10-01 op signal(SIGCHLD, SIG_IGN); /* don't allow zombies */
129 e573cb09 2020-09-27 op fname = NULL;
130 e573cb09 2020-09-27 op while ((ch = getopt(argc, argv, "c:dn")) != -1) {
131 e573cb09 2020-09-27 op switch (ch) {
134 e573cb09 2020-09-27 op if ((fname = strdup(optarg)) == NULL)
135 e573cb09 2020-09-27 op err(1, "strdup");
139 e573cb09 2020-09-27 op dump_config = 1;
143 e573cb09 2020-09-27 op conftest = 1;
147 e573cb09 2020-09-27 op fprintf(stderr, "USAGE: %s [-dn] [-c conf]\n", *argv);
152 e573cb09 2020-09-27 op if (fname == NULL) {
153 e573cb09 2020-09-27 op if ((fname = find_config()) == NULL)
154 e573cb09 2020-09-27 op errx(1, "can't find a configuration file");
157 e573cb09 2020-09-27 op if ((yyin = fopen(fname, "r")) == NULL)
158 e573cb09 2020-09-27 op err(1, "cannot open %s", fname);
160 e573cb09 2020-09-27 op fclose(yyin);
163 e573cb09 2020-09-27 op fname = NULL;
165 e573cb09 2020-09-27 op if (goterror)
168 e573cb09 2020-09-27 op if (dump_config)
169 e573cb09 2020-09-27 op printgroup(config);
171 e573cb09 2020-09-27 op if (conftest) {
172 e573cb09 2020-09-27 op recfree_group(config);
176 e573cb09 2020-09-27 op if ((d = XOpenDisplay(NULL)) == NULL) {
177 e573cb09 2020-09-27 op recfree_group(config);
181 fe0dcf4e 2020-09-29 op XSetErrorHandler(&error_handler);
183 e573cb09 2020-09-27 op root = DefaultRootWindow(d);
185 e573cb09 2020-09-27 op /* grab all the keys */
186 e573cb09 2020-09-27 op for (g = config; g != NULL; g = g->next)
187 e573cb09 2020-09-27 op for (r = g->rules; r != NULL; r = r->next)
188 e573cb09 2020-09-27 op grabkey(r->key);
190 e573cb09 2020-09-27 op XSelectInput(d, root, KeyPressMask);
193 e0041ee5 2020-10-01 op pledge("stdio proc exec", NULL);
196 e573cb09 2020-09-27 op XNextEvent(d, &e);
197 e573cb09 2020-09-27 op switch (e.type) {
198 e573cb09 2020-09-27 op case KeyRelease:
199 e573cb09 2020-09-27 op case KeyPress:
200 e573cb09 2020-09-27 op process_event(config, (XKeyEvent*)&e);
204 e573cb09 2020-09-27 op printf("Unknown event %d\n", e.type);
209 e573cb09 2020-09-27 op XCloseDisplay(d);
210 e573cb09 2020-09-27 op recfree_group(config);
211 e573cb09 2020-09-27 op return status;
218 fe0dcf4e 2020-09-29 op error_handler(Display *d, XErrorEvent *e)
220 fe0dcf4e 2020-09-29 op fprintf(stderr, "Xlib error %d\n", e->type);
224 e573cb09 2020-09-27 op /* TODO: it should grab ALL POSSIBLE COMBINATIONS of `ignored_modifiers`! */
226 e573cb09 2020-09-27 op grabkey(struct key k)
228 e573cb09 2020-09-27 op static size_t len = sizeof(ignored_modifiers)/sizeof(int);
232 e573cb09 2020-09-27 op root = DefaultRootWindow(d);
234 e573cb09 2020-09-27 op /* printf("Grabbing "); printkey(k); printf("\n"); */
235 e573cb09 2020-09-27 op for (i = 0; i < len; ++i) {
236 ecc6fa05 2020-09-29 op XGrabKey(d, XKeysymToKeycode(d, k.key),
237 e573cb09 2020-09-27 op k.modifier | ignored_modifiers[i],
238 e573cb09 2020-09-27 op root, False, GrabModeAsync, GrabModeAsync);
243 e573cb09 2020-09-27 op keycode_to_keysym(unsigned int kc)
245 e573cb09 2020-09-27 op /* group 0 (?). shift level is 0 because we don't want it*/
246 e573cb09 2020-09-27 op return XkbKeycodeToKeysym(d, kc, 0, 0);
250 e573cb09 2020-09-27 op focused_window()
253 e573cb09 2020-09-27 op int revert_to;
255 e573cb09 2020-09-27 op /* one can use (at least) three way to obtain the current
256 e573cb09 2020-09-27 op * focused window using xlib:
258 e573cb09 2020-09-27 op * - XQueryTree : you traverse tre tree until you find the
261 e573cb09 2020-09-27 op * - looking at _NET_ACTIVE_WINDOW in the root window, but
262 e573cb09 2020-09-27 op * depedns on the window manager to set it
264 e573cb09 2020-09-27 op * - using XGetInputFocus
266 e573cb09 2020-09-27 op * I don't know the pro/cons of these, but XGetInputFocus
267 e573cb09 2020-09-27 op * seems the easiest.
269 e573cb09 2020-09-27 op XGetInputFocus(d, &w, &revert_to);
274 2eb11d8e 2020-10-04 op send_fake(Window w, struct key k, XKeyEvent *original)
279 2eb11d8e 2020-10-04 op * this needs to be hijacked. original->window is the root
280 2eb11d8e 2020-10-04 op * window (since we grabbed the key there) and we want to
281 2eb11d8e 2020-10-04 op * deliver the key to another window.
283 2eb11d8e 2020-10-04 op e.window = w;
285 2eb11d8e 2020-10-04 op /* this is the fake key */
286 e573cb09 2020-09-27 op e.keycode = XKeysymToKeycode(d, k.key);
287 e573cb09 2020-09-27 op e.state = k.modifier;
289 2eb11d8e 2020-10-04 op /* the rest is just copying fields from the original event */
290 2eb11d8e 2020-10-04 op e.type = original->type;
291 2eb11d8e 2020-10-04 op e.display = original->display;
292 2eb11d8e 2020-10-04 op e.root = original->root;
293 2eb11d8e 2020-10-04 op e.subwindow = original->subwindow;
294 2eb11d8e 2020-10-04 op e.time = original->time;
295 2eb11d8e 2020-10-04 op e.same_screen = original->same_screen;
296 2eb11d8e 2020-10-04 op e.x = original->x;
297 2eb11d8e 2020-10-04 op e.y = original->y;
298 2eb11d8e 2020-10-04 op e.x_root = original->x_root;
299 2eb11d8e 2020-10-04 op e.y_root = original->y_root;
301 e573cb09 2020-09-27 op XSendEvent(d, w, True, KeyPressMask, (XEvent*)&e);
306 e573cb09 2020-09-27 op window_match_class(Window w, const char *class)
308 e573cb09 2020-09-27 op XClassHint ch;
311 ecc6fa05 2020-09-29 op if (!XGetClassHint(d, w, &ch)) {
312 e573cb09 2020-09-27 op fprintf(stderr, "XGetClassHint failed\n");
316 e573cb09 2020-09-27 op matched = !strcmp(ch.res_class, class);
318 e573cb09 2020-09-27 op XFree(ch.res_name);
319 e573cb09 2020-09-27 op XFree(ch.res_class);
321 e573cb09 2020-09-27 op return matched;
328 2eb11d8e 2020-10-04 op do_action(struct action a, Window focused, XKeyEvent *original)
330 e573cb09 2020-09-27 op switch (a.type) {
332 2eb11d8e 2020-10-04 op send_fake(focused, a.send_key, original);
335 e573cb09 2020-09-27 op case ASPECIAL:
336 e573cb09 2020-09-27 op switch (a.special) {
337 e573cb09 2020-09-27 op case ATOGGLE:
338 e573cb09 2020-09-27 op case AACTIVATE:
339 e573cb09 2020-09-27 op case ADEACTIVATE:
340 e573cb09 2020-09-27 op printf("TODO\n");
343 e573cb09 2020-09-27 op case AIGNORE:
347 e573cb09 2020-09-27 op /* unreachable */
352 e0041ee5 2020-10-01 op case AEXEC: {
354 e0041ee5 2020-10-01 op const char *sh;
356 e0041ee5 2020-10-01 op switch (p = fork()) {
358 e0041ee5 2020-10-01 op err(1, "fork");
361 e0041ee5 2020-10-01 op if ((sh = getenv("SHELL")) == NULL)
362 e0041ee5 2020-10-01 op sh = "/bin/sh";
363 e0041ee5 2020-10-01 op printf("before exec'ing %s -c '%s'\n", sh, a.str);
364 e0041ee5 2020-10-01 op execlp(sh, sh, "-c", a.str, NULL);
365 e0041ee5 2020-10-01 op err(1, "execlp");
372 e573cb09 2020-09-27 op /* unreachable */
378 e0041ee5 2020-10-01 op free_action(struct action a)
380 e0041ee5 2020-10-01 op if (a.type == AEXEC)
387 e573cb09 2020-09-27 op struct match *
388 e573cb09 2020-09-27 op new_match(int prop, char *s)
390 e573cb09 2020-09-27 op struct match *m;
392 e573cb09 2020-09-27 op if ((m = calloc(1, sizeof(*m))) == NULL)
393 e573cb09 2020-09-27 op err(1, "calloc");
394 e573cb09 2020-09-27 op m->prop = prop;
400 e573cb09 2020-09-27 op recfree_match(struct match *m)
402 e573cb09 2020-09-27 op struct match *mt;
404 e573cb09 2020-09-27 op if (m == NULL)
407 e573cb09 2020-09-27 op mt = m->next;
408 e573cb09 2020-09-27 op free(m->str);
410 e573cb09 2020-09-27 op recfree_match(mt);
414 e573cb09 2020-09-27 op match_window(struct match *m, Window w)
416 ecc6fa05 2020-09-29 op switch (m->prop) {
421 e573cb09 2020-09-27 op return window_match_class(w, m->str);
425 e573cb09 2020-09-27 op /* unreachable */
433 e573cb09 2020-09-27 op struct rule *
434 e573cb09 2020-09-27 op new_rule(struct key k, struct action a)
436 e573cb09 2020-09-27 op struct rule *r;
438 e573cb09 2020-09-27 op if ((r = calloc(1, sizeof(*r))) == NULL)
439 e573cb09 2020-09-27 op err(1, "calloc");
440 e573cb09 2020-09-27 op memcpy(&r->key, &k, sizeof(k));
441 e573cb09 2020-09-27 op memcpy(&r->action, &a, sizeof(a));
446 e573cb09 2020-09-27 op recfree_rule(struct rule *r)
448 e573cb09 2020-09-27 op struct rule *rt;
450 e573cb09 2020-09-27 op if (r == NULL)
453 e573cb09 2020-09-27 op rt = r->next;
455 e573cb09 2020-09-27 op recfree_rule(rt);
459 e573cb09 2020-09-27 op rule_matched(struct rule *r, struct key k)
461 e573cb09 2020-09-27 op unsigned int m;
463 e573cb09 2020-09-27 op m = k.modifier;
464 e573cb09 2020-09-27 op m &= ~IGNORE_MODIFIERS; /* clear ignored modifiers */
466 e573cb09 2020-09-27 op return r->key.modifier == m
467 e573cb09 2020-09-27 op && r->key.key == k.key;
473 e573cb09 2020-09-27 op struct group *
474 e573cb09 2020-09-27 op new_group(struct match *matches, struct rule *rules)
476 e573cb09 2020-09-27 op struct group *g;
478 e573cb09 2020-09-27 op if ((g = calloc(1, sizeof(*g))) == NULL)
479 e573cb09 2020-09-27 op err(1, "calloc");
480 e573cb09 2020-09-27 op g->matches = matches;
481 e573cb09 2020-09-27 op g->rules = rules;
486 e573cb09 2020-09-27 op recfree_group(struct group *g)
488 e573cb09 2020-09-27 op struct group *gt;
490 e573cb09 2020-09-27 op if (g == NULL)
493 e573cb09 2020-09-27 op gt = g->next;
494 e573cb09 2020-09-27 op recfree_match(g->matches);
495 e573cb09 2020-09-27 op recfree_rule(g->rules);
497 e573cb09 2020-09-27 op recfree_group(gt);
501 e573cb09 2020-09-27 op process_event(struct group *g, XKeyEvent *e)
503 e573cb09 2020-09-27 op Window focused;
504 e573cb09 2020-09-27 op struct rule *r;
505 e573cb09 2020-09-27 op struct key pressed = {
506 e573cb09 2020-09-27 op .modifier = e->state,
507 e573cb09 2020-09-27 op .key = keycode_to_keysym(e->keycode),
510 e573cb09 2020-09-27 op focused = focused_window();
512 e573cb09 2020-09-27 op for (; g != NULL; g = g->next) {
513 e573cb09 2020-09-27 op if (!group_match(g, focused))
516 e573cb09 2020-09-27 op for (r = g->rules; r != NULL; r = r->next) {
517 e573cb09 2020-09-27 op if (rule_matched(r, pressed)) {
518 2eb11d8e 2020-10-04 op do_action(r->action, focused, e);
524 2eb11d8e 2020-10-04 op send_fake(focused, pressed, e);
528 e573cb09 2020-09-27 op group_match(struct group *g, Window w)
530 e573cb09 2020-09-27 op struct match *m;
532 e573cb09 2020-09-27 op for (m = g->matches; m != NULL; m = m->next)
533 e573cb09 2020-09-27 op if (match_window(m, w))
539 e573cb09 2020-09-27 op /* debug/dump stuff */
542 e573cb09 2020-09-27 op printkey(struct key k)
544 e573cb09 2020-09-27 op if (k.modifier & ControlMask)
545 e573cb09 2020-09-27 op printf("C-");
546 e573cb09 2020-09-27 op if (k.modifier & ShiftMask)
547 e573cb09 2020-09-27 op printf("S-");
548 e573cb09 2020-09-27 op if (k.modifier & Mod1Mask)
549 e573cb09 2020-09-27 op printf("M-");
550 e573cb09 2020-09-27 op if (k.modifier & Mod4Mask)
551 e573cb09 2020-09-27 op printf("s-");
553 e573cb09 2020-09-27 op printf("%s", XKeysymToString(k.key));
557 e573cb09 2020-09-27 op printaction(struct action a)
559 e0041ee5 2020-10-01 op switch (a.type) {
560 e0041ee5 2020-10-01 op case ASPECIAL:
561 e573cb09 2020-09-27 op switch (a.special) {
562 e573cb09 2020-09-27 op case ATOGGLE: printf("toggle"); break;
563 e573cb09 2020-09-27 op case AACTIVATE: printf("activate"); break;
564 e573cb09 2020-09-27 op case ADEACTIVATE: printf("deactivate"); break;
565 e573cb09 2020-09-27 op case AIGNORE: printf("ignore"); break;
566 e0041ee5 2020-10-01 op default: abort(); /* unreachable */
571 e573cb09 2020-09-27 op printf("send key ");
572 e573cb09 2020-09-27 op printkey(a.send_key);
576 e0041ee5 2020-10-01 op printf("exec %s", a.str);
580 e0041ee5 2020-10-01 op /* unreachable */
586 e573cb09 2020-09-27 op printmatch(struct match *m)
588 e573cb09 2020-09-27 op if (m == NULL) {
589 e573cb09 2020-09-27 op printf("(null)");
592 e573cb09 2020-09-27 op printf("match ");
593 e573cb09 2020-09-27 op switch (m->prop) {
595 e0041ee5 2020-10-01 op printf("all");
598 e0041ee5 2020-10-01 op printf("class %s", m->str);
601 e0041ee5 2020-10-01 op /* unreachable */
605 e573cb09 2020-09-27 op if (m->next == NULL)
606 e573cb09 2020-09-27 op printf("\n");
608 e573cb09 2020-09-27 op printf(" ; ");
609 e573cb09 2020-09-27 op printmatch(m->next);
614 e573cb09 2020-09-27 op printrule(struct rule *r)
616 e573cb09 2020-09-27 op if (r == NULL) {
617 e573cb09 2020-09-27 op printf("(null)");
620 e573cb09 2020-09-27 op printf("on ");
621 e573cb09 2020-09-27 op printkey(r->key);
622 e573cb09 2020-09-27 op printf(" do ");
623 e573cb09 2020-09-27 op printaction(r->action);
624 e573cb09 2020-09-27 op printf("\n");
626 e573cb09 2020-09-27 op if (r->next != NULL)
627 e573cb09 2020-09-27 op printrule(r->next);
631 e573cb09 2020-09-27 op printgroup(struct group *g)
633 e573cb09 2020-09-27 op if (g == NULL) {
634 e573cb09 2020-09-27 op printf("(null)");
637 e573cb09 2020-09-27 op printmatch(g->matches);
638 e573cb09 2020-09-27 op printf("\n");
639 e573cb09 2020-09-27 op printrule(g->rules);
640 e573cb09 2020-09-27 op printf("\n\n");
642 e573cb09 2020-09-27 op if (g->next != NULL)
643 e573cb09 2020-09-27 op printgroup(g->next);