/* TMUXWM.C * Copyright (c) 2015-2018 Jesse McClure * * 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 #include #include #include #include #include static struct { KeySym sym; char **args; } bind[] = { /* No keysym = start with WM. First is Mod+Shift+Enter bound */ { 0, (char *[]) { "st", "-e", "tmux", "new-session", "-AD", "-s", "TmuxWM", NULL } }, { 0, (char *[]) { "setxkbmap", "-option", "caps:escape", NULL } }, { XF86XK_AudioRaiseVolume, (char *[]) { "amixer", "set", "Master", "2+", NULL} }, { XF86XK_AudioLowerVolume, (char *[]) { "amixer", "set", "Master", "2-", NULL} }, { XF86XK_AudioMute, (char *[]) { "amixer", "set", "Master", "toggle", NULL} }, { XF86XK_ScreenSaver, (char *[]) { "slock", NULL} }, }; static void configurerequest(XEvent *); static void enternotify(XEvent *); static void keypress(XEvent *); static void maprequest(XEvent *); static void unmapnotify(XEvent *); static Display *dpy; static Window root; static int sw, sh; static void (*handler[LASTEvent])(XEvent *) = { [ConfigureRequest] = configurerequest, [EnterNotify] = enternotify, [KeyPress] = keypress, [MapRequest] = maprequest, }; static int spawn(char *const *args) { if (fork() != 0) return 1; close(ConnectionNumber(dpy)); setsid(); if (fork() != 0) exit(0); return execvp(args[0], args); } static int init() { if(!(dpy = XOpenDisplay(0x0))) return 1; signal(SIGCHLD, SIG_IGN); root = DefaultRootWindow(dpy); XSetWindowBackground(dpy, root, 0x060812); XDefineCursor(dpy, root, XCreateFontCursor(dpy, 68)); sw = DisplayWidth(dpy, DefaultScreen(dpy)); sh = DisplayHeight(dpy, DefaultScreen(dpy)); XGrabKey(dpy, XKeysymToKeycode(dpy, XK_Tab), Mod1Mask, root, True, GrabModeAsync, GrabModeAsync); XGrabKey(dpy, XKeysymToKeycode(dpy, XK_Return), Mod4Mask | ShiftMask, root, True, GrabModeAsync, GrabModeAsync); XGrabKey(dpy, AnyKey, Mod4Mask, root, True, GrabModeAsync, GrabModeAsync); XSelectInput(dpy, root, SubstructureNotifyMask | SubstructureRedirectMask); int i; for (i = 0; i < sizeof(bind) / sizeof(bind[0]); i++) { if (bind[i].sym == 0) spawn(bind[i].args); else XGrabKey(dpy, XKeysymToKeycode(dpy, bind[i].sym), 0, root, True, GrabModeAsync, GrabModeAsync); } return 0; } int main() { if (init() != 0) return 1; XEvent ev; while (!XNextEvent(dpy,&ev)) if (handler[ev.type]) handler[ev.type](&ev); return 0; } void configurerequest(XEvent *ev) { XConfigureRequestEvent *e = &ev->xconfigurerequest; int bpx = (e->x == 0 && e->y == 0 && e->width == sw && e->height == sh ? 0 : 2); XWindowChanges wc = { e->x, e->y, e->width, e->height, bpx, e->detail, Above }; XConfigureWindow(dpy, e->window, e->value_mask | CWBorderWidth, &wc); } void enternotify(XEvent *ev) { XSetInputFocus(dpy, ev->xcrossing.window, RevertToPointerRoot, CurrentTime); } void keypress(XEvent *ev) { XKeyEvent *e = &ev->xkey; KeySym sym = XkbKeycodeToKeysym(dpy, e->keycode, 0, 0); int i; if (e->state == Mod1Mask && sym == XK_Tab) XCirculateSubwindowsUp(dpy, root); else if (e->state == (Mod4Mask|ShiftMask) && sym == XK_Return) spawn(bind[0].args); else if (e->state == 0) { for (i = 0; i < sizeof(bind) / sizeof(bind[0]); i++) if (bind[i].sym == sym) spawn(bind[i].args); } else if (e->state == Mod4Mask) { XEvent xev; xev.type = KeyPress; xev.xkey.window = e->subwindow; xev.xkey.time = CurrentTime; xev.xkey.state = ControlMask; xev.xkey.keycode = XKeysymToKeycode(dpy, XK_space); XSendEvent(dpy, e->subwindow, True, KeyPressMask, &xev); xev.xkey.state = 0; xev.xkey.keycode = e->keycode; XSendEvent(dpy, e->subwindow, True, KeyPressMask, &xev); XFlush(dpy); } } void maprequest(XEvent *ev) { XMapRequestEvent *e = &ev->xmaprequest; XWindowAttributes wa; if (!XGetWindowAttributes(dpy, e->window, &wa) || wa.override_redirect) return; XWMHints *wmHint = XGetWMHints(dpy,e->window); if (!(wmHint && wmHint->flags & InputHint && !wmHint->input)) XSelectInput(dpy, e->window, EnterWindowMask); if (wmHint) XFree(wmHint); Window parent; if (XGetTransientForHint(dpy, e->window, &parent)) XMoveResizeWindow(dpy, e->window, wa.x, wa.y, wa.width, wa.height); else XMoveResizeWindow(dpy, e->window, 0, 0, sw, sh); XSetWindowBorder(dpy, e->window, 0x000000); XMapWindow(dpy, e->window); }