+ if (winfocus)
+ focus_win(winfocus); /* has to be done outside of the loop */
+}
+
+void
+send_to_ws(struct swm_region *r, union arg *args)
+{
+ int wsid = args->id;
+ struct ws_win *win = cur_focus;
+ struct workspace *ws, *nws;
+ Atom ws_idx_atom = 0;
+ unsigned char ws_idx_str[SWM_PROPLEN];
+
+ if (win == NULL)
+ return;
+
+ DNPRINTF(SWM_D_MOVE, "send_to_ws: win: %lu\n", win->id);
+
+ ws = win->ws;
+ nws = &win->s->ws[wsid];
+
+ XUnmapWindow(display, win->id);
+
+ /* find a window to focus */
+ ws->focus = TAILQ_PREV(win, ws_win_list, entry);
+ if (ws->focus == NULL)
+ ws->focus = TAILQ_FIRST(&ws->winlist);
+ if (ws->focus == win)
+ ws->focus = NULL;
+
+ TAILQ_REMOVE(&ws->winlist, win, entry);
+
+ TAILQ_INSERT_TAIL(&nws->winlist, win, entry);
+ win->ws = nws;
+
+ /* Try to update the window's workspace property */
+ ws_idx_atom = XInternAtom(display, "_SWM_WS", False);
+ if (ws_idx_atom &&
+ snprintf(ws_idx_str, SWM_PROPLEN, "%d", nws->idx) < SWM_PROPLEN) {
+ DNPRINTF(SWM_D_PROP, "setting property _SWM_WS to %s\n",
+ ws_idx_str);
+ XChangeProperty(display, win->id, ws_idx_atom, XA_STRING, 8,
+ PropModeReplace, ws_idx_str, SWM_PROPLEN);
+ }
+
+ if (count_win(nws, 1) == 1)
+ nws->focus = win;
+ ws->restack = 1;
+ nws->restack = 1;
+
+ stack();
+}
+
+void
+wkill(struct swm_region *r, union arg *args)
+{
+ DNPRINTF(SWM_D_MISC, "wkill %d\n", args->id);
+
+ if(r->ws->focus == NULL)
+ return;
+
+ if (args->id == SWM_ARG_ID_KILLWINDOW)
+ XKillClient(display, r->ws->focus->id);
+ else
+ if (r->ws->focus->can_delete)
+ client_msg(r->ws->focus, adelete);
+}
+
+void
+screenshot(struct swm_region *r, union arg *args)
+{
+ union arg a;
+
+ DNPRINTF(SWM_D_MISC, "screenshot\n");
+
+ if (ss_enabled == 0)
+ return;
+
+ switch (args->id) {
+ case SWM_ARG_ID_SS_ALL:
+ spawn_screenshot[1] = "full";
+ break;
+ case SWM_ARG_ID_SS_WINDOW:
+ spawn_screenshot[1] = "window";
+ break;
+ default:
+ return;
+ }
+ a.argv = spawn_screenshot;
+ spawn(r, &a);
+}
+
+void
+floating_toggle(struct swm_region *r, union arg *args)
+{
+ struct ws_win *win = cur_focus;
+
+ if (win == NULL)
+ return;
+
+ win->floating = !win->floating;
+ win->manual = 0;
+ stack();
+ focus_win(win);
+}
+
+void
+resize_window(struct ws_win *win, int center)
+{
+ unsigned int mask;
+ XWindowChanges wc;
+ struct swm_region *r;
+
+ r = root_to_region(win->wa.root);
+ bzero(&wc, sizeof wc);
+ mask = CWBorderWidth | CWWidth | CWHeight;
+ wc.border_width = 1;
+ wc.width = win->g.w;
+ wc.height = win->g.h;
+ if (center == SWM_ARG_ID_CENTER) {
+ wc.x = (WIDTH(r) - win->g.w) / 2;
+ wc.y = (HEIGHT(r) - win->g.h) / 2;
+ mask |= CWX | CWY;
+ }
+
+ DNPRINTF(SWM_D_STACK, "resize_window: win %lu x %d y %d w %d h %d\n",
+ win->id, wc.x, wc.y, wc.width, wc.height);
+
+ XConfigureWindow(display, win->id, mask, &wc);
+ config_win(win);
+}
+
+void
+resize(struct ws_win *win, union arg *args)
+{
+ XEvent ev;
+ Time time = 0;
+
+ DNPRINTF(SWM_D_MOUSE, "resize: win %lu floating %d trans %d\n",
+ win->id, win->floating, win->transient);
+
+ if (!(win->transient != 0 || win->floating != 0))
+ return;
+
+ if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync,
+ GrabModeAsync, None, None /* cursor */, CurrentTime) != GrabSuccess)
+ return;
+ XWarpPointer(display, None, win->id, 0, 0, 0, 0, win->g.w, win->g.h);
+ do {
+ XMaskEvent(display, MOUSEMASK | ExposureMask |
+ SubstructureRedirectMask, &ev);
+ switch(ev.type) {
+ case ConfigureRequest:
+ case Expose:
+ case MapRequest:
+ handler[ev.type](&ev);
+ break;
+ case MotionNotify:
+ if (ev.xmotion.x <= 1)
+ ev.xmotion.x = 1;
+ if (ev.xmotion.y <= 1)
+ ev.xmotion.y = 1;
+ win->g.w = ev.xmotion.x;
+ win->g.h = ev.xmotion.y;
+
+ /* not free, don't sync more than 60 times / second */
+ if ((ev.xmotion.time - time) > (1000 / 60) ) {
+ time = ev.xmotion.time;
+ XSync(display, False);
+ resize_window(win, args->id);
+ }
+ break;
+ }
+ } while (ev.type != ButtonRelease);
+ if (time) {
+ XSync(display, False);
+ resize_window(win, args->id);
+ }
+ XWarpPointer(display, None, win->id, 0, 0, 0, 0, win->g.w - 1,
+ win->g.h - 1);
+ XUngrabPointer(display, CurrentTime);
+
+ /* drain events */
+ while (XCheckMaskEvent(display, EnterWindowMask, &ev));
+}
+
+void
+move_window(struct ws_win *win)
+{
+ unsigned int mask;
+ XWindowChanges wc;
+ struct swm_region *r;
+
+ r = root_to_region(win->wa.root);
+ bzero(&wc, sizeof wc);
+ mask = CWX | CWY;
+ wc.x = win->g.x;
+ wc.y = win->g.y;
+
+ DNPRINTF(SWM_D_STACK, "move_window: win %lu x %d y %d w %d h %d\n",
+ win->id, wc.x, wc.y, wc.width, wc.height);
+
+ XConfigureWindow(display, win->id, mask, &wc);
+ config_win(win);
+}
+
+void
+move(struct ws_win *win, union arg *args)
+{
+ XEvent ev;
+ Time time = 0;
+ int restack = 0;
+
+ DNPRINTF(SWM_D_MOUSE, "move: win %lu floating %d trans %d\n",
+ win->id, win->floating, win->transient);
+
+ if (win->floating == 0) {
+ win->floating = 1;
+ win->manual = 1;
+ restack = 1;
+ }
+
+ if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync,
+ GrabModeAsync, None, None /* cursor */, CurrentTime) != GrabSuccess)
+ return;
+ XWarpPointer(display, None, win->id, 0, 0, 0, 0, 0, 0);
+ do {
+ XMaskEvent(display, MOUSEMASK | ExposureMask |
+ SubstructureRedirectMask, &ev);
+ switch(ev.type) {
+ case ConfigureRequest:
+ case Expose:
+ case MapRequest:
+ handler[ev.type](&ev);
+ break;
+ case MotionNotify:
+ win->g.x = ev.xmotion.x_root;
+ win->g.y = ev.xmotion.y_root;
+
+ /* not free, don't sync more than 60 times / second */
+ if ((ev.xmotion.time - time) > (1000 / 60) ) {
+ time = ev.xmotion.time;
+ XSync(display, False);
+ move_window(win);
+ }
+ break;
+ }
+ } while (ev.type != ButtonRelease);
+ if (time) {
+ XSync(display, False);
+ move_window(win);
+ }
+ XWarpPointer(display, None, win->id, 0, 0, 0, 0, 0, 0);
+ XUngrabPointer(display, CurrentTime);
+ if (restack)
+ stack();
+
+ /* drain events */
+ while (XCheckMaskEvent(display, EnterWindowMask, &ev));
+}
+
+/* key definitions */
+struct keyfunc {
+ char name[SWM_FUNCNAME_LEN];
+ void (*func)(struct swm_region *r, union arg *);
+ union arg args;
+} keyfuncs[kf_invalid] = {
+ /* name function argument */
+ { "cycle_layout", cycle_layout, {0} },
+ { "stack_reset", stack_config, {.id = SWM_ARG_ID_STACKRESET} },
+ { "master_shrink", stack_config, {.id = SWM_ARG_ID_MASTERSHRINK} },
+ { "master_grow", stack_config, {.id = SWM_ARG_ID_MASTERGROW} },
+ { "master_add", stack_config, {.id = SWM_ARG_ID_MASTERADD} },
+ { "master_del", stack_config, {.id = SWM_ARG_ID_MASTERDEL} },
+ { "stack_inc", stack_config, {.id = SWM_ARG_ID_STACKINC} },
+ { "stack_dec", stack_config, {.id = SWM_ARG_ID_STACKDEC} },
+ { "swap_main", swapwin, {.id = SWM_ARG_ID_SWAPMAIN} },
+ { "focus_next", focus, {.id = SWM_ARG_ID_FOCUSNEXT} },
+ { "focus_prev", focus, {.id = SWM_ARG_ID_FOCUSPREV} },
+ { "swap_next", swapwin, {.id = SWM_ARG_ID_SWAPNEXT} },
+ { "swap_prev", swapwin, {.id = SWM_ARG_ID_SWAPPREV} },
+ { "spawn_term", spawnterm, {.argv = spawn_term} },
+ { "spawn_menu", spawnmenu, {.argv = spawn_menu} },
+ { "quit", quit, {0} },
+ { "restart", restart, {0} },
+ { "focus_main", focus, {.id = SWM_ARG_ID_FOCUSMAIN} },
+ { "ws_1", switchws, {.id = 0} },
+ { "ws_2", switchws, {.id = 1} },
+ { "ws_3", switchws, {.id = 2} },
+ { "ws_4", switchws, {.id = 3} },
+ { "ws_5", switchws, {.id = 4} },
+ { "ws_6", switchws, {.id = 5} },
+ { "ws_7", switchws, {.id = 6} },
+ { "ws_8", switchws, {.id = 7} },
+ { "ws_9", switchws, {.id = 8} },
+ { "ws_10", switchws, {.id = 9} },
+ { "ws_next", cyclews, {.id = SWM_ARG_ID_CYCLEWS_UP} },
+ { "ws_prev", cyclews, {.id = SWM_ARG_ID_CYCLEWS_DOWN} },
+ { "screen_next", cyclescr, {.id = SWM_ARG_ID_CYCLESC_UP} },
+ { "screen_prev", cyclescr, {.id = SWM_ARG_ID_CYCLESC_DOWN} },
+ { "mvws_1", send_to_ws, {.id = 0} },
+ { "mvws_2", send_to_ws, {.id = 1} },
+ { "mvws_3", send_to_ws, {.id = 2} },
+ { "mvws_4", send_to_ws, {.id = 3} },
+ { "mvws_5", send_to_ws, {.id = 4} },
+ { "mvws_6", send_to_ws, {.id = 5} },
+ { "mvws_7", send_to_ws, {.id = 6} },
+ { "mvws_8", send_to_ws, {.id = 7} },
+ { "mvws_9", send_to_ws, {.id = 8} },
+ { "mvws_10", send_to_ws, {.id = 9} },
+ { "bar_toggle", bar_toggle, {0} },
+ { "wind_kill", wkill, {.id = SWM_ARG_ID_KILLWINDOW} },
+ { "wind_del", wkill, {.id = SWM_ARG_ID_DELETEWINDOW} },
+ { "screenshot_all", screenshot, {.id = SWM_ARG_ID_SS_ALL} },
+ { "screenshot_wind", screenshot, {.id = SWM_ARG_ID_SS_WINDOW} },
+ { "float_toggle", floating_toggle,{0} },
+ { "version", version, {0} },
+ { "spawn_lock", spawn, {.argv = spawn_lock} },
+ { "spawn_initscr", spawn, {.argv = spawn_initscr} },
+};
+struct key {
+ unsigned int mod;
+ KeySym keysym;
+ enum keyfuncid funcid;
+};
+int keys_size = 0, keys_length = 0;
+struct key *keys = NULL;
+
+/* mouse */
+enum { client_click, root_click };
+struct button {
+ unsigned int action;
+ unsigned int mask;
+ unsigned int button;
+ void (*func)(struct ws_win *, union arg *);
+ union arg args;
+} buttons[] = {
+ /* action key mouse button func args */
+ { client_click, MODKEY, Button3, resize, {.id = SWM_ARG_ID_DONTCENTER} },
+ { client_click, MODKEY | ShiftMask, Button3, resize, {.id = SWM_ARG_ID_CENTER} },
+ { client_click, MODKEY, Button1, move, {0} },
+};
+
+void
+update_modkey(unsigned int mod)
+{
+ int i;
+
+ for (i = 0; i < keys_length; i++)
+ if (keys[i].mod & ShiftMask)
+ keys[i].mod = mod | ShiftMask;
+ else
+ keys[i].mod = mod;
+
+ for (i = 0; i < LENGTH(buttons); i++)
+ if (buttons[i].mask & ShiftMask)
+ buttons[i].mask = mod | ShiftMask;
+ else
+ buttons[i].mask = mod;
+}
+
+#define SWM_MODNAME_SIZE 32
+#define SWM_KEY_WS "\n+ \t"
+int
+parsekeys(char *keystr, unsigned int currmod, unsigned int *mod, KeySym *ks)
+{
+ char *cp, *name;
+ KeySym uks;
+ if (mod == NULL || ks == NULL)
+ return (0);
+ cp = keystr;
+ *mod = 0;
+ while ((name = strsep(&cp, SWM_KEY_WS)) != NULL) {
+ if (cp)
+ cp += (long)strspn(cp, SWM_KEY_WS);
+ if (strncasecmp(name, "MOD", SWM_MODNAME_SIZE) == 0)
+ *mod |= currmod;
+ else if (!strncasecmp(name, "Mod1", SWM_MODNAME_SIZE))
+ *mod |= Mod1Mask;
+ else if (!strncasecmp(name, "Mod2", SWM_MODNAME_SIZE))
+ *mod += Mod2Mask;
+ else if (!strncmp(name, "Mod3", SWM_MODNAME_SIZE))
+ *mod |= Mod3Mask;
+ else if (!strncmp(name, "Mod4", SWM_MODNAME_SIZE))
+ *mod |= Mod4Mask;
+ else if (strncasecmp(name, "SHIFT", SWM_MODNAME_SIZE) == 0)
+ *mod |= ShiftMask;
+ else {
+ *ks = XStringToKeysym(name);
+ XConvertCase(*ks, ks, &uks);
+ if (ks == NoSymbol) {
+ DNPRINTF(SWM_D_KEY,
+ "parsekeys: invalid key %s\n",
+ name);
+ return (0);
+ }
+ }
+ }
+ return (1);
+}
+int
+bindmatch(const char *var, const char *name, unsigned int currmod, char *keystr,
+ enum keyfuncid *kfid, unsigned int *mod, KeySym *ks)
+{
+ char *p;
+ int i;
+ char funcname[SWM_FUNCNAME_LEN];
+ i = strncmp(var, name, 255);
+ if (kfid == NULL || mod == NULL || ks == NULL)
+ return (i);
+ *kfid = kf_invalid;
+ *mod = 0;
+ *ks = NoSymbol;
+ bzero(funcname, LENGTH(funcname));
+ if (i <= 0)
+ return (i);
+ p = (char *)var + strlen(name);
+ if (*p++ != '[')
+ return (i);
+ i = 0;
+ while (isgraph(*p) && *p != ']' && i < LENGTH(funcname))
+ funcname[i++] = *p++;
+ if (i >= LENGTH(funcname) || *p != ']')
+ return (1);
+ if (i == 0)
+ return (!parsekeys(keystr, currmod, mod, ks));
+ for (*kfid = 0; *kfid < kf_invalid; (*kfid)++) {
+ if (strncasecmp(funcname, keyfuncs[*kfid].name,
+ SWM_FUNCNAME_LEN) == 0) {
+ return (!parsekeys(keystr, currmod, mod, ks));
+ }
+
+ }
+ return (1);