commit e573cb09321087dc40dc088a53a9e079a31ecfe5 from: Omar Polo date: Sun Sep 27 21:09:15 2020 UTC initial commit commit - /dev/null commit + e573cb09321087dc40dc088a53a9e079a31ecfe5 blob - /dev/null blob + e73266e856b84e6a78ec62202d12c963fe8525cf (mode 644) --- /dev/null +++ .gitignore @@ -0,0 +1,7 @@ +TAGS +lex.yy.c +y.tab.c +y.tab.h +*.o +*.tar.gz +star-platinum blob - /dev/null blob + 37af6c334b108aebf1d314ab4e2986358cec9122 (mode 644) --- /dev/null +++ LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2020 Omar Polo + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. blob - /dev/null blob + c9661be9fd15f246901aa9ad08c9a83684fa1269 (mode 644) --- /dev/null +++ Makefile @@ -0,0 +1,36 @@ +include config.mk + +all: star-platinum TAGS + +.PHONY: all clean archive + +lex.yy.c: lex.l y.tab.c + ${LEX} lex.l + +y.tab.c: parse.y + ${YACC} -d parse.y + +OBJS = star-platinum.o lex.yy.o y.tab.o +star-platinum: ${OBJS} + ${CC} -o $@ ${OBJS} ${LDFLAGS} + +TAGS: star-platinum.c star-platinum.h parse.y lex.l + -${ETAGS} star-platinum.c star-platinum.h parse.y lex.l + +clean: + rm -f star-platinum ${OBJS} lex.yy.c y.tab.c y.tab.h y.output + rm -f star-platinum.tar.gz + rm -f TAGS + +SRC += Makefile config.mk lex.l parse.y star-platinum.c +SRC += star-platinum.h star-platinum.1 star-platinum.conf.5 +star-platinum.tar.gz: ${SRC} + rm -f star-platinum.tar.gz + mkdir star-platinum-archive + cp ${SRC} ./star-platinum-archive + tar -czvf star-platinum.tar.gz \ + -s '/^star-platinum-archive/star-platinum/gp' \ + star-platinum-archive + rm -r star-platinum-archive + +archive: star-platinum.tar.gz blob - /dev/null blob + ad55b3892535095587bf543c20f62e6fdd4468d0 (mode 644) --- /dev/null +++ README.md @@ -0,0 +1,34 @@ +# star platinum + +`star-platinum` is a key binding manager for x11. It acts primarily +as a translator: the canonical example is to Emacs-ify various +programs by customizing how some key are handled. + +Check out the [manpage](star-platinum.1) for more information. + +## Building + +`star-platinum` depends on `xlib` and needs a C compiler, `yacc` and +`lex` to compile. With that in place, it's as easy as + + make + +Configuration for the build process can be found in `config.mk`, but +you usually don't need to modify it: passing the variables to make +should be enough. For instance, to build with `gcc` + + make CC=gcc + +## FAQ + + - *something something* bison *something something* + + `star-platinum` should build with GNU `bison` and flex, but I've + still not tried. `bison` has some defaults different from `yacc` + IIRC, so additional flags may be needed. + + - the name is a jojo reference? + + Sort of. I was listening to 「sono chi kioku」, the fourth opening, + while I was playing with the idea of translating the key. Given + that I'm generally bad at naming things... blob - /dev/null blob + 8ec174409aebdeb1a9e6838306712a6e199d1ad3 (mode 644) --- /dev/null +++ config.mk @@ -0,0 +1,8 @@ +CC = cc +CFLAGS = -Wall -g -I/usr/X11R6/include +LDFLAGS = -L/usr/X11R6/lib/ -lX11 + +LEX = lex +YACC = yacc + +ETAGS = etags blob - /dev/null blob + b02ff034fe7ec6b73083b8f43f90c995c9d0ba9d (mode 644) --- /dev/null +++ lex.l @@ -0,0 +1,112 @@ +/* -*- mode:fundamental; indent-tabs-mode: t; -*- */ +%{ + +/* + * Copyright (c) 2020 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "star-platinum.h" + +#include +#include + +#include +#include + +#include "y.tab.h" + +int state = 0; +KeySym key = 0; + +%} + +%x COMMENT +%x KEY + +%% + +"#" { BEGIN(COMMENT); } +\n { yylineno++; BEGIN(INITIAL); } +. ; + +[ \t\r\v\f]+ ; + +"match" return TMATCH; +"class" return TCLASS; +"on" return TON; +"do" return TDO; +"toggle" return TTOGGLE; +"activate" return TACTIVATE; +"deactivate" return TDEACTIVATE; +"ignore" return TIGNORE; + +"\n" yylineno++; return '\n'; + +[-a-zA-Z0-9]+ { + char *ident; + if ((ident = strdup(yytext)) == NULL) + err(1, "strdup"); + yylval.str = ident; + return TSTRING; + } + +"\"" { BEGIN(KEY); } +"\"" { + BEGIN(INITIAL); + + if (key == NoSymbol) + return TERR; + + yylval.key = (struct key){ state, key }; + state = 0; + key = 0; + return TKEY; + } + +C- { state |= ControlMask; } +S- { state |= ShiftMask; } +M- { state |= Mod1Mask; } +s- { state |= Mod4Mask; } + +<[_a-zA-Z]+> { + char *c; + + if ((c = strdup(yytext)) == NULL) + err(1, "strdup"); + + c++; /* skip the < */ + c[strlen(c)-1] = '\0'; /* trim the > */ + + key = XStringToKeysym(c); + + free(--c); + } + +SPC { key = XK_space; } +RET { key = XK_Return; } + +"(" { key = XK_parenleft; } +")" { key = XK_parenright; } + +. { key = XStringToKeysym(yytext); } + + +%% + +int +yywrap(void) +{ + return 1; +} blob - /dev/null blob + 0c76c3c970710a5bb2ef5bcf34fb6faaaa8e2a8f (mode 644) --- /dev/null +++ parse.y @@ -0,0 +1,91 @@ +/* -*- mode: fundamental; indent-tabs-mode: t; -*- */ +%{ + +/* + * Copyright (c) 2020 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "star-platinum.h" + +void yyerror(const char*); + +/* + * #define YYDEBUG 1 + * int yydebug = 1; + */ + +#define SPECIAL(X) ((struct action){.type = (ASPECIAL), .special = (X) }) +#define FAKE_KEY(K) ((struct action){.type = (AFAKE), .send_key = (K) }) + +%} + +%union { + struct key key; + char *str; + struct action action; + struct match *match; + struct rule *rule; + struct group *group; +} + +%token TMATCH TCLASS +%token TON TDO +%token TTOGGLE TACTIVATE TDEACTIVATE TIGNORE +%token TERR + +%token TKEY +%token TSTRING + +%type action +%type matches match +%type keys key +%type groups group + +%% + +groups : /* empty */ { $$ = NULL; } + | groups group { $2->next = $1; config = $$ = $2; } + | error '\n' + ; + +group : matches keys { $$ = new_group($1, $2); } + ; + +matches : /* empty */ { $$ = NULL; } + | matches '\n' { $$ = $1; } + | matches match '\n' { $2->next = $1; $$ = $2; } + ; + +match : TMATCH TCLASS TSTRING { $$ = new_match(MCLASS, $3); } + ; + +keys : /* empty */ { $$ = NULL; } + | keys '\n' { $$ = $1; } + | keys key '\n' { $2->next = $1; $$ = $2; } + ; + +key : TON TKEY TDO action { $$ = new_rule($2, $4); } + ; + +action : TKEY { $$ = FAKE_KEY($1); } + | TTOGGLE { $$ = SPECIAL(ATOGGLE); } + | TACTIVATE { $$ = SPECIAL(AACTIVATE); } + | TDEACTIVATE { $$ = SPECIAL(ADEACTIVATE); } + | TIGNORE { $$ = SPECIAL(AIGNORE); } + ; blob - /dev/null blob + 356d0c55e1a07747c5ca62cd17eb749c192d9e5f (mode 644) --- /dev/null +++ star-platinum.1 @@ -0,0 +1,56 @@ +.\" Copyright (c) 2020 Omar Polo +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.Dd $Mdocdate: September 27 2020$ +.Dt STAR-PLATINUM 1 +.Os +.Sh NAME +.Nm star-platinum +.Nd key translator for xorg +.Sh SYNOPSIS +.Nm +.Bk -words +.Op Fl dn +.Op Fl c Ar config +.Ek +.Sh DESCRIPTION +.Nm +is an utility to translate keybindings for X11 programs. +It acts by grabbing some keys, say control n, and send a fake events +when you press that sequence. +The fake event will be possibly a different key, depending on the +application that had the focus. +.Pp +The options are as follows: +.Bl -tag -width keyword +.It Fl c Ar conf +load the configuration from the given file instead of the default +.It Fl d +print the config (as parsed). +Useful for debugging +.It Fl n +config test mode +.El +.Sh CONFIGURATION FILE +If a configuration file is not given with the +.Fl c +option, the default behaviour is to search for +.Pa $HOME/.star-platinum.conf +or +.Pa $XDG_CONFIG_HOME/star-platinum.conf +(where +.Pa $XDG_CONFIG_HOME +if not defined is assumed +.Pa $HOME/.config ) +.Sh SEE ALSO +.Xr star-platinum.conf 5 blob - /dev/null blob + 9db19aa52b32d963822a25e3b440f17cfad7e25c (mode 644) --- /dev/null +++ star-platinum.c @@ -0,0 +1,568 @@ +/* + * Copyright (c) 2020 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "star-platinum.h" + +#include +#include +#include +#include + +#include +#include + +#ifndef __OpenBSD__ +# define pledge(a, b) 0 +#endif + +extern FILE *yyin; +char *fname; + +extern int yylineno; +int yyparse(void); + +struct group *config; +int goterror = 0; + +Display *d; + +int ignored_modifiers[] = { + 0, /* no modifiers */ + LockMask, /* caps lock */ + Mod2Mask, /* num lock */ + Mod3Mask, /* scroll lock */ + Mod5Mask, /* ? */ +}; + +#define IGNORE_MODIFIERS (LockMask | Mod2Mask | Mod3Mask | Mod5Mask) + +void +yyerror(const char *e) +{ + goterror = 1; + fprintf(stderr, "%s:%d %s\n", fname, yylineno, e); +} + +char * +find_config() +{ + char *home, *xdg, *t; + int free_xdg = 0; + + home = getenv("HOME"); + xdg = getenv("XDG_CONFIG_HOME"); + + if (home == NULL && xdg == NULL) + return NULL; + + /* file in home directory >>>>>> XDG shit */ + if (home != NULL) { + if (asprintf(&t, "%s/.star-platinum.conf", home) == -1) + return NULL; + if (access(t, R_OK) == 0) + return t; + + free(t); + /* continue to search */ + } + + /* sanitize XDG_CONFIG_HOME */ + if (xdg == NULL) { + free_xdg = 1; + if (asprintf(&xdg, "%s/.config", home) == -1) + return NULL; + } + + if (asprintf(&t, "%s/star-platinum.conf", xdg) == -1) + goto err; + + if (access(t, R_OK) == 0) + return t; + free(t); + +err: + if (free_xdg) + free(xdg); + + return NULL; +} + +int +main(int argc, char **argv) +{ + int status = 0, dump_config = 0, conftest = 0, ch; + struct group *g; + struct rule *r; + XEvent e; + Window root; + + fname = NULL; + while ((ch = getopt(argc, argv, "c:dn")) != -1) { + switch (ch) { + case 'c': + free(fname); + if ((fname = strdup(optarg)) == NULL) + err(1, "strdup"); + break; + + case 'd': + dump_config = 1; + break; + + case 'n': + conftest = 1; + break; + + default: + fprintf(stderr, "USAGE: %s [-dn] [-c conf]\n", *argv); + return 1; + } + } + + if (fname == NULL) { + if ((fname = find_config()) == NULL) + errx(1, "can't find a configuration file"); + } + + if ((yyin = fopen(fname, "r")) == NULL) + err(1, "cannot open %s", fname); + yyparse(); + fclose(yyin); + + free(fname); + fname = NULL; + + if (goterror) + return 1; + + if (dump_config) + printgroup(config); + + if (conftest) { + recfree_group(config); + return 0; + } + + if ((d = XOpenDisplay(NULL)) == NULL) { + recfree_group(config); + return 1; + } + + root = DefaultRootWindow(d); + + /* grab all the keys */ + for (g = config; g != NULL; g = g->next) + for (r = g->rules; r != NULL; r = r->next) + grabkey(r->key); + + XSelectInput(d, root, KeyPressMask); + XFlush(d); + + pledge("stdio", ""); + + while (1) { + XNextEvent(d, &e); + switch (e.type) { + case KeyRelease: + case KeyPress: + process_event(config, (XKeyEvent*)&e); + break; + + default: + printf("Unknown event %d\n", e.type); + break; + } + } + + XCloseDisplay(d); + recfree_group(config); + return status; +} + + +/* xlib */ + +/* TODO: it should grab ALL POSSIBLE COMBINATIONS of `ignored_modifiers`! */ +void +grabkey(struct key k) +{ + static size_t len = sizeof(ignored_modifiers)/sizeof(int); + size_t i; + Window root; + + root = DefaultRootWindow(d); + + /* printf("Grabbing "); printkey(k); printf("\n"); */ + for (i = 0; i < len; ++i) { + XGrabKey(d, XKeysymToKeycode(d, k.key), + k.modifier | ignored_modifiers[i], + root, False, GrabModeAsync, GrabModeAsync); + } +} + +KeySym +keycode_to_keysym(unsigned int kc) +{ + /* group 0 (?). shift level is 0 because we don't want it*/ + return XkbKeycodeToKeysym(d, kc, 0, 0); +} + +Window +focused_window() +{ + Window w; + int revert_to; + + /* one can use (at least) three way to obtain the current + * focused window using xlib: + * + * - XQueryTree : you traverse tre tree until you find the + * window + * + * - looking at _NET_ACTIVE_WINDOW in the root window, but + * depedns on the window manager to set it + * + * - using XGetInputFocus + * + * I don't know the pro/cons of these, but XGetInputFocus + * seems the easiest. + */ + XGetInputFocus(d, &w, &revert_to); + return w; +} + +void +send_fake(Window w, struct key k, int pressed) +{ + XKeyEvent e; + + e.type = pressed ? KeyPress : KeyRelease; + + e.display = d; + e.window = w; + e.root = DefaultRootWindow(d); + e.subwindow = None; + e.time = CurrentTime; + + /* TODO: fix these */ + e.x = 1; + e.y = 1; + e.x_root = 1; + e.y_root = 1; + + e.same_screen = True; + e.keycode = XKeysymToKeycode(d, k.key); + e.state = k.modifier; + + XSendEvent(d, w, True, KeyPressMask, (XEvent*)&e); + XFlush(d); +} + +int +window_match_class(Window w, const char *class) +{ + XClassHint ch; + int matched; + + if (!XGetClassHint(d, w, &ch)) { + fprintf(stderr, "XGetClassHint failed\n"); + return 0; + } + + matched = !strcmp(ch.res_class, class); + + XFree(ch.res_name); + XFree(ch.res_class); + + return matched; +} + + +/* action */ + +void +do_action(struct action a, Window focused, int pressed) +{ + switch (a.type) { + case AFAKE: + send_fake(focused, a.send_key, pressed); + break; + + case ASPECIAL: + switch (a.special) { + case ATOGGLE: + case AACTIVATE: + case ADEACTIVATE: + printf("TODO\n"); + break; + + case AIGNORE: + break; + + default: + /* unreachable */ + abort(); + } + break; + + default: + /* unreachable */ + abort(); + } +} + + +/* match */ + +struct match * +new_match(int prop, char *s) +{ + struct match *m; + + if ((m = calloc(1, sizeof(*m))) == NULL) + err(1, "calloc"); + m->prop = prop; + m->str = s; + return m; +} + +void +recfree_match(struct match *m) +{ + struct match *mt; + + if (m == NULL) + return; + + mt = m->next; + free(m->str); + free(m); + recfree_match(mt); +} + +int +match_window(struct match *m, Window w) +{ + switch (m->prop) { + case MCLASS: + return window_match_class(w, m->str); + break; + + default: + /* unreachable */ + abort(); + } +} + + +/* rule */ + +struct rule * +new_rule(struct key k, struct action a) +{ + struct rule *r; + + if ((r = calloc(1, sizeof(*r))) == NULL) + err(1, "calloc"); + memcpy(&r->key, &k, sizeof(k)); + memcpy(&r->action, &a, sizeof(a)); + return r; +} + +void +recfree_rule(struct rule *r) +{ + struct rule *rt; + + if (r == NULL) + return; + + rt = r->next; + free(r); + recfree_rule(rt); +} + +int +rule_matched(struct rule *r, struct key k) +{ + unsigned int m; + + m = k.modifier; + m &= ~IGNORE_MODIFIERS; /* clear ignored modifiers */ + + return r->key.modifier == m + && r->key.key == k.key; +} + + +/* group */ + +struct group * +new_group(struct match *matches, struct rule *rules) +{ + struct group *g; + + if ((g = calloc(1, sizeof(*g))) == NULL) + err(1, "calloc"); + g->matches = matches; + g->rules = rules; + return g; +} + +void +recfree_group(struct group *g) +{ + struct group *gt; + + if (g == NULL) + return; + + gt = g->next; + recfree_match(g->matches); + recfree_rule(g->rules); + free(g); + recfree_group(gt); +} + +void +process_event(struct group *g, XKeyEvent *e) +{ + Window focused; + struct rule *r; + struct key pressed = { + .modifier = e->state, + .key = keycode_to_keysym(e->keycode), + }; + + focused = focused_window(); + + for (; g != NULL; g = g->next) { + if (!group_match(g, focused)) + continue; + + for (r = g->rules; r != NULL; r = r->next) { + if (rule_matched(r, pressed)) { + do_action(r->action, focused, e->type == KeyPress); + return; + } + } + } + + send_fake(focused, pressed, e->type == KeyPress); +} + +int +group_match(struct group *g, Window w) +{ + struct match *m; + + for (m = g->matches; m != NULL; m = m->next) + if (match_window(m, w)) + return 1; + return 0; +} + + +/* debug/dump stuff */ + +void +printkey(struct key k) +{ + if (k.modifier & ControlMask) + printf("C-"); + if (k.modifier & ShiftMask) + printf("S-"); + if (k.modifier & Mod1Mask) + printf("M-"); + if (k.modifier & Mod4Mask) + printf("s-"); + + printf("%s", XKeysymToString(k.key)); +} + +void +printaction(struct action a) +{ + if (a.type == ASPECIAL) { + switch (a.special) { + case ATOGGLE: printf("toggle"); break; + case AACTIVATE: printf("activate"); break; + case ADEACTIVATE: printf("deactivate"); break; + case AIGNORE: printf("ignore"); break; + } + } else { + printf("send key "); + printkey(a.send_key); + } +} + +void +printmatch(struct match *m) +{ + if (m == NULL) { + printf("(null)"); + return; + } + printf("match "); + switch (m->prop) { + case MCLASS: + printf("class"); + break; + default: + abort(); + } + printf(" %s", m->str); + + if (m->next == NULL) + printf("\n"); + else { + printf(" ; "); + printmatch(m->next); + } +} + +void +printrule(struct rule *r) +{ + if (r == NULL) { + printf("(null)"); + return; + } + printf("on "); + printkey(r->key); + printf(" do "); + printaction(r->action); + printf("\n"); + + if (r->next != NULL) + printrule(r->next); +} + +void +printgroup(struct group *g) +{ + if (g == NULL) { + printf("(null)"); + return; + } + printmatch(g->matches); + printf("\n"); + printrule(g->rules); + printf("\n\n"); + + if (g->next != NULL) + printgroup(g->next); +} blob - /dev/null blob + 3352960340b4db625d7226c68a4612bc101bcf72 (mode 644) --- /dev/null +++ star-platinum.conf.5 @@ -0,0 +1,83 @@ +.\" Copyright (c) 2020 Omar Polo +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.Dd $Mdocdate: September 27 2020$ +.Dt STAR-PLATINUM.CONF 5 +.Os +.Sh NAME +.Nm star-platinum.conf +.Nd star-platinum configuration file +.Sh DESCRIPTION +.Nm +is the configuration file for the +.Xr star-platinum 1 +program. +.Pp +Empty lines are ignored, and comments too. +Comments starts with a # sign and continue until the end of the line +and can be placed everywhere in the configuration file. +.Pp +The configuration file is made of blocks. +Every block starts with one or more +.Ic match +directives and continues with several keybinding directives. +If more than a single +.Ic match +directive is given, the keybinding will be used if at least one of the +.Ic match +directives matches the window. +That is, if both match class Firefox and match class Chromium is +given, the directives will be available for both Firefox and Chromium +windows. +.Pp +A keybinding directive is made of the +.Ic on +keyword followed by a keybinding, followed by the +.Ic do +keyword and an action. +The action can be another keybinding, or a special internal command. +.Pp +The syntax for the keybindings is inspired +.Xr emacs 1 . +A keybindings is written within double quotes and is made of modifiers +followed by the key. +Modifiers follows the +.Xr emacs 1 +notation of using C- to mean control, S- for shift, M- for alt (mod1) +and s- for super (mod4). +The key can be either a plain letter (e.g. x) or the name of the key +written within angular brackets (e.g. ). +.Pp +The only internal command available now is +.Ic ignore Ns : it's a no-op. +.Sh EXAMPLES +The following is an example of configuration file that binds some +.Xr emacs 1 Ns -esque keys for both Firefox and Chromium: +.Bd -literal -offset indent +match class Firefox +match class Chromium-browser +on "C-s" do "C-f" + +# clipboard +on "C-w" do "C-x" +on "M-w" do "C-c" +on "C-y" do "C-v" + +# movements +on "C-n" do "" +on "C-p" do "" +on "C-f" do "" +on "C-b" do "" + +on "C-(" do ignore +.Ed \ No newline at end of file blob - /dev/null blob + 445302b1c862912606adc693d2f237a3a02060b3 (mode 644) --- /dev/null +++ star-platinum.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2020 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef STAR_PLATINUM_H +#define STAR_PLATINUM_H + +/* XXX: why I need to define this to get XK_space/...? + * is this a bad idea? there is another way? */ +#define XK_LATIN1 +#define XK_MISCELLANY + +#include + +#include +#include + +struct key { + unsigned int modifier; + KeySym key; +}; + +struct action { +#define ASPECIAL 1 +#define AFAKE 2 + int type; + union { +#define ATOGGLE 3 +#define AACTIVATE 4 +#define ADEACTIVATE 5 +#define AIGNORE 6 + int special; + struct key send_key; + }; +}; + +void do_action(struct action, Window, int); + +struct match { +#define MCLASS 1 + int prop; + char *str; + struct match *next; +}; + +struct match *new_match(int, char*); +void recfree_match(struct match*); +int match_window(struct match*, Window); + +struct rule { + struct key key; + struct action action; + struct rule *next; +}; + +struct rule *new_rule(struct key, struct action); +void recfree_rule(struct rule*); +int rule_matched(struct rule*, struct key); + +struct group { + struct match *matches; + struct rule *rules; + struct group *next; +}; + +extern struct group *config; + +struct group *new_group(struct match*, struct rule*); +void recfree_group(struct group*); +void process_event(struct group*, XKeyEvent*); +int group_match(struct group*, Window); + +/* xlib-related */ +void grabkey(struct key); +KeySym keycode_to_keysym(unsigned int); +Window focused_window(); +void send_fake(Window, struct key, int); +int window_match_class(Window, const char*); + +/* debugging */ +void printkey(struct key); +void printaction(struct action); +void printmatch(struct match*); +void printrule(struct rule*); +void printgroup(struct group*); + +#endif /* STAR_PLATINUM_H */