| tmuxwm.c - tmuxwm - A window manager utilising only tmux. | |
| git clone git://jay.scot/tmuxwm | |
| Log | |
| Files | |
| Refs | |
| README | |
| LICENSE | |
| --- | |
| tmuxwm.c (6524B) | |
| --- | |
| 1 /* TMUXWM.C | |
| 2 * Copyright Jesse McClure 2015 | |
| 3 * License: GPLv3 | |
| 4 * Compile: gcc -o tmuxwm tmuxwm.c -lX11 | |
| 5 */ | |
| 6 | |
| 7 #include <stdlib.h> | |
| 8 #include <stdio.h> | |
| 9 #include <signal.h> | |
| 10 #include <unistd.h> | |
| 11 #include <string.h> | |
| 12 #include <X11/Xlib.h> | |
| 13 #include <X11/XKBlib.h> | |
| 14 #include <X11/keysym.h> | |
| 15 #include <X11/Xutil.h> | |
| 16 #include <X11/XF86keysym.h> | |
| 17 | |
| 18 typedef struct Bind { KeySym sym; int mod; const char *const *args; } … | |
| 19 typedef const char *const Arg[]; | |
| 20 | |
| 21 Arg tmux_client = (Arg) {"st", "-e", "tmux", "new-session", NULL }; | |
| 22 | |
| 23 static Bind bind[] = | |
| 24 { | |
| 25 { XK_Return, Mod4Mask, (Arg) {"tmux", "r… | |
| 26 { XK_Tab, Mod4Mask, (Arg) {"tmux", "l… | |
| 27 { XK_space, Mod4Mask, (Arg) {"tmux", "n… | |
| 28 { XK_1, Mod4Mask, (Arg) {"tmux", … | |
| 29 { XK_2, Mod4Mask, (Arg) {"tmux", … | |
| 30 { XK_3, Mod4Mask, (Arg) {"tmux", … | |
| 31 { XK_4, Mod4Mask, (Arg) {"tmux", … | |
| 32 { XK_5, Mod4Mask, (Arg) {"tmux", … | |
| 33 { XK_6, Mod4Mask, (Arg) {"tmux", … | |
| 34 { XK_7, Mod4Mask, (Arg) {"tmux", … | |
| 35 { XK_8, Mod4Mask, (Arg) {"tmux", … | |
| 36 { XK_9, Mod4Mask, (Arg) {"tmux", … | |
| 37 { XK_h, Mod4Mask, (Arg) {"tmux", … | |
| 38 { XK_v, Mod4Mask, (Arg) {"tmux", … | |
| 39 { XK_f, Mod4Mask, (Arg) {"firefox", … | |
| 40 { XK_p, Mod4Mask, (Arg) {"rofi", … | |
| 41 { XK_z, Mod4Mask, (Arg) {"/home/jay/.bin/battery", … | |
| 42 { XK_x, Mod4Mask, (Arg) {"/home/jay/.bin/all-time", … | |
| 43 }; | |
| 44 | |
| 45 | |
| 46 static void buttonpress(XEvent *); | |
| 47 static void configurerequest(XEvent *); | |
| 48 static void enternotify(XEvent *); | |
| 49 static void keypress(XEvent *); | |
| 50 static void maprequest(XEvent *); | |
| 51 static void unmapnotify(XEvent *); | |
| 52 | |
| 53 static Display *dpy; | |
| 54 static Window root; | |
| 55 static int sw, sh; | |
| 56 static void (*handler[LASTEvent])(XEvent *) = | |
| 57 { | |
| 58 [ButtonPress] = buttonpress, | |
| 59 [ConfigureRequest] = configurerequest, | |
| 60 [EnterNotify] = enternotify, | |
| 61 [KeyPress] = keypress, | |
| 62 [MapRequest] = maprequest, | |
| 63 }; | |
| 64 | |
| 65 static int xerror(Display *d, XErrorEvent *e) | |
| 66 { | |
| 67 char msg[256]; | |
| 68 | |
| 69 XGetErrorText(d, e->error_code, msg, sizeof(msg)); | |
| 70 fprintf(stderr, "[X11 %d:%d] %s\n", e->request_code, e->error_co… | |
| 71 } | |
| 72 | |
| 73 static inline int spawn(Arg args) | |
| 74 { | |
| 75 if (fork() != 0) | |
| 76 return 1; | |
| 77 close(ConnectionNumber(dpy)); | |
| 78 setsid(); | |
| 79 execvp(args[0], args); | |
| 80 } | |
| 81 | |
| 82 int main(int argc, const char **argv) | |
| 83 { | |
| 84 signal(SIGCHLD, SIG_IGN); | |
| 85 if (!(dpy = XOpenDisplay(0x0))) | |
| 86 return 1; | |
| 87 XSetErrorHandler(xerror); | |
| 88 root = DefaultRootWindow(dpy); | |
| 89 sw = DisplayWidth(dpy, DefaultScreen(dpy)); | |
| 90 sh = DisplayHeight(dpy, DefaultScreen(dpy)); | |
| 91 XGrabKey(dpy, XKeysymToKeycode(dpy, XK_Tab), Mod1Mask, root, Tru… | |
| 92 XGrabKey(dpy, XKeysymToKeycode(dpy, XK_Return), Mod4Mask | Shift… | |
| 93 int i; | |
| 94 for (i = 0; i < sizeof(bind) / sizeof(bind[0]); ++i) | |
| 95 XGrabKey(dpy, XKeysymToKeycode(dpy, bind[i].sym), bind[i… | |
| 96 XGrabButton(dpy, AnyButton, Mod4Mask, root, True, ButtonPressMas… | |
| 97 XSelectInput(dpy, root, SubstructureNotifyMask | SubstructureRed… | |
| 98 XEvent ev; | |
| 99 spawn(tmux_client); | |
| 100 while (!XNextEvent(dpy, &ev)) | |
| 101 if (handler[ev.type]) | |
| 102 handler[ev.type](&ev); | |
| 103 return 0; | |
| 104 } | |
| 105 | |
| 106 void buttonpress(XEvent *ev) | |
| 107 { | |
| 108 XButtonEvent *e = &ev->xbutton; | |
| 109 | |
| 110 XRaiseWindow(dpy, e->subwindow); | |
| 111 if (e->button == 2) | |
| 112 XMoveResizeWindow(dpy, e->subwindow, 0, 0, sw, sh); | |
| 113 if (e->button != 1 && e->button != 3) | |
| 114 return; | |
| 115 XGrabPointer(dpy, root, True, PointerMotionMask | ButtonReleaseM… | |
| 116 GrabModeAsync, GrabModeAsync, None, None, CurrentTi… | |
| 117 XWindowAttributes attr; | |
| 118 XGetWindowAttributes(dpy, e->subwindow, &attr); | |
| 119 int xx = e->x_root, yy = e->y_root, px, py; | |
| 120 XEvent ee; | |
| 121 while (!XMaskEvent(dpy, PointerMotionMask | ButtonReleaseMask, &… | |
| 122 if (ee.type == ButtonRelease) | |
| 123 break; | |
| 124 px = xx; py = yy; xx = ee.xbutton.x_root; yy = ee.xbutto… | |
| 125 if (e->button == 1) { | |
| 126 attr.x += xx - px; attr.y += yy - py; | |
| 127 } else if (e->button == 3) { | |
| 128 attr.width += xx - px; attr.height += yy - py; | |
| 129 if (attr.width < 12) | |
| 130 attr.width = 12; | |
| 131 else if (attr.height < 12) | |
| 132 attr.height = 12; | |
| 133 } | |
| 134 XMoveResizeWindow(dpy, e->subwindow, attr.x, attr.y, att… | |
| 135 } | |
| 136 XUngrabPointer(dpy, CurrentTime); | |
| 137 } | |
| 138 | |
| 139 void configurerequest(XEvent *ev) | |
| 140 { | |
| 141 XConfigureRequestEvent *e = &ev->xconfigurerequest; | |
| 142 XWindowChanges wc; | |
| 143 | |
| 144 wc.x = e->x; wc.y = e->y; wc.width = e->width; wc.height = e->he… | |
| 145 wc.sibling = Above; wc.stack_mode = e->detail; wc.border_width =… | |
| 146 XConfigureWindow(dpy, e->window, e->value_mask, &wc); | |
| 147 } | |
| 148 | |
| 149 void enternotify(XEvent *ev) | |
| 150 { | |
| 151 XSetInputFocus(dpy, ev->xcrossing.window, RevertToPointerRoot, C… | |
| 152 } | |
| 153 | |
| 154 void keypress(XEvent *ev) | |
| 155 { | |
| 156 XKeyEvent *e = &ev->xkey; | |
| 157 KeySym sym = XkbKeycodeToKeysym(dpy, e->keycode, 0, 0); | |
| 158 int i; | |
| 159 | |
| 160 if (e->state == Mod1Mask && sym == XK_Tab) { | |
| 161 XCirculateSubwindowsUp(dpy, root); | |
| 162 } else if (e->state == (Mod4Mask | ShiftMask) && sym == XK_Retur… | |
| 163 spawn(tmux_client); | |
| 164 } else { | |
| 165 for (i = 0; i < sizeof(bind) / sizeof(bind[0]); ++i) | |
| 166 if (bind[i].sym == sym && bind[i].mod == e->stat… | |
| 167 spawn(bind[i].args); | |
| 168 } | |
| 169 } | |
| 170 | |
| 171 #define MapFocus 0x01 | |
| 172 #define MapFull 0x02 | |
| 173 | |
| 174 void maprequest(XEvent *ev) | |
| 175 { | |
| 176 XMapRequestEvent *e = &ev->xmaprequest; | |
| 177 XWindowAttributes wa; | |
| 178 | |
| 179 if (!XGetWindowAttributes(dpy, e->window, &wa) || wa.override_re… | |
| 180 return; | |
| 181 int flags = MapFocus | MapFull; | |
| 182 XWMHints *wmHint; | |
| 183 if ((wmHint = XGetWMHints(dpy, e->window))) { | |
| 184 if (wmHint->flags & InputHint && !wmHint->input) | |
| 185 flags &= ~MapFocus; | |
| 186 XFree(wmHint); | |
| 187 } | |
| 188 XClassHint csHint; | |
| 189 XGetClassHint(dpy, e->window, &csHint); | |
| 190 if (strncmp(csHint.res_name, "Float", 5) == 0) | |
| 191 flags &= ~MapFull; | |
| 192 if (strncmp(csHint.res_class, "Float", 5) == 0) | |
| 193 flags &= ~MapFull; | |
| 194 Window parent; | |
| 195 if (XGetTransientForHint(dpy, e->window, &parent)) | |
| 196 flags &= ~MapFull; | |
| 197 if (flags & MapFocus) | |
| 198 XSelectInput(dpy, e->window, EnterWindowMask); | |
| 199 if (flags & MapFull) | |
| 200 XMoveResizeWindow(dpy, e->window, 0, 0, sw, sh); | |
| 201 else | |
| 202 XMoveResizeWindow(dpy, e->window, wa.x, wa.y, wa.width, … | |
| 203 XMapWindow(dpy, e->window); | |
| 204 } |