2 * Copyright (c) 2020 Omar Polo <op@omarpolo.com>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 #include "star-platinum.h"
24 #include <X11/XKBlib.h>
25 #include <X11/Xutil.h>
28 # define pledge(a, b) 0
42 int ignored_modifiers[] = {
44 LockMask, /* caps lock */
45 Mod2Mask, /* num lock */
46 Mod3Mask, /* scroll lock */
50 #define IGNORE_MODIFIERS (LockMask | Mod2Mask | Mod3Mask | Mod5Mask)
53 yyerror(const char *e)
56 fprintf(stderr, "%s:%d %s\n", fname, yylineno, e);
65 home = getenv("HOME");
66 xdg = getenv("XDG_CONFIG_HOME");
68 if (home == NULL && xdg == NULL)
71 /* file in home directory >>>>>> XDG shit */
73 if (asprintf(&t, "%s/.star-platinum.conf", home) == -1)
75 if (access(t, R_OK) == 0)
79 /* continue to search */
82 /* sanitize XDG_CONFIG_HOME */
85 if (asprintf(&xdg, "%s/.config", home) == -1)
89 if (asprintf(&t, "%s/star-platinum.conf", xdg) == -1)
92 if (access(t, R_OK) == 0)
104 main(int argc, char **argv)
106 int status = 0, dump_config = 0, conftest = 0, ch;
113 while ((ch = getopt(argc, argv, "c:dn")) != -1) {
117 if ((fname = strdup(optarg)) == NULL)
130 fprintf(stderr, "USAGE: %s [-dn] [-c conf]\n", *argv);
136 if ((fname = find_config()) == NULL)
137 errx(1, "can't find a configuration file");
140 if ((yyin = fopen(fname, "r")) == NULL)
141 err(1, "cannot open %s", fname);
155 recfree_group(config);
159 if ((d = XOpenDisplay(NULL)) == NULL) {
160 recfree_group(config);
164 XSetErrorHandler(&error_handler);
166 root = DefaultRootWindow(d);
168 /* grab all the keys */
169 for (g = config; g != NULL; g = g->next)
170 for (r = g->rules; r != NULL; r = r->next)
173 XSelectInput(d, root, KeyPressMask);
183 process_event(config, (XKeyEvent*)&e);
187 printf("Unknown event %d\n", e.type);
193 recfree_group(config);
201 error_handler(Display *d, XErrorEvent *e)
203 fprintf(stderr, "Xlib error %d\n", e->type);
207 /* TODO: it should grab ALL POSSIBLE COMBINATIONS of `ignored_modifiers`! */
209 grabkey(struct key k)
211 static size_t len = sizeof(ignored_modifiers)/sizeof(int);
215 root = DefaultRootWindow(d);
217 /* printf("Grabbing "); printkey(k); printf("\n"); */
218 for (i = 0; i < len; ++i) {
219 XGrabKey(d, XKeysymToKeycode(d, k.key),
220 k.modifier | ignored_modifiers[i],
221 root, False, GrabModeAsync, GrabModeAsync);
226 keycode_to_keysym(unsigned int kc)
228 /* group 0 (?). shift level is 0 because we don't want it*/
229 return XkbKeycodeToKeysym(d, kc, 0, 0);
238 /* one can use (at least) three way to obtain the current
239 * focused window using xlib:
241 * - XQueryTree : you traverse tre tree until you find the
244 * - looking at _NET_ACTIVE_WINDOW in the root window, but
245 * depedns on the window manager to set it
247 * - using XGetInputFocus
249 * I don't know the pro/cons of these, but XGetInputFocus
252 XGetInputFocus(d, &w, &revert_to);
257 send_fake(Window w, struct key k, int pressed)
261 e.type = pressed ? KeyPress : KeyRelease;
265 e.root = DefaultRootWindow(d);
267 e.time = CurrentTime;
269 /* TODO: fix these */
275 e.same_screen = True;
276 e.keycode = XKeysymToKeycode(d, k.key);
277 e.state = k.modifier;
279 XSendEvent(d, w, True, KeyPressMask, (XEvent*)&e);
284 window_match_class(Window w, const char *class)
289 if (!XGetClassHint(d, w, &ch)) {
290 fprintf(stderr, "XGetClassHint failed\n");
294 matched = !strcmp(ch.res_class, class);
306 do_action(struct action a, Window focused, int pressed)
310 send_fake(focused, a.send_key, pressed);
340 new_match(int prop, char *s)
344 if ((m = calloc(1, sizeof(*m))) == NULL)
352 recfree_match(struct match *m)
366 match_window(struct match *m, Window w)
373 return window_match_class(w, m->str);
386 new_rule(struct key k, struct action a)
390 if ((r = calloc(1, sizeof(*r))) == NULL)
392 memcpy(&r->key, &k, sizeof(k));
393 memcpy(&r->action, &a, sizeof(a));
398 recfree_rule(struct rule *r)
411 rule_matched(struct rule *r, struct key k)
416 m &= ~IGNORE_MODIFIERS; /* clear ignored modifiers */
418 return r->key.modifier == m
419 && r->key.key == k.key;
426 new_group(struct match *matches, struct rule *rules)
430 if ((g = calloc(1, sizeof(*g))) == NULL)
432 g->matches = matches;
438 recfree_group(struct group *g)
446 recfree_match(g->matches);
447 recfree_rule(g->rules);
453 process_event(struct group *g, XKeyEvent *e)
457 struct key pressed = {
458 .modifier = e->state,
459 .key = keycode_to_keysym(e->keycode),
462 focused = focused_window();
464 for (; g != NULL; g = g->next) {
465 if (!group_match(g, focused))
468 for (r = g->rules; r != NULL; r = r->next) {
469 if (rule_matched(r, pressed)) {
470 do_action(r->action, focused, e->type == KeyPress);
476 send_fake(focused, pressed, e->type == KeyPress);
480 group_match(struct group *g, Window w)
484 for (m = g->matches; m != NULL; m = m->next)
485 if (match_window(m, w))
491 /* debug/dump stuff */
494 printkey(struct key k)
496 if (k.modifier & ControlMask)
498 if (k.modifier & ShiftMask)
500 if (k.modifier & Mod1Mask)
502 if (k.modifier & Mod4Mask)
505 printf("%s", XKeysymToString(k.key));
509 printaction(struct action a)
511 if (a.type == ASPECIAL) {
513 case ATOGGLE: printf("toggle"); break;
514 case AACTIVATE: printf("activate"); break;
515 case ADEACTIVATE: printf("deactivate"); break;
516 case AIGNORE: printf("ignore"); break;
520 printkey(a.send_key);
525 printmatch(struct match *m)
539 printf(" %s", m->str);
550 printrule(struct rule *r)
559 printaction(r->action);
567 printgroup(struct group *g)
573 printmatch(g->matches);