+ if (validate_ws(win->ws))
+ return; /* XXX this gets hit with thunderbird, needs fixing */
+
+ if (validate_win(win)) {
+ kill_refs(win);
+ return;
+ }
+
+ if (validate_win(win)) {
+ kill_refs(win);
+ return;
+ }
+
+ XGetInputFocus(display, &cur_focus, &rr);
+ if ((cfw = find_window(cur_focus)) != NULL)
+ unfocus_win(cfw);
+ else {
+ /* use larger hammer since the window was killed somehow */
+ TAILQ_FOREACH(cfw, &win->ws->winlist, entry)
+ if (cfw->ws && cfw->ws->r && cfw->ws->r->s)
+ XSetWindowBorder(display, cfw->id,
+ cfw->ws->r->s->c[SWM_S_COLOR_UNFOCUS].color);
+ }
+
+ win->ws->focus = win;
+
+ if (win->ws->r != NULL) {
+ /* drain all previous focus events */
+ while (XCheckTypedEvent(display, FocusIn, &cne) == True)
+ ;
+
+ if (win->java == 0)
+ XSetInputFocus(display, win->id,
+ RevertToParent, CurrentTime);
+ grabbuttons(win, 1);
+ XSetWindowBorder(display, win->id,
+ win->ws->r->s->c[SWM_S_COLOR_FOCUS].color);
+ if (win->ws->cur_layout->flags & SWM_L_MAPONFOCUS ||
+ win->ws->always_raise)
+ XMapRaised(display, win->id);
+
+ XChangeProperty(display, win->s->root,
+ ewmh[_NET_ACTIVE_WINDOW].atom, XA_WINDOW, 32,
+ PropModeReplace, (unsigned char *)&win->id,1);
+ }
+
+ bar_check_opts();
+}
+
+void
+switchws(struct swm_region *r, union arg *args)
+{
+ int wsid = args->id, unmap_old = 0;
+ struct swm_region *this_r, *other_r;
+ struct ws_win *win;
+ struct workspace *new_ws, *old_ws;
+ union arg a;
+
+ if (!(r && r->s))
+ return;
+
+ this_r = r;
+ old_ws = this_r->ws;
+ new_ws = &this_r->s->ws[wsid];
+
+ DNPRINTF(SWM_D_WS, "switchws: screen[%d]:%dx%d+%d+%d: %d -> %d\n",
+ r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), old_ws->idx, wsid);
+
+ if (new_ws == NULL || old_ws == NULL)
+ return;
+ if (new_ws == old_ws)
+ return;
+
+ other_r = new_ws->r;
+ if (other_r == NULL) {
+ /* the other workspace is hidden, hide this one */
+ old_ws->r = NULL;
+ unmap_old = 1;
+ } else {
+ /* the other ws is visible in another region, exchange them */
+ other_r->ws_prior = new_ws;
+ other_r->ws = old_ws;
+ old_ws->r = other_r;
+ }
+ this_r->ws_prior = old_ws;
+ this_r->ws = new_ws;
+ new_ws->r = this_r;
+
+ /* this is needed so that we can click on a window after a restart */
+ unfocus_all();
+
+ stack();
+ a.id = SWM_ARG_ID_FOCUSCUR;
+ focus(new_ws->r, &a);
+
+ bar_update();
+
+ /* unmap old windows */
+ if (unmap_old)
+ TAILQ_FOREACH(win, &old_ws->winlist, entry)
+ unmap_window(win);
+
+ if (focus_mode == SWM_FOCUS_DEFAULT)
+ drain_enter_notify();
+}
+
+void
+cyclews(struct swm_region *r, union arg *args)
+{
+ union arg a;
+ struct swm_screen *s = r->s;
+ int cycle_all = 0;
+
+ DNPRINTF(SWM_D_WS, "cyclews: id: %d, screen[%d]:%dx%d+%d+%d, ws: %d\n",
+ args->id, r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
+
+ a.id = r->ws->idx;
+ do {
+ switch (args->id) {
+ case SWM_ARG_ID_CYCLEWS_UP_ALL:
+ cycle_all = 1;
+ /* FALLTHROUGH */
+ case SWM_ARG_ID_CYCLEWS_UP:
+ if (a.id < SWM_WS_MAX - 1)
+ a.id++;
+ else
+ a.id = 0;
+ break;
+ case SWM_ARG_ID_CYCLEWS_DOWN_ALL:
+ cycle_all = 1;
+ /* FALLTHROUGH */
+ case SWM_ARG_ID_CYCLEWS_DOWN:
+ if (a.id > 0)
+ a.id--;
+ else
+ a.id = SWM_WS_MAX - 1;
+ break;
+ default:
+ return;
+ };
+
+ if (!cycle_all &&
+ (cycle_empty == 0 && TAILQ_EMPTY(&s->ws[a.id].winlist)))
+ continue;
+ if (cycle_visible == 0 && s->ws[a.id].r != NULL)
+ continue;
+
+ switchws(r, &a);
+ } while (a.id != r->ws->idx);
+}
+
+void
+priorws(struct swm_region *r, union arg *args)
+{
+ union arg a;
+
+ DNPRINTF(SWM_D_WS, "priorws: id: %d, screen[%d]:%dx%d+%d+%d, ws: %d\n",
+ args->id, r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
+
+ if (r->ws_prior == NULL)
+ return;
+
+ a.id = r->ws_prior->idx;
+ switchws(r, &a);
+}
+
+void
+cyclescr(struct swm_region *r, union arg *args)
+{
+ struct swm_region *rr = NULL;
+ union arg a;
+ int i, x, y;
+
+ /* do nothing if we don't have more than one screen */
+ if (!(ScreenCount(display) > 1 || outputs > 1))
+ return;
+
+ i = r->s->idx;
+ switch (args->id) {
+ case SWM_ARG_ID_CYCLESC_UP:
+ rr = TAILQ_NEXT(r, entry);
+ if (rr == NULL)
+ rr = TAILQ_FIRST(&screens[i].rl);
+ break;
+ case SWM_ARG_ID_CYCLESC_DOWN:
+ rr = TAILQ_PREV(r, swm_region_list, entry);
+ if (rr == NULL)
+ rr = TAILQ_LAST(&screens[i].rl, swm_region_list);
+ break;
+ default:
+ return;
+ };
+ if (rr == NULL)
+ return;
+
+ /* move mouse to region */
+ x = X(rr) + 1;
+ y = Y(rr) + 1 + (bar_enabled ? bar_height : 0);
+ XWarpPointer(display, None, rr->s[i].root, 0, 0, 0, 0, x, y);
+
+ a.id = SWM_ARG_ID_FOCUSCUR;
+ focus(rr, &a);
+
+ if (rr->ws->focus) {
+ /* move to focus window */
+ x = X(rr->ws->focus) + 1;
+ y = Y(rr->ws->focus) + 1;
+ XWarpPointer(display, None, rr->s[i].root, 0, 0, 0, 0, x, y);
+ }
+}
+
+void
+sort_windows(struct ws_win_list *wl)
+{
+ struct ws_win *win, *parent, *nxt;
+
+ if (wl == NULL)
+ return;
+
+ for (win = TAILQ_FIRST(wl); win != TAILQ_END(wl); win = nxt) {
+ nxt = TAILQ_NEXT(win, entry);
+ if (win->transient) {
+ parent = find_window(win->transient);
+ if (parent == NULL) {
+ warnx("not possible bug");
+ continue;
+ }
+ TAILQ_REMOVE(wl, win, entry);
+ TAILQ_INSERT_AFTER(wl, parent, win, entry);
+ }
+ }
+
+}
+
+void
+swapwin(struct swm_region *r, union arg *args)
+{
+ struct ws_win *target, *source;
+ struct ws_win *cur_focus;
+ struct ws_win_list *wl;
+
+
+ DNPRINTF(SWM_D_WS, "swapwin: id: %d, screen[%d]:%dx%d+%d+%d, ws: %d\n",
+ args->id, r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
+
+ cur_focus = r->ws->focus;
+ if (cur_focus == NULL)
+ return;
+
+ source = cur_focus;
+ wl = &source->ws->winlist;
+
+ switch (args->id) {
+ case SWM_ARG_ID_SWAPPREV:
+ if (source->transient)
+ source = find_window(source->transient);
+ target = TAILQ_PREV(source, ws_win_list, entry);
+ if (target && target->transient)
+ target = find_window(target->transient);
+ TAILQ_REMOVE(wl, source, entry);
+ if (target == NULL)
+ TAILQ_INSERT_TAIL(wl, source, entry);
+ else
+ TAILQ_INSERT_BEFORE(target, source, entry);
+ break;
+ case SWM_ARG_ID_SWAPNEXT:
+ target = TAILQ_NEXT(source, entry);
+ /* move the parent and let the sort handle the move */
+ if (source->transient)
+ source = find_window(source->transient);
+ TAILQ_REMOVE(wl, source, entry);
+ if (target == NULL)
+ TAILQ_INSERT_HEAD(wl, source, entry);
+ else
+ TAILQ_INSERT_AFTER(wl, target, source, entry);
+ break;
+ case SWM_ARG_ID_SWAPMAIN:
+ target = TAILQ_FIRST(wl);
+ if (target == source) {
+ if (source->ws->focus_prev != NULL &&
+ source->ws->focus_prev != target)
+
+ source = source->ws->focus_prev;
+ else
+ return;
+ }
+ if (target == NULL || source == NULL)
+ return;
+ source->ws->focus_prev = target;
+ TAILQ_REMOVE(wl, target, entry);
+ TAILQ_INSERT_BEFORE(source, target, entry);
+ TAILQ_REMOVE(wl, source, entry);
+ TAILQ_INSERT_HEAD(wl, source, entry);
+ break;
+ case SWM_ARG_ID_MOVELAST:
+ TAILQ_REMOVE(wl, source, entry);
+ TAILQ_INSERT_TAIL(wl, source, entry);
+ break;
+ default:
+ DNPRINTF(SWM_D_MOVE, "swapwin: invalid id: %d\n", args->id);
+ return;
+ }
+
+ sort_windows(wl);
+
+ stack();
+}
+
+void
+focus_prev(struct ws_win *win)
+{
+ struct ws_win *winfocus = NULL;
+ struct ws_win *cur_focus = NULL;
+ struct ws_win_list *wl = NULL;
+ struct workspace *ws = NULL;
+
+ DNPRINTF(SWM_D_FOCUS, "focus_prev: window: 0x%lx\n", WINID(win));
+
+ if (!(win && win->ws))
+ return;
+
+ ws = win->ws;
+ wl = &ws->winlist;
+ cur_focus = ws->focus;
+
+ /* pickle, just focus on whatever */
+ if (cur_focus == NULL) {
+ /* use prev_focus if valid */
+ if (ws->focus_prev && ws->focus_prev != cur_focus &&
+ find_window(WINID(ws->focus_prev)))
+ winfocus = ws->focus_prev;
+ if (winfocus == NULL)
+ winfocus = TAILQ_FIRST(wl);
+ goto done;
+ }
+
+ /* if transient focus on parent */
+ if (cur_focus->transient) {
+ winfocus = find_window(cur_focus->transient);
+ goto done;
+ }
+
+ /* if in max_stack try harder */
+ if ((win->quirks & SWM_Q_FOCUSPREV) ||
+ (ws->cur_layout->flags & SWM_L_FOCUSPREV)) {
+ if (cur_focus != ws->focus_prev)
+ winfocus = ws->focus_prev;
+ else if (cur_focus != ws->focus)
+ winfocus = ws->focus;
+ else
+ winfocus = TAILQ_PREV(win, ws_win_list, entry);
+ if (winfocus)
+ goto done;
+ }
+
+ if (cur_focus == win)
+ winfocus = TAILQ_PREV(win, ws_win_list, entry);
+ if (winfocus == NULL)
+ winfocus = TAILQ_LAST(wl, ws_win_list);
+ if (winfocus == NULL || winfocus == win)
+ winfocus = TAILQ_NEXT(cur_focus, entry);
+
+done:
+ focus_magic(winfocus);
+}
+
+void
+focus(struct swm_region *r, union arg *args)
+{
+ struct ws_win *winfocus = NULL, *head;
+ struct ws_win *cur_focus = NULL;
+ struct ws_win_list *wl = NULL;
+ struct workspace *ws = NULL;
+ int all_iconics;
+
+ if (!(r && r->ws))
+ return;
+
+ DNPRINTF(SWM_D_FOCUS, "focus: id: %d\n", args->id);
+
+ /* treat FOCUS_CUR special */
+ if (args->id == SWM_ARG_ID_FOCUSCUR) {
+ if (r->ws->focus && r->ws->focus->iconic == 0)
+ winfocus = r->ws->focus;
+ else if (r->ws->focus_prev && r->ws->focus_prev->iconic == 0)
+ winfocus = r->ws->focus_prev;
+ else
+ TAILQ_FOREACH(winfocus, &r->ws->winlist, entry)
+ if (winfocus->iconic == 0)
+ break;
+
+ focus_magic(winfocus);
+ return;
+ }
+
+ if ((cur_focus = r->ws->focus) == NULL)
+ return;
+ ws = r->ws;
+ wl = &ws->winlist;
+ if (TAILQ_EMPTY(wl))
+ return;
+ /* make sure there is at least one uniconified window */
+ all_iconics = 1;
+ TAILQ_FOREACH(winfocus, wl, entry)
+ if (winfocus->iconic == 0) {
+ all_iconics = 0;
+ break;
+ }
+ if (all_iconics)
+ return;
+
+ switch (args->id) {
+ case SWM_ARG_ID_FOCUSPREV:
+ head = TAILQ_PREV(cur_focus, ws_win_list, entry);
+ if (head == NULL)
+ head = TAILQ_LAST(wl, ws_win_list);
+ winfocus = head;
+ if (WINID(winfocus) == cur_focus->transient) {
+ head = TAILQ_PREV(winfocus, ws_win_list, entry);
+ if (head == NULL)
+ head = TAILQ_LAST(wl, ws_win_list);
+ winfocus = head;
+ }
+
+ /* skip iconics */
+ if (winfocus && winfocus->iconic) {
+ while (winfocus != cur_focus) {
+ if (winfocus == NULL)
+ winfocus = TAILQ_LAST(wl, ws_win_list);
+ if (winfocus->iconic == 0)
+ break;
+ winfocus = TAILQ_PREV(winfocus, ws_win_list,
+ entry);
+ }
+ }
+ break;
+
+ case SWM_ARG_ID_FOCUSNEXT:
+ head = TAILQ_NEXT(cur_focus, entry);
+ if (head == NULL)
+ head = TAILQ_FIRST(wl);
+ winfocus = head;
+
+ /* skip iconics */
+ if (winfocus && winfocus->iconic) {
+ while (winfocus != cur_focus) {
+ if (winfocus == NULL)
+ winfocus = TAILQ_FIRST(wl);
+ if (winfocus->iconic == 0)
+ break;
+ winfocus = TAILQ_NEXT(winfocus, entry);
+ }
+ }
+ break;
+
+ case SWM_ARG_ID_FOCUSMAIN:
+ winfocus = TAILQ_FIRST(wl);
+ if (winfocus == cur_focus)
+ winfocus = cur_focus->ws->focus_prev;
+ break;
+
+ default:
+ return;
+ }
+
+ focus_magic(winfocus);
+}
+
+void
+cycle_layout(struct swm_region *r, union arg *args)
+{
+ struct workspace *ws = r->ws;
+ union arg a;
+
+ DNPRINTF(SWM_D_EVENT, "cycle_layout: workspace: %d\n", ws->idx);
+
+ ws->cur_layout++;
+ if (ws->cur_layout->l_stack == NULL)
+ ws->cur_layout = &layouts[0];
+
+ stack();
+ if (focus_mode == SWM_FOCUS_DEFAULT)
+ drain_enter_notify();
+ a.id = SWM_ARG_ID_FOCUSCUR;
+ focus(r, &a);
+ bar_update();
+}
+
+void
+stack_config(struct swm_region *r, union arg *args)
+{
+ struct workspace *ws = r->ws;
+
+ DNPRINTF(SWM_D_STACK, "stack_config: id: %d workspace: %d\n",
+ args->id, ws->idx);
+
+ if (ws->cur_layout->l_config != NULL)
+ ws->cur_layout->l_config(ws, args->id);
+
+ if (args->id != SWM_ARG_ID_STACKINIT)
+ stack();
+ bar_update();
+}
+
+void
+stack(void) {
+ struct swm_geometry g;
+ struct swm_region *r;
+ int i;
+#ifdef SWM_DEBUG
+ int j;
+#endif
+
+ DNPRINTF(SWM_D_STACK, "stack: begin\n");
+
+ for (i = 0; i < ScreenCount(display); i++) {
+#ifdef SWM_DEBUG
+ j = 0;
+#endif
+ TAILQ_FOREACH(r, &screens[i].rl, entry) {
+ DNPRINTF(SWM_D_STACK, "stack: workspace: %d "
+ "(screen: %d, region: %d)\n", r->ws->idx, i, j++);
+
+ /* start with screen geometry, adjust for bar */
+ g = r->g;
+ g.w -= 2 * border_width;
+ g.h -= 2 * border_width;
+ if (bar_enabled) {
+ if (!bar_at_bottom)
+ g.y += bar_height;
+ g.h -= bar_height;
+ }
+ r->ws->cur_layout->l_stack(r->ws, &g);
+ r->ws->cur_layout->l_string(r->ws);
+ /* save r so we can track region changes */
+ r->ws->old_r = r;
+ }
+ }
+ if (font_adjusted)
+ font_adjusted--;
+
+ if (focus_mode == SWM_FOCUS_DEFAULT)
+ drain_enter_notify();
+
+ DNPRINTF(SWM_D_STACK, "stack: end\n");
+}
+
+void
+store_float_geom(struct ws_win *win, struct swm_region *r)
+{
+ /* retain window geom and region geom */
+ win->g_float = win->g;
+ win->rg_float = r->g;
+ win->g_floatvalid = 1;
+}
+
+void
+stack_floater(struct ws_win *win, struct swm_region *r)
+{
+ unsigned int mask;
+ XWindowChanges wc;
+
+ if (win == NULL)
+ return;
+
+ bzero(&wc, sizeof wc);
+ mask = CWX | CWY | CWBorderWidth | CWWidth | CWHeight;
+
+ /*
+ * to allow windows to change their size (e.g. mplayer fs) only retrieve
+ * geom on ws switches or return from max mode
+ */
+ if (win->floatmaxed || (r != r->ws->old_r && win->g_floatvalid
+ && !(win->ewmh_flags & EWMH_F_FULLSCREEN))) {
+ /*
+ * use stored g and rg to set relative position and size
+ * as in old region or before max stack mode
+ */
+ X(win) = win->g_float.x - win->rg_float.x + X(r);
+ Y(win) = win->g_float.y - win->rg_float.y + Y(r);
+ WIDTH(win) = win->g_float.w;
+ HEIGHT(win) = win->g_float.h;
+ win->g_floatvalid = 0;
+ }
+
+ win->floatmaxed = 0;
+
+ if ((win->quirks & SWM_Q_FULLSCREEN) && (WIDTH(win) >= WIDTH(r)) &&
+ (HEIGHT(win) >= HEIGHT(r)))
+ wc.border_width = 0;
+ else
+ wc.border_width = border_width;
+ if (win->transient && (win->quirks & SWM_Q_TRANSSZ)) {
+ WIDTH(win) = (double)WIDTH(r) * dialog_ratio;
+ HEIGHT(win) = (double)HEIGHT(r) * dialog_ratio;
+ }
+
+ if (!win->manual) {
+ /*
+ * floaters and transients are auto-centred unless moved
+ * or resized
+ */
+ X(win) = X(r) + (WIDTH(r) - WIDTH(win)) / 2 - wc.border_width;
+ Y(win) = Y(r) + (HEIGHT(r) - HEIGHT(win)) / 2 - wc.border_width;
+ }
+
+ /* win can be outside r if new r smaller than old r */
+ /* Ensure top left corner inside r (move probs otherwise) */
+ if (X(win) < X(r) - wc.border_width)
+ X(win) = X(r) - wc.border_width;
+ if (X(win) > X(r) + WIDTH(r) - 1)
+ X(win) = (WIDTH(win) > WIDTH(r)) ? X(r) :
+ (X(r) + WIDTH(r) - WIDTH(win) - 2 * wc.border_width);
+ if (Y(win) < Y(r) - wc.border_width)
+ Y(win) = Y(r) - wc.border_width;
+ if (Y(win) > Y(r) + HEIGHT(r) - 1)
+ Y(win) = (HEIGHT(win) > HEIGHT(r)) ? Y(r) :
+ (Y(r) + HEIGHT(r) - HEIGHT(win) - 2 * wc.border_width);
+
+ wc.x = X(win);
+ wc.y = Y(win);
+ wc.width = WIDTH(win);
+ wc.height = HEIGHT(win);
+
+ /*
+ * Retain floater and transient geometry for correct positioning
+ * when ws changes region
+ */
+ if (!(win->ewmh_flags & EWMH_F_FULLSCREEN))
+ store_float_geom(win, r);
+
+ DNPRINTF(SWM_D_MISC, "stack_floater: window: %lu, (x,y) w x h: (%d,%d) "
+ "%d x %d\n", win->id, wc.x, wc.y, wc.width, wc.height);
+
+ XConfigureWindow(display, win->id, mask, &wc);
+}
+
+/*
+ * Send keystrokes to terminal to decrease/increase the font size as the
+ * window size changes.
+ */
+void
+adjust_font(struct ws_win *win)
+{
+ if (!(win->quirks & SWM_Q_XTERM_FONTADJ) ||
+ win->floating || win->transient)
+ return;
+
+ if (win->sh.width_inc && win->last_inc != win->sh.width_inc &&
+ WIDTH(win) / win->sh.width_inc < term_width &&
+ win->font_steps < SWM_MAX_FONT_STEPS) {
+ win->font_size_boundary[win->font_steps] =
+ (win->sh.width_inc * term_width) + win->sh.base_width;
+ win->font_steps++;
+ font_adjusted++;
+ win->last_inc = win->sh.width_inc;
+ fake_keypress(win, XK_KP_Subtract, ShiftMask);
+ } else if (win->font_steps && win->last_inc != win->sh.width_inc &&
+ WIDTH(win) > win->font_size_boundary[win->font_steps - 1]) {
+ win->font_steps--;
+ font_adjusted++;
+ win->last_inc = win->sh.width_inc;
+ fake_keypress(win, XK_KP_Add, ShiftMask);
+ }
+}
+
+#define SWAPXY(g) do { \
+ int tmp; \
+ tmp = (g)->y; (g)->y = (g)->x; (g)->x = tmp; \
+ tmp = (g)->h; (g)->h = (g)->w; (g)->w = tmp; \
+} while (0)
+void
+stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
+{
+ XWindowChanges wc;
+ XWindowAttributes wa;
+ struct swm_geometry win_g, r_g = *g;
+ struct ws_win *win, *fs_win = 0;
+ int i, j, s, stacks;
+ int w_inc = 1, h_inc, w_base = 1, h_base;
+ int hrh, extra = 0, h_slice, last_h = 0;
+ int split, colno, winno, mwin, msize, mscale;
+ int remain, missing, v_slice, reconfigure;
+ unsigned int mask;
+
+ DNPRINTF(SWM_D_STACK, "stack_master: workspace: %d, rot: %s, "
+ "flip: %s\n", ws->idx, YESNO(rot), YESNO(flip));
+
+ winno = count_win(ws, 0);
+ if (winno == 0 && count_win(ws, 1) == 0)
+ return;
+
+ TAILQ_FOREACH(win, &ws->winlist, entry)
+ if (win->transient == 0 && win->floating == 0
+ && win->iconic == 0)
+ break;
+
+ if (win == NULL)
+ goto notiles;
+
+ if (rot) {
+ w_inc = win->sh.width_inc;
+ w_base = win->sh.base_width;
+ mwin = ws->l_state.horizontal_mwin;
+ mscale = ws->l_state.horizontal_msize;
+ stacks = ws->l_state.horizontal_stacks;
+ SWAPXY(&r_g);
+ } else {
+ w_inc = win->sh.height_inc;
+ w_base = win->sh.base_height;
+ mwin = ws->l_state.vertical_mwin;
+ mscale = ws->l_state.vertical_msize;
+ stacks = ws->l_state.vertical_stacks;
+ }
+ win_g = r_g;
+
+ if (stacks > winno - mwin)
+ stacks = winno - mwin;
+ if (stacks < 1)
+ stacks = 1;
+
+ h_slice = r_g.h / SWM_H_SLICE;
+ if (mwin && winno > mwin) {
+ v_slice = r_g.w / SWM_V_SLICE;
+
+ split = mwin;
+ colno = split;
+ win_g.w = v_slice * mscale;
+
+ if (w_inc > 1 && w_inc < v_slice) {
+ /* adjust for window's requested size increment */
+ remain = (win_g.w - w_base) % w_inc;
+ missing = w_inc - remain;
+ win_g.w -= remain;
+ extra += remain;
+ }
+
+ msize = win_g.w;
+ if (flip)
+ win_g.x += r_g.w - msize;
+ } else {
+ msize = -2;
+ colno = split = winno / stacks;
+ win_g.w = ((r_g.w - (stacks * 2 * border_width) +
+ 2 * border_width) / stacks);
+ }
+ hrh = r_g.h / colno;
+ extra = r_g.h - (colno * hrh);
+ win_g.h = hrh - 2 * border_width;
+
+ /* stack all the tiled windows */
+ i = j = 0, s = stacks;
+ TAILQ_FOREACH(win, &ws->winlist, entry) {
+ if (win->transient != 0 || win->floating != 0)
+ continue;
+ if (win->iconic != 0)
+ continue;
+
+ if (win->ewmh_flags & EWMH_F_FULLSCREEN) {
+ fs_win = win;
+ continue;
+ }
+
+ if (split && i == split) {
+ colno = (winno - mwin) / stacks;
+ if (s <= (winno - mwin) % stacks)
+ colno++;
+ split = split + colno;
+ hrh = (r_g.h / colno);
+ extra = r_g.h - (colno * hrh);
+ if (flip)
+ win_g.x = r_g.x;
+ else
+ win_g.x += win_g.w + 2 * border_width;
+ win_g.w = (r_g.w - msize -
+ (stacks * 2 * border_width)) / stacks;
+ if (s == 1)
+ win_g.w += (r_g.w - msize -
+ (stacks * 2 * border_width)) % stacks;
+ s--;
+ j = 0;
+ }
+ win_g.h = hrh - 2 * border_width;
+ if (rot) {
+ h_inc = win->sh.width_inc;
+ h_base = win->sh.base_width;
+ } else {
+ h_inc = win->sh.height_inc;
+ h_base = win->sh.base_height;
+ }
+ if (j == colno - 1) {
+ win_g.h = hrh + extra;
+ } else if (h_inc > 1 && h_inc < h_slice) {
+ /* adjust for window's requested size increment */
+ remain = (win_g.h - h_base) % h_inc;
+ missing = h_inc - remain;
+
+ if (missing <= extra || j == 0) {
+ extra -= missing;
+ win_g.h += missing;
+ } else {
+ win_g.h -= remain;
+ extra += remain;
+ }
+ }
+
+ if (j == 0)
+ win_g.y = r_g.y;
+ else
+ win_g.y += last_h + 2 * border_width;
+
+ bzero(&wc, sizeof wc);
+ if (disable_border && bar_enabled == 0 && winno == 1){
+ wc.border_width = 0;
+ win_g.w += 2 * border_width;
+ win_g.h += 2 * border_width;
+ } else
+ wc.border_width = border_width;
+ reconfigure = 0;
+ if (rot) {
+ if (X(win) != win_g.y || Y(win) != win_g.x ||
+ WIDTH(win) != win_g.h || HEIGHT(win) != win_g.w) {
+ reconfigure = 1;
+ X(win) = wc.x = win_g.y;
+ Y(win) = wc.y = win_g.x;
+ WIDTH(win) = wc.width = win_g.h;
+ HEIGHT(win) = wc.height = win_g.w;
+ }
+ } else {
+ if (X(win) != win_g.x || Y(win) != win_g.y ||
+ WIDTH(win) != win_g.w || HEIGHT(win) != win_g.h) {
+ reconfigure = 1;
+ X(win) = wc.x = win_g.x;
+ Y(win) = wc.y = win_g.y;
+ WIDTH(win) = wc.width = win_g.w;
+ HEIGHT(win) = wc.height = win_g.h;
+ }
+ }
+ if (reconfigure) {
+ adjust_font(win);
+ mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
+ XConfigureWindow(display, win->id, mask, &wc);
+ }
+
+ if (XGetWindowAttributes(display, win->id, &wa))
+ if (wa.map_state == IsUnmapped)
+ XMapRaised(display, win->id);
+
+ last_h = win_g.h;
+ i++;
+ j++;
+ }
+
+notiles:
+ /* now, stack all the floaters and transients */
+ TAILQ_FOREACH(win, &ws->winlist, entry) {
+ if (win->transient == 0 && win->floating == 0)
+ continue;
+ if (win->iconic == 1)
+ continue;
+ if (win->ewmh_flags & EWMH_F_FULLSCREEN) {
+ fs_win = win;
+ continue;
+ }
+
+ stack_floater(win, ws->r);
+ XMapRaised(display, win->id);
+ }
+
+ if (fs_win) {
+ stack_floater(fs_win, ws->r);
+ XMapRaised(display, fs_win->id);
+ }
+}
+
+void
+vertical_config(struct workspace *ws, int id)
+{
+ DNPRINTF(SWM_D_STACK, "vertical_config: id: %d, workspace: %d\n",
+ id, ws->idx);
+
+ switch (id) {
+ case SWM_ARG_ID_STACKRESET:
+ case SWM_ARG_ID_STACKINIT:
+ ws->l_state.vertical_msize = SWM_V_SLICE / 2;
+ ws->l_state.vertical_mwin = 1;
+ ws->l_state.vertical_stacks = 1;
+ break;
+ case SWM_ARG_ID_MASTERSHRINK:
+ if (ws->l_state.vertical_msize > 1)
+ ws->l_state.vertical_msize--;
+ break;
+ case SWM_ARG_ID_MASTERGROW:
+ if (ws->l_state.vertical_msize < SWM_V_SLICE - 1)
+ ws->l_state.vertical_msize++;
+ break;
+ case SWM_ARG_ID_MASTERADD:
+ ws->l_state.vertical_mwin++;
+ break;
+ case SWM_ARG_ID_MASTERDEL:
+ if (ws->l_state.vertical_mwin > 0)
+ ws->l_state.vertical_mwin--;
+ break;
+ case SWM_ARG_ID_STACKINC:
+ ws->l_state.vertical_stacks++;
+ break;
+ case SWM_ARG_ID_STACKDEC:
+ if (ws->l_state.vertical_stacks > 1)
+ ws->l_state.vertical_stacks--;
+ break;
+ case SWM_ARG_ID_FLIPLAYOUT:
+ ws->l_state.vertical_flip = !ws->l_state.vertical_flip;
+ break;
+ default:
+ return;
+ }
+}
+
+void
+vertical_stack(struct workspace *ws, struct swm_geometry *g)
+{
+ DNPRINTF(SWM_D_STACK, "vertical_stack: workspace: %d\n", ws->idx);
+
+ stack_master(ws, g, 0, ws->l_state.vertical_flip);
+}
+
+void
+horizontal_config(struct workspace *ws, int id)
+{
+ DNPRINTF(SWM_D_STACK, "horizontal_config: workspace: %d\n", ws->idx);
+
+ switch (id) {
+ case SWM_ARG_ID_STACKRESET:
+ case SWM_ARG_ID_STACKINIT:
+ ws->l_state.horizontal_mwin = 1;
+ ws->l_state.horizontal_msize = SWM_H_SLICE / 2;
+ ws->l_state.horizontal_stacks = 1;
+ break;
+ case SWM_ARG_ID_MASTERSHRINK:
+ if (ws->l_state.horizontal_msize > 1)
+ ws->l_state.horizontal_msize--;
+ break;
+ case SWM_ARG_ID_MASTERGROW:
+ if (ws->l_state.horizontal_msize < SWM_H_SLICE - 1)
+ ws->l_state.horizontal_msize++;
+ break;
+ case SWM_ARG_ID_MASTERADD:
+ ws->l_state.horizontal_mwin++;
+ break;
+ case SWM_ARG_ID_MASTERDEL:
+ if (ws->l_state.horizontal_mwin > 0)
+ ws->l_state.horizontal_mwin--;
+ break;
+ case SWM_ARG_ID_STACKINC:
+ ws->l_state.horizontal_stacks++;
+ break;
+ case SWM_ARG_ID_STACKDEC:
+ if (ws->l_state.horizontal_stacks > 1)
+ ws->l_state.horizontal_stacks--;
+ break;
+ case SWM_ARG_ID_FLIPLAYOUT:
+ ws->l_state.horizontal_flip = !ws->l_state.horizontal_flip;
+ break;
+ default:
+ return;
+ }
+}
+
+void
+horizontal_stack(struct workspace *ws, struct swm_geometry *g)
+{
+ DNPRINTF(SWM_D_STACK, "horizontal_stack: workspace: %d\n", ws->idx);
+
+ stack_master(ws, g, 1, ws->l_state.horizontal_flip);
+}
+
+/* fullscreen view */
+void
+max_stack(struct workspace *ws, struct swm_geometry *g)
+{
+ XWindowChanges wc;
+ struct swm_geometry gg = *g;
+ struct ws_win *win, *wintrans = NULL, *parent = NULL;
+ unsigned int mask;
+ int winno;
+
+ DNPRINTF(SWM_D_STACK, "max_stack: workspace: %d\n", ws->idx);
+
+ if (ws == NULL)
+ return;
+
+ winno = count_win(ws, 0);
+ if (winno == 0 && count_win(ws, 1) == 0)
+ return;
+
+ TAILQ_FOREACH(win, &ws->winlist, entry) {
+ if (win->transient) {
+ wintrans = win;
+ parent = find_window(win->transient);
+ continue;
+ }
+
+ if (win->floating && win->floatmaxed == 0 ) {
+ /*
+ * retain geometry for retrieval on exit from
+ * max_stack mode
+ */
+ store_float_geom(win, ws->r);
+ win->floatmaxed = 1;
+ }
+
+ /* only reconfigure if necessary */
+ if (X(win) != gg.x || Y(win) != gg.y || WIDTH(win) != gg.w ||
+ HEIGHT(win) != gg.h) {
+ bzero(&wc, sizeof wc);
+ X(win) = wc.x = gg.x;
+ Y(win) = wc.y = gg.y;
+ if (bar_enabled){
+ wc.border_width = border_width;
+ WIDTH(win) = wc.width = gg.w;
+ HEIGHT(win) = wc.height = gg.h;
+ } else {
+ wc.border_width = 0;
+ WIDTH(win) = wc.width = gg.w + 2 * border_width;
+ HEIGHT(win) = wc.height = gg.h +
+ 2 * border_width;
+ }
+ mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
+ XConfigureWindow(display, win->id, mask, &wc);
+ }
+ /* unmap only if we don't have multi screen */
+ if (win != ws->focus)
+ if (!(ScreenCount(display) > 1 || outputs > 1))
+ unmap_window(win);
+ }
+
+ /* put the last transient on top */
+ if (wintrans) {
+ if (parent)
+ XMapRaised(display, parent->id);
+ stack_floater(wintrans, ws->r);
+ focus_magic(wintrans);
+ }
+}
+
+void
+send_to_ws(struct swm_region *r, union arg *args)
+{
+ int wsid = args->id;
+ struct ws_win *win = NULL, *parent;
+ struct workspace *ws, *nws;
+ Atom ws_idx_atom = 0;
+ unsigned char ws_idx_str[SWM_PROPLEN];
+ union arg a;
+
+ if (r && r->ws && r->ws->focus)
+ win = r->ws->focus;
+ else
+ return;
+ if (win == NULL)
+ return;
+ if (win->ws->idx == wsid)
+ return;
+
+ DNPRINTF(SWM_D_MOVE, "send_to_ws: window: 0x%lx\n", win->id);
+
+ ws = win->ws;
+ nws = &win->s->ws[wsid];
+
+ a.id = SWM_ARG_ID_FOCUSPREV;
+ focus(r, &a);
+ if (win->transient) {
+ parent = find_window(win->transient);
+ if (parent) {
+ unmap_window(parent);
+ TAILQ_REMOVE(&ws->winlist, parent, entry);
+ TAILQ_INSERT_TAIL(&nws->winlist, parent, entry);
+ parent->ws = nws;
+ }
+ }
+ unmap_window(win);
+ TAILQ_REMOVE(&ws->winlist, win, entry);
+ TAILQ_INSERT_TAIL(&nws->winlist, win, entry);
+ if (TAILQ_EMPTY(&ws->winlist))
+ r->ws->focus = NULL;
+ win->ws = nws;
+
+ /* Try to update the window's workspace property */
+ ws_idx_atom = XInternAtom(display, "_SWM_WS", False);
+ if (ws_idx_atom &&
+ snprintf((char *)ws_idx_str, SWM_PROPLEN, "%d", nws->idx) <
+ SWM_PROPLEN) {
+ DNPRINTF(SWM_D_PROP, "send_to_ws: set property: _SWM_WS: %s\n",
+ ws_idx_str);
+ XChangeProperty(display, win->id, ws_idx_atom, XA_STRING, 8,
+ PropModeReplace, ws_idx_str, SWM_PROPLEN);
+ }
+
+ stack();
+}
+
+void
+pressbutton(struct swm_region *r, union arg *args)
+{
+ XTestFakeButtonEvent(display, args->id, True, CurrentTime);
+ XTestFakeButtonEvent(display, args->id, False, CurrentTime);
+}
+
+void
+raise_toggle(struct swm_region *r, union arg *args)
+{
+ if (r && r->ws == NULL)
+ return;
+
+ r->ws->always_raise = !r->ws->always_raise;
+
+ /* bring floaters back to top */
+ if (r->ws->always_raise == 0)
+ stack();
+}
+
+void
+iconify(struct swm_region *r, union arg *args)
+{
+ union arg a;
+
+ if (r->ws->focus == NULL)
+ return;
+ unmap_window(r->ws->focus);
+ update_iconic(r->ws->focus, 1);
+ stack();
+ if (focus_mode == SWM_FOCUS_DEFAULT)
+ drain_enter_notify();
+ r->ws->focus = NULL;
+ a.id = SWM_ARG_ID_FOCUSCUR;
+ focus(r, &a);
+}
+
+unsigned char *
+get_win_name(Window win)
+{
+ unsigned char *prop = NULL;
+ unsigned long nbytes, nitems;
+
+ /* try _NET_WM_NAME first */
+ if (get_property(win, a_netwmname, 0L, a_utf8_string, NULL, &nbytes,
+ &prop)) {
+ XFree(prop);
+ if (get_property(win, a_netwmname, nbytes, a_utf8_string,
+ &nitems, NULL, &prop))
+ return (prop);
+ }
+
+ /* fallback to WM_NAME */
+ if (!get_property(win, a_wmname, 0L, a_string, NULL, &nbytes, &prop))
+ return (NULL);
+ XFree(prop);
+ if (get_property(win, a_wmname, nbytes, a_string, &nitems, NULL, &prop))
+ return (prop);
+
+ return (NULL);
+}
+
+void
+uniconify(struct swm_region *r, union arg *args)
+{
+ struct ws_win *win;
+ FILE *lfile;
+ unsigned char *name;
+ int count = 0;
+
+ DNPRINTF(SWM_D_MISC, "uniconify\n");
+
+ if (r && r->ws == NULL)
+ return;
+
+ /* make sure we have anything to uniconify */
+ TAILQ_FOREACH(win, &r->ws->winlist, entry) {
+ if (win->ws == NULL)
+ continue; /* should never happen */
+ if (win->iconic == 0)
+ continue;
+ count++;
+ }
+ if (count == 0)
+ return;
+
+ search_r = r;
+ search_resp_action = SWM_SEARCH_UNICONIFY;
+
+ spawn_select(r, args, "search", &searchpid);
+
+ if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
+ return;
+
+ TAILQ_FOREACH(win, &r->ws->winlist, entry) {
+ if (win->ws == NULL)
+ continue; /* should never happen */
+ if (win->iconic == 0)
+ continue;
+
+ name = get_win_name(win->id);
+ if (name == NULL)
+ continue;
+ fprintf(lfile, "%s.%lu\n", name, win->id);
+ XFree(name);
+ }
+
+ fclose(lfile);
+}
+
+void
+name_workspace(struct swm_region *r, union arg *args)
+{
+ FILE *lfile;
+
+ DNPRINTF(SWM_D_MISC, "name_workspace\n");
+
+ if (r == NULL)
+ return;
+
+ search_r = r;
+ search_resp_action = SWM_SEARCH_NAME_WORKSPACE;
+
+ spawn_select(r, args, "name_workspace", &searchpid);
+
+ if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
+ return;
+
+ fprintf(lfile, "%s", "");
+ fclose(lfile);
+}
+
+void
+search_workspace(struct swm_region *r, union arg *args)
+{
+ int i;
+ struct workspace *ws;
+ FILE *lfile;
+
+ DNPRINTF(SWM_D_MISC, "search_workspace\n");
+
+ if (r == NULL)
+ return;
+
+ search_r = r;
+ search_resp_action = SWM_SEARCH_SEARCH_WORKSPACE;
+
+ spawn_select(r, args, "search", &searchpid);
+
+ if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
+ return;
+
+ for (i = 0; i < SWM_WS_MAX; i++) {
+ ws = &r->s->ws[i];
+ if (ws == NULL)
+ continue;
+ fprintf(lfile, "%d%s%s\n", ws->idx + 1,
+ (ws->name ? ":" : ""), (ws->name ? ws->name : ""));
+ }
+
+ fclose(lfile);
+}
+
+void
+search_win_cleanup(void)
+{
+ struct search_window *sw = NULL;
+
+ while ((sw = TAILQ_FIRST(&search_wl)) != NULL) {
+ XDestroyWindow(display, sw->indicator);
+ XFreeGC(display, sw->gc);
+ TAILQ_REMOVE(&search_wl, sw, entry);
+ free(sw);
+ }
+}
+
+void
+search_win(struct swm_region *r, union arg *args)
+{
+ struct ws_win *win = NULL;
+ struct search_window *sw = NULL;
+ Window w;
+ XGCValues gcv;
+ int i;
+ char s[8];
+ FILE *lfile;
+ size_t len;
+ XRectangle ibox, lbox;
+
+ DNPRINTF(SWM_D_MISC, "search_win\n");
+
+ search_r = r;
+ search_resp_action = SWM_SEARCH_SEARCH_WINDOW;
+
+ spawn_select(r, args, "search", &searchpid);
+
+ if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
+ return;
+
+ TAILQ_INIT(&search_wl);
+
+ i = 1;
+ TAILQ_FOREACH(win, &r->ws->winlist, entry) {
+ if (win->iconic == 1)
+ continue;
+
+ sw = calloc(1, sizeof(struct search_window));
+ if (sw == NULL) {
+ warn("search_win: calloc");
+ fclose(lfile);
+ search_win_cleanup();
+ return;
+ }
+ sw->idx = i;
+ sw->win = win;
+
+ snprintf(s, sizeof s, "%d", i);
+ len = strlen(s);
+
+ XmbTextExtents(bar_fs, s, len, &ibox, &lbox);
+
+ w = XCreateSimpleWindow(display,
+ win->id, 0, 0,lbox.width + 4,
+ bar_fs_extents->max_logical_extent.height, 1,
+ r->s->c[SWM_S_COLOR_UNFOCUS].color,
+ r->s->c[SWM_S_COLOR_FOCUS].color);
+
+ sw->indicator = w;
+ TAILQ_INSERT_TAIL(&search_wl, sw, entry);
+
+ sw->gc = XCreateGC(display, w, 0, &gcv);
+ XMapRaised(display, w);
+ XSetForeground(display, sw->gc, r->s->c[SWM_S_COLOR_BAR].color);
+
+ DRAWSTRING(display, w, bar_fs, sw->gc, 2,
+ (bar_fs_extents->max_logical_extent.height -
+ lbox.height) / 2 - lbox.y, s, len);
+
+ fprintf(lfile, "%d\n", i);
+ i++;
+ }
+
+ fclose(lfile);
+}
+
+void
+search_resp_uniconify(char *resp, unsigned long len)
+{
+ unsigned char *name;
+ struct ws_win *win;
+ char *s;
+
+ DNPRINTF(SWM_D_MISC, "search_resp_uniconify: resp: %s\n", resp);
+
+ TAILQ_FOREACH(win, &search_r->ws->winlist, entry) {
+ if (win->iconic == 0)
+ continue;
+ name = get_win_name(win->id);
+ if (name == NULL)
+ continue;
+ if (asprintf(&s, "%s.%lu", name, win->id) == -1) {
+ XFree(name);
+ continue;
+ }
+ XFree(name);
+ if (strncmp(s, resp, len) == 0) {
+ /* XXX this should be a callback to generalize */
+ update_iconic(win, 0);
+ free(s);
+ break;
+ }
+ free(s);
+ }
+}
+
+void
+search_resp_name_workspace(char *resp, unsigned long len)
+{
+ struct workspace *ws;
+
+ DNPRINTF(SWM_D_MISC, "search_resp_name_workspace: resp: %s\n", resp);
+
+ if (search_r->ws == NULL)
+ return;
+ ws = search_r->ws;
+
+ if (ws->name) {
+ free(search_r->ws->name);
+ search_r->ws->name = NULL;
+ }
+
+ if (len > 1) {
+ ws->name = strdup(resp);
+ if (ws->name == NULL) {
+ DNPRINTF(SWM_D_MISC, "search_resp_name_workspace: "
+ "strdup: %s", strerror(errno));
+ return;
+ }
+ }
+}
+
+void
+search_resp_search_workspace(char *resp, unsigned long len)
+{
+ char *p, *q;
+ int ws_idx;
+ const char *errstr;
+ union arg a;
+
+ DNPRINTF(SWM_D_MISC, "search_resp_search_workspace: resp: %s\n", resp);
+
+ q = strdup(resp);
+ if (!q) {
+ DNPRINTF(SWM_D_MISC, "search_resp_search_workspace: strdup: %s",
+ strerror(errno));
+ return;
+ }
+ p = strchr(q, ':');
+ if (p != NULL)
+ *p = '\0';
+ ws_idx = strtonum(q, 1, SWM_WS_MAX, &errstr);
+ if (errstr) {
+ DNPRINTF(SWM_D_MISC, "workspace idx is %s: %s",
+ errstr, q);
+ free(q);
+ return;
+ }
+ free(q);
+ a.id = ws_idx - 1;
+ switchws(search_r, &a);
+}
+
+void
+search_resp_search_window(char *resp, unsigned long len)
+{
+ char *s;
+ int idx;
+ const char *errstr;
+ struct search_window *sw;
+
+ DNPRINTF(SWM_D_MISC, "search_resp_search_window: resp: %s\n", resp);
+
+ s = strdup(resp);
+ if (!s) {
+ DNPRINTF(SWM_D_MISC, "search_resp_search_window: strdup: %s",
+ strerror(errno));
+ return;
+ }
+
+ idx = strtonum(s, 1, INT_MAX, &errstr);
+ if (errstr) {
+ DNPRINTF(SWM_D_MISC, "window idx is %s: %s",
+ errstr, s);
+ free(s);
+ return;
+ }
+ free(s);
+
+ TAILQ_FOREACH(sw, &search_wl, entry)
+ if (idx == sw->idx) {
+ focus_win(sw->win);
+ break;
+ }
+}
+
+#define MAX_RESP_LEN 1024
+
+void
+search_do_resp(void)
+{
+ ssize_t rbytes;
+ char *resp;
+ unsigned long len;
+
+ DNPRINTF(SWM_D_MISC, "search_do_resp:\n");
+
+ search_resp = 0;
+ searchpid = 0;
+
+ if ((resp = calloc(1, MAX_RESP_LEN + 1)) == NULL) {
+ warn("search: calloc");
+ goto done;
+ }
+
+ rbytes = read(select_resp_pipe[0], resp, MAX_RESP_LEN);
+ if (rbytes <= 0) {
+ warn("search: read error");
+ goto done;
+ }
+ resp[rbytes] = '\0';
+
+ /* XXX:
+ * Older versions of dmenu (Atleast pre 4.4.1) do not send a
+ * newline, so work around that by sanitizing the resp now.
+ */
+ resp[strcspn(resp, "\n")] = '\0';
+ len = strlen(resp);
+
+ switch (search_resp_action) {
+ case SWM_SEARCH_UNICONIFY:
+ search_resp_uniconify(resp, len);
+ break;
+ case SWM_SEARCH_NAME_WORKSPACE:
+ search_resp_name_workspace(resp, len);
+ break;
+ case SWM_SEARCH_SEARCH_WORKSPACE:
+ search_resp_search_workspace(resp, len);
+ break;
+ case SWM_SEARCH_SEARCH_WINDOW:
+ search_resp_search_window(resp, len);
+ break;
+ }
+
+done:
+ if (search_resp_action == SWM_SEARCH_SEARCH_WINDOW)
+ search_win_cleanup();
+
+ search_resp_action = SWM_SEARCH_NONE;
+ close(select_resp_pipe[0]);
+ free(resp);
+}
+
+void
+wkill(struct swm_region *r, union arg *args)
+{
+ DNPRINTF(SWM_D_MISC, "wkill: id: %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);
+}
+
+
+int
+floating_toggle_win(struct ws_win *win)
+{
+ struct swm_region *r;
+
+ if (win == NULL)
+ return 0;
+
+ if (!win->ws->r)
+ return 0;
+
+ r = win->ws->r;
+
+ /* reject floating toggles in max stack mode */
+ if (win->ws->cur_layout == &layouts[SWM_MAX_STACK])
+ return 0;
+
+ if (win->floating) {
+ if (!win->floatmaxed) {
+ /* retain position for refloat */
+ store_float_geom(win, r);
+ }
+ win->floating = 0;
+ } else {
+ if (win->g_floatvalid) {
+ /* refloat at last floating relative position */
+ X(win) = win->g_float.x - win->rg_float.x + X(r);
+ Y(win) = win->g_float.y - win->rg_float.y + Y(r);
+ WIDTH(win) = win->g_float.w;
+ HEIGHT(win) = win->g_float.h;
+ }
+ win->floating = 1;
+ }
+
+ ewmh_update_actions(win);
+
+ return 1;
+}
+
+void
+floating_toggle(struct swm_region *r, union arg *args)
+{
+ struct ws_win *win = r->ws->focus;
+ union arg a;
+
+ if (win == NULL)
+ return;
+
+ ewmh_update_win_state(win, ewmh[_NET_WM_STATE_ABOVE].atom,
+ _NET_WM_STATE_TOGGLE);
+
+ stack();
+ if (focus_mode == SWM_FOCUS_DEFAULT)
+ drain_enter_notify();
+
+ if (win == win->ws->focus) {
+ a.id = SWM_ARG_ID_FOCUSCUR;
+ focus(win->ws->r, &a);
+ }
+}
+
+void
+constrain_window(struct ws_win *win, struct swm_region *r, int resizable)
+{
+ if (X(win) + WIDTH(win) > X(r) + WIDTH(r) - border_width) {
+ if (resizable)
+ WIDTH(win) = X(r) + WIDTH(r) - X(win) - border_width;
+ else
+ X(win) = X(r) + WIDTH(r) - WIDTH(win) - border_width;
+ }
+
+ if (X(win) < X(r) - border_width) {
+ if (resizable)
+ WIDTH(win) -= X(r) - X(win) - border_width;
+
+ X(win) = X(r) - border_width;
+ }
+
+ if (Y(win) + HEIGHT(win) > Y(r) + HEIGHT(r) - border_width) {
+ if (resizable)
+ HEIGHT(win) = Y(r) + HEIGHT(r) - Y(win) - border_width;
+ else
+ Y(win) = Y(r) + HEIGHT(r) - HEIGHT(win) - border_width;
+ }
+
+ if (Y(win) < Y(r) - border_width) {
+ if (resizable)
+ HEIGHT(win) -= Y(r) - Y(win) - border_width;
+
+ Y(win) = Y(r) - border_width;
+ }
+
+ if (WIDTH(win) < 1)
+ WIDTH(win) = 1;
+ if (HEIGHT(win) < 1)
+ HEIGHT(win) = 1;
+}
+
+void
+update_window(struct ws_win *win)
+{
+ unsigned int mask;
+ XWindowChanges wc;
+
+ bzero(&wc, sizeof wc);
+ mask = CWBorderWidth | CWWidth | CWHeight | CWX | CWY;
+ wc.border_width = border_width;
+ wc.x = X(win);
+ wc.y = Y(win);
+ wc.width = WIDTH(win);
+ wc.height = HEIGHT(win);
+
+ DNPRINTF(SWM_D_MISC, "update_window: window: 0x%lx, (x,y) w x h: "
+ "(%d,%d) %d x %d\n", win->id, wc.x, wc.y, wc.width, wc.height);
+
+ XConfigureWindow(display, win->id, mask, &wc);
+}
+
+#define SWM_RESIZE_STEPS (50)
+
+void
+resize(struct ws_win *win, union arg *args)
+{
+ XEvent ev;
+ Time time = 0;
+ struct swm_region *r = NULL;
+ int resize_step = 0;
+ Window rr, cr;
+ int x, y, wx, wy;
+ unsigned int mask;
+ struct swm_geometry g;
+ int top = 0, left = 0;
+ int dx, dy;
+ Cursor cursor;
+ unsigned int shape; /* cursor style */
+
+ if (win == NULL)
+ return;
+ r = win->ws->r;
+
+ DNPRINTF(SWM_D_MOUSE, "resize: window: 0x%lx, floating: %s, "
+ "transient: 0x%lx\n", win->id, YESNO(win->floating),
+ win->transient);
+
+ if (!(win->transient != 0 || win->floating != 0))
+ return;
+
+ /* reject resizes in max mode for floaters (transient ok) */
+ if (win->floatmaxed)
+ return;
+
+ win->manual = 1;
+ ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom,
+ _NET_WM_STATE_ADD);
+
+ stack();
+
+ switch (args->id) {
+ case SWM_ARG_ID_WIDTHSHRINK:
+ WIDTH(win) -= SWM_RESIZE_STEPS;
+ resize_step = 1;
+ break;
+ case SWM_ARG_ID_WIDTHGROW:
+ WIDTH(win) += SWM_RESIZE_STEPS;
+ resize_step = 1;
+ break;
+ case SWM_ARG_ID_HEIGHTSHRINK:
+ HEIGHT(win) -= SWM_RESIZE_STEPS;
+ resize_step = 1;
+ break;
+ case SWM_ARG_ID_HEIGHTGROW:
+ HEIGHT(win) += SWM_RESIZE_STEPS;
+ resize_step = 1;
+ break;
+ default:
+ break;
+ }
+ if (resize_step) {
+ constrain_window(win, r, 1);
+ update_window(win);
+ store_float_geom(win,r);
+ return;
+ }
+
+ if (focus_mode == SWM_FOCUS_DEFAULT)
+ drain_enter_notify();
+
+ /* get cursor offset from window root */
+ if (!XQueryPointer(display, win->id, &rr, &cr, &x, &y, &wx, &wy, &mask))
+ return;
+
+ g = win->g;
+
+ if (wx < WIDTH(win) / 2)
+ left = 1;
+
+ if (wy < HEIGHT(win) / 2)
+ top = 1;
+
+ if (args->id == SWM_ARG_ID_CENTER)
+ shape = XC_sizing;
+ else if (top)
+ shape = (left) ? XC_top_left_corner : XC_top_right_corner;
+ else
+ shape = (left) ? XC_bottom_left_corner : XC_bottom_right_corner;
+
+ cursor = XCreateFontCursor(display, shape);
+
+ if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync,
+ GrabModeAsync, None, cursor, CurrentTime) != GrabSuccess) {
+ XFreeCursor(display, cursor);
+ return;
+ }
+
+ do {
+ XMaskEvent(display, MOUSEMASK | ExposureMask |
+ SubstructureRedirectMask, &ev);
+ switch (ev.type) {
+ case ConfigureRequest:
+ case Expose:
+ case MapRequest:
+ handler[ev.type](&ev);
+ break;
+ case MotionNotify:
+ /* cursor offset/delta from start of the operation */
+ dx = ev.xmotion.x_root - x;
+ dy = ev.xmotion.y_root - y;
+
+ /* vertical */
+ if (top)
+ dy = -dy;
+
+ if (args->id == SWM_ARG_ID_CENTER) {
+ if (g.h / 2 + dy < 1)
+ dy = 1 - g.h / 2;
+
+ Y(win) = g.y - dy;
+ HEIGHT(win) = g.h + 2 * dy;
+ } else {
+ if (g.h + dy < 1)
+ dy = 1 - g.h;
+
+ if (top)
+ Y(win) = g.y - dy;
+
+ HEIGHT(win) = g.h + dy;
+ }
+
+ /* horizontal */
+ if (left)
+ dx = -dx;
+
+ if (args->id == SWM_ARG_ID_CENTER) {
+ if (g.w / 2 + dx < 1)
+ dx = 1 - g.w / 2;
+
+ X(win) = g.x - dx;
+ WIDTH(win) = g.w + 2 * dx;
+ } else {
+ if (g.w + dx < 1)
+ dx = 1 - g.w;
+
+ if (left)
+ X(win) = g.x - dx;
+
+ WIDTH(win) = g.w + dx;
+ }
+
+ constrain_window(win, r, 1);
+
+ /* not free, don't sync more than 120 times / second */
+ if ((ev.xmotion.time - time) > (1000 / 120) ) {
+ time = ev.xmotion.time;
+ XSync(display, False);
+ update_window(win);
+ }
+ break;
+ }
+ } while (ev.type != ButtonRelease);
+ if (time) {
+ XSync(display, False);
+ update_window(win);
+ }
+ store_float_geom(win,r);
+
+ XUngrabPointer(display, CurrentTime);
+ XFreeCursor(display, cursor);
+
+ /* drain events */
+ drain_enter_notify();
+}
+
+void
+resize_step(struct swm_region *r, union arg *args)
+{
+ struct ws_win *win = NULL;
+
+ if (r && r->ws && r->ws->focus)
+ win = r->ws->focus;
+ else
+ return;
+
+ resize(win, args);
+}
+
+#define SWM_MOVE_STEPS (50)
+
+void
+move(struct ws_win *win, union arg *args)
+{
+ XEvent ev;
+ Time time = 0;
+ int move_step = 0;
+ struct swm_region *r = NULL;
+
+ Window rr, cr;
+ int x, y, wx, wy;
+ unsigned int mask;
+
+ if (win == NULL)
+ return;
+ r = win->ws->r;
+
+ DNPRINTF(SWM_D_MOUSE, "move: window: 0x%lx, floating: %s, transient: "
+ "0x%lx\n", win->id, YESNO(win->floating), win->transient);
+
+ /* in max_stack mode should only move transients */
+ if (win->ws->cur_layout == &layouts[SWM_MAX_STACK] && !win->transient)
+ return;
+
+ win->manual = 1;
+ if (win->floating == 0 && !win->transient) {
+ store_float_geom(win,r);
+ ewmh_update_win_state(win, ewmh[_NET_WM_STATE_ABOVE].atom,
+ _NET_WM_STATE_ADD);
+ }
+ ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom,
+ _NET_WM_STATE_ADD);
+
+ stack();
+
+ move_step = 0;
+ switch (args->id) {
+ case SWM_ARG_ID_MOVELEFT:
+ X(win) -= (SWM_MOVE_STEPS - border_width);
+ move_step = 1;
+ break;
+ case SWM_ARG_ID_MOVERIGHT:
+ X(win) += (SWM_MOVE_STEPS - border_width);
+ move_step = 1;
+ break;
+ case SWM_ARG_ID_MOVEUP:
+ Y(win) -= (SWM_MOVE_STEPS - border_width);
+ move_step = 1;
+ break;
+ case SWM_ARG_ID_MOVEDOWN:
+ Y(win) += (SWM_MOVE_STEPS - border_width);
+ move_step = 1;
+ break;
+ default:
+ break;
+ }
+ if (move_step) {
+ constrain_window(win, r, 0);
+ update_window(win);
+ store_float_geom(win, r);
+ return;
+ }
+
+ if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync,
+ GrabModeAsync, None, XCreateFontCursor(display, XC_fleur),
+ CurrentTime) != GrabSuccess)
+ return;
+
+ /* get cursor offset from window root */
+ if (!XQueryPointer(display, win->id, &rr, &cr, &x, &y, &wx, &wy, &mask))
+ return;
+
+ do {
+ XMaskEvent(display, MOUSEMASK | ExposureMask |
+ SubstructureRedirectMask, &ev);
+ switch (ev.type) {
+ case ConfigureRequest:
+ case Expose:
+ case MapRequest:
+ handler[ev.type](&ev);
+ break;
+ case MotionNotify:
+ X(win) = ev.xmotion.x_root - wx - border_width;
+ Y(win) = ev.xmotion.y_root - wy - border_width;
+
+ constrain_window(win, r, 0);
+
+ /* not free, don't sync more than 120 times / second */
+ if ((ev.xmotion.time - time) > (1000 / 120) ) {
+ time = ev.xmotion.time;
+ XSync(display, False);
+ update_window(win);
+ }
+ break;
+ }
+ } while (ev.type != ButtonRelease);
+ if (time) {
+ XSync(display, False);
+ update_window(win);
+ }
+ store_float_geom(win,r);
+ XUngrabPointer(display, CurrentTime);
+
+ /* drain events */
+ drain_enter_notify();
+}
+
+void
+move_step(struct swm_region *r, union arg *args)
+{
+ struct ws_win *win = NULL;
+
+ if (r && r->ws && r->ws->focus)
+ win = r->ws->focus;
+ else
+ return;
+
+ if (!(win->transient != 0 || win->floating != 0))
+ return;
+
+ move(win, args);
+}
+
+
+/* user/key callable function IDs */
+enum keyfuncid {
+ kf_cycle_layout,
+ kf_flip_layout,
+ kf_stack_reset,
+ kf_master_shrink,
+ kf_master_grow,
+ kf_master_add,
+ kf_master_del,
+ kf_stack_inc,
+ kf_stack_dec,
+ kf_swap_main,
+ kf_focus_next,
+ kf_focus_prev,
+ kf_swap_next,
+ kf_swap_prev,
+ kf_spawn_term,
+ kf_spawn_menu,
+ kf_quit,
+ kf_restart,
+ kf_focus_main,
+ kf_ws_1,
+ kf_ws_2,
+ kf_ws_3,
+ kf_ws_4,
+ kf_ws_5,
+ kf_ws_6,
+ kf_ws_7,
+ kf_ws_8,
+ kf_ws_9,
+ kf_ws_10,
+ kf_ws_next,
+ kf_ws_prev,
+ kf_ws_next_all,
+ kf_ws_prev_all,
+ kf_ws_prior,
+ kf_screen_next,
+ kf_screen_prev,
+ kf_mvws_1,
+ kf_mvws_2,
+ kf_mvws_3,
+ kf_mvws_4,
+ kf_mvws_5,
+ kf_mvws_6,
+ kf_mvws_7,
+ kf_mvws_8,
+ kf_mvws_9,
+ kf_mvws_10,
+ kf_bar_toggle,
+ kf_wind_kill,
+ kf_wind_del,
+ kf_screenshot_all,
+ kf_screenshot_wind,
+ kf_float_toggle,
+ kf_version,
+ kf_spawn_lock,
+ kf_spawn_initscr,
+ kf_spawn_custom,
+ kf_iconify,
+ kf_uniconify,
+ kf_raise_toggle,
+ kf_button2,
+ kf_width_shrink,
+ kf_width_grow,
+ kf_height_shrink,
+ kf_height_grow,
+ kf_move_left,
+ kf_move_right,
+ kf_move_up,
+ kf_move_down,
+ kf_name_workspace,
+ kf_search_workspace,
+ kf_search_win,
+ kf_dumpwins, /* MUST BE LAST */
+ kf_invalid
+};
+
+/* key definitions */
+void
+dummykeyfunc(struct swm_region *r, union arg *args)
+{
+};
+
+void
+legacyfunc(struct swm_region *r, union arg *args)
+{
+};
+
+struct keyfunc {
+ char name[SWM_FUNCNAME_LEN];
+ void (*func)(struct swm_region *r, union arg *);
+ union arg args;
+} keyfuncs[kf_invalid + 1] = {
+ /* name function argument */
+ { "cycle_layout", cycle_layout, {0} },
+ { "flip_layout", stack_config, {.id = SWM_ARG_ID_FLIPLAYOUT} },
+ { "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", legacyfunc, {0} },
+ { "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} },
+ { "ws_next_all", cyclews, {.id = SWM_ARG_ID_CYCLEWS_UP_ALL} },
+ { "ws_prev_all", cyclews, {.id = SWM_ARG_ID_CYCLEWS_DOWN_ALL} },
+ { "ws_prior", priorws, {0} },
+ { "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", legacyfunc, {0} },
+ { "screenshot_wind", legacyfunc, {0} },
+ { "float_toggle", floating_toggle,{0} },
+ { "version", version, {0} },
+ { "spawn_lock", legacyfunc, {0} },
+ { "spawn_initscr", legacyfunc, {0} },
+ { "spawn_custom", dummykeyfunc, {0} },
+ { "iconify", iconify, {0} },
+ { "uniconify", uniconify, {0} },
+ { "raise_toggle", raise_toggle, {0} },
+ { "button2", pressbutton, {2} },
+ { "width_shrink", resize_step, {.id = SWM_ARG_ID_WIDTHSHRINK} },
+ { "width_grow", resize_step, {.id = SWM_ARG_ID_WIDTHGROW} },
+ { "height_shrink", resize_step, {.id = SWM_ARG_ID_HEIGHTSHRINK} },
+ { "height_grow", resize_step, {.id = SWM_ARG_ID_HEIGHTGROW} },
+ { "move_left", move_step, {.id = SWM_ARG_ID_MOVELEFT} },
+ { "move_right", move_step, {.id = SWM_ARG_ID_MOVERIGHT} },
+ { "move_up", move_step, {.id = SWM_ARG_ID_MOVEUP} },
+ { "move_down", move_step, {.id = SWM_ARG_ID_MOVEDOWN} },
+ { "name_workspace", name_workspace, {0} },
+ { "search_workspace", search_workspace, {0} },
+ { "search_win", search_win, {0} },
+ { "dumpwins", dumpwins, {0} }, /* MUST BE LAST */
+ { "invalid key func", NULL, {0} },
+};
+struct key {
+ RB_ENTRY(key) entry;
+ unsigned int mod;
+ KeySym keysym;
+ enum keyfuncid funcid;
+ char *spawn_name;
+};
+RB_HEAD(key_list, key);
+
+int
+key_cmp(struct key *kp1, struct key *kp2)
+{
+ if (kp1->keysym < kp2->keysym)
+ return (-1);
+ if (kp1->keysym > kp2->keysym)
+ return (1);
+
+ if (kp1->mod < kp2->mod)
+ return (-1);
+ if (kp1->mod > kp2->mod)
+ return (1);
+
+ return (0);
+}
+
+RB_GENERATE_STATIC(key_list, key, entry, key_cmp);
+struct key_list keys;
+
+/* 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;
+ struct key *kp;
+
+ mod_key = mod;
+ RB_FOREACH(kp, key_list, &keys)
+ if (kp->mod & ShiftMask)
+ kp->mod = mod | ShiftMask;
+ else
+ kp->mod = mod;
+
+ for (i = 0; i < LENGTH(buttons); i++)
+ if (buttons[i].mask & ShiftMask)
+ buttons[i].mask = mod | ShiftMask;
+ else
+ buttons[i].mask = mod;
+}
+
+/* spawn */
+struct spawn_prog {
+ TAILQ_ENTRY(spawn_prog) entry;
+ char *name;
+ int argc;
+ char **argv;
+};
+TAILQ_HEAD(spawn_list, spawn_prog);
+struct spawn_list spawns = TAILQ_HEAD_INITIALIZER(spawns);
+
+int
+spawn_expand(struct swm_region *r, union arg *args, char *spawn_name,
+ char ***ret_args)
+{
+ struct spawn_prog *prog = NULL;
+ int i;
+ char *ap, **real_args;
+
+ DNPRINTF(SWM_D_SPAWN, "spawn_expand: %s\n", spawn_name);
+
+ /* find program */
+ TAILQ_FOREACH(prog, &spawns, entry) {
+ if (!strcasecmp(spawn_name, prog->name))
+ break;
+ }
+ if (prog == NULL) {
+ warnx("spawn_custom: program %s not found", spawn_name);
+ return (-1);
+ }
+
+ /* make room for expanded args */
+ if ((real_args = calloc(prog->argc + 1, sizeof(char *))) == NULL)
+ err(1, "spawn_custom: calloc real_args");
+
+ /* expand spawn_args into real_args */
+ for (i = 0; i < prog->argc; i++) {
+ ap = prog->argv[i];
+ DNPRINTF(SWM_D_SPAWN, "spawn_custom: raw arg: %s\n", ap);
+ if (!strcasecmp(ap, "$bar_border")) {
+ if ((real_args[i] =
+ strdup(r->s->c[SWM_S_COLOR_BAR_BORDER].name))
+ == NULL)
+ err(1, "spawn_custom border color");
+ } else if (!strcasecmp(ap, "$bar_color")) {
+ if ((real_args[i] =
+ strdup(r->s->c[SWM_S_COLOR_BAR].name))
+ == NULL)
+ err(1, "spawn_custom bar color");
+ } else if (!strcasecmp(ap, "$bar_font")) {
+ if ((real_args[i] = strdup(bar_fonts))
+ == NULL)
+ err(1, "spawn_custom bar fonts");
+ } else if (!strcasecmp(ap, "$bar_font_color")) {
+ if ((real_args[i] =
+ strdup(r->s->c[SWM_S_COLOR_BAR_FONT].name))
+ == NULL)
+ err(1, "spawn_custom color font");
+ } else if (!strcasecmp(ap, "$color_focus")) {
+ if ((real_args[i] =
+ strdup(r->s->c[SWM_S_COLOR_FOCUS].name))
+ == NULL)
+ err(1, "spawn_custom color focus");
+ } else if (!strcasecmp(ap, "$color_unfocus")) {
+ if ((real_args[i] =
+ strdup(r->s->c[SWM_S_COLOR_UNFOCUS].name))
+ == NULL)
+ err(1, "spawn_custom color unfocus");
+ } else {
+ /* no match --> copy as is */
+ if ((real_args[i] = strdup(ap)) == NULL)
+ err(1, "spawn_custom strdup(ap)");
+ }
+ DNPRINTF(SWM_D_SPAWN, "spawn_custom: cooked arg: %s\n",
+ real_args[i]);
+ }
+
+#ifdef SWM_DEBUG
+ DNPRINTF(SWM_D_SPAWN, "spawn_custom: result: ");
+ for (i = 0; i < prog->argc; i++)
+ DNPRINTF(SWM_D_SPAWN, "\"%s\" ", real_args[i]);
+ DNPRINTF(SWM_D_SPAWN, "\n");
+#endif
+ *ret_args = real_args;
+ return (prog->argc);
+}
+
+void
+spawn_custom(struct swm_region *r, union arg *args, char *spawn_name)
+{
+ union arg a;
+ char **real_args;
+ int spawn_argc, i;
+
+ if ((spawn_argc = spawn_expand(r, args, spawn_name, &real_args)) < 0)
+ return;
+ a.argv = real_args;
+ if (fork() == 0)
+ spawn(r->ws->idx, &a, 1);
+
+ for (i = 0; i < spawn_argc; i++)
+ free(real_args[i]);
+ free(real_args);
+}
+
+void
+spawn_select(struct swm_region *r, union arg *args, char *spawn_name, int *pid)
+{
+ union arg a;
+ char **real_args;
+ int i, spawn_argc;
+
+ if ((spawn_argc = spawn_expand(r, args, spawn_name, &real_args)) < 0)
+ return;
+ a.argv = real_args;
+
+ if (pipe(select_list_pipe) == -1)
+ err(1, "pipe error");
+ if (pipe(select_resp_pipe) == -1)
+ err(1, "pipe error");
+
+ if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
+ err(1, "could not disable SIGPIPE");
+ switch (*pid = fork()) {
+ case -1:
+ err(1, "cannot fork");
+ break;
+ case 0: /* child */
+ if (dup2(select_list_pipe[0], 0) == -1)
+ err(1, "dup2");
+ if (dup2(select_resp_pipe[1], 1) == -1)
+ err(1, "dup2");
+ close(select_list_pipe[1]);
+ close(select_resp_pipe[0]);
+ spawn(r->ws->idx, &a, 0);
+ break;
+ default: /* parent */
+ close(select_list_pipe[0]);
+ close(select_resp_pipe[1]);
+ break;
+ }
+
+ for (i = 0; i < spawn_argc; i++)
+ free(real_args[i]);
+ free(real_args);
+}
+
+void
+spawn_insert(char *name, char *args)
+{
+ char *arg, *cp, *ptr;
+ struct spawn_prog *sp;
+
+ DNPRINTF(SWM_D_SPAWN, "spawn_insert: %s\n", name);
+
+ if ((sp = calloc(1, sizeof *sp)) == NULL)
+ err(1, "spawn_insert: malloc");
+ if ((sp->name = strdup(name)) == NULL)
+ err(1, "spawn_insert: strdup");
+
+ /* convert the arguments to an argument list */
+ if ((ptr = cp = strdup(args)) == NULL)
+ err(1, "spawn_insert: strdup");
+ while ((arg = strsep(&ptr, " \t")) != NULL) {
+ /* empty field; skip it */
+ if (*arg == '\0')
+ continue;
+
+ sp->argc++;
+ if ((sp->argv = realloc(sp->argv, sp->argc *
+ sizeof *sp->argv)) == NULL)
+ err(1, "spawn_insert: realloc");
+ if ((sp->argv[sp->argc - 1] = strdup(arg)) == NULL)
+ err(1, "spawn_insert: strdup");
+ }
+ free(cp);
+
+ TAILQ_INSERT_TAIL(&spawns, sp, entry);
+ DNPRINTF(SWM_D_SPAWN, "spawn_insert: leave\n");
+}
+
+void
+spawn_remove(struct spawn_prog *sp)
+{
+ int i;
+
+ DNPRINTF(SWM_D_SPAWN, "spawn_remove: %s\n", sp->name);
+
+ TAILQ_REMOVE(&spawns, sp, entry);
+ for (i = 0; i < sp->argc; i++)
+ free(sp->argv[i]);
+ free(sp->argv);
+ free(sp->name);
+ free(sp);
+
+ DNPRINTF(SWM_D_SPAWN, "spawn_remove: leave\n");
+}
+
+void
+spawn_replace(struct spawn_prog *sp, char *name, char *args)
+{
+ DNPRINTF(SWM_D_SPAWN, "spawn_replace: %s [%s]\n", sp->name, name);
+
+ spawn_remove(sp);
+ spawn_insert(name, args);
+
+ DNPRINTF(SWM_D_SPAWN, "spawn_replace: leave\n");
+}
+
+void
+setspawn(char *name, char *args)
+{
+ struct spawn_prog *sp;
+
+ DNPRINTF(SWM_D_SPAWN, "setspawn: %s\n", name);
+
+ if (name == NULL)
+ return;
+
+ TAILQ_FOREACH(sp, &spawns, entry) {
+ if (!strcmp(sp->name, name)) {
+ if (*args == '\0')
+ spawn_remove(sp);
+ else
+ spawn_replace(sp, name, args);
+ DNPRINTF(SWM_D_SPAWN, "setspawn: leave\n");
+ return;
+ }
+ }
+ if (*args == '\0') {
+ warnx("error: setspawn: cannot find program: %s", name);
+ return;
+ }
+
+ spawn_insert(name, args);
+ DNPRINTF(SWM_D_SPAWN, "setspawn: leave\n");
+}
+
+int
+setconfspawn(char *selector, char *value, int flags)
+{
+ DNPRINTF(SWM_D_SPAWN, "setconfspawn: [%s] [%s]\n", selector, value);
+
+ setspawn(selector, value);
+
+ DNPRINTF(SWM_D_SPAWN, "setconfspawn: done\n");
+ return (0);
+}
+
+void
+setup_spawn(void)
+{
+ setconfspawn("term", "xterm", 0);
+ setconfspawn("screenshot_all", "screenshot.sh full", 0);
+ setconfspawn("screenshot_wind", "screenshot.sh window", 0);
+ setconfspawn("lock", "xlock", 0);
+ setconfspawn("initscr", "initscreen.sh", 0);
+ setconfspawn("menu", "dmenu_run"
+ " -fn $bar_font"
+ " -nb $bar_color"
+ " -nf $bar_font_color"
+ " -sb $bar_border"
+ " -sf $bar_color", 0);
+ setconfspawn("search", "dmenu"
+ " -i"
+ " -fn $bar_font"
+ " -nb $bar_color"
+ " -nf $bar_font_color"
+ " -sb $bar_border"
+ " -sf $bar_color", 0);
+ setconfspawn("name_workspace", "dmenu"
+ " -p Workspace"
+ " -fn $bar_font"
+ " -nb $bar_color"
+ " -nf $bar_font_color"
+ " -sb $bar_border"
+ " -sf $bar_color", 0);
+}
+
+/* key bindings */
+#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;
+ DNPRINTF(SWM_D_KEY, "parsekeys: enter [%s]\n", keystr);
+ if (mod == NULL || ks == NULL) {
+ DNPRINTF(SWM_D_KEY, "parsekeys: no mod or key vars\n");
+ return (1);
+ }
+ if (keystr == NULL || strlen(keystr) == 0) {
+ DNPRINTF(SWM_D_KEY, "parsekeys: no keystr\n");
+ return (1);
+ }
+ cp = keystr;
+ *ks = NoSymbol;
+ *mod = 0;
+ while ((name = strsep(&cp, SWM_KEY_WS)) != NULL) {
+ DNPRINTF(SWM_D_KEY, "parsekeys: key [%s]\n", name);
+ 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 if (strncasecmp(name, "CONTROL", SWM_MODNAME_SIZE) == 0)
+ *mod |= ControlMask;
+ else {
+ *ks = XStringToKeysym(name);
+ XConvertCase(*ks, ks, &uks);
+ if (ks == NoSymbol) {
+ DNPRINTF(SWM_D_KEY,
+ "parsekeys: invalid key %s\n",
+ name);
+ return (1);
+ }
+ }
+ }
+ DNPRINTF(SWM_D_KEY, "parsekeys: leave ok\n");
+ return (0);
+}
+
+char *
+strdupsafe(char *str)
+{
+ if (str == NULL)
+ return (NULL);
+ else
+ return (strdup(str));
+}
+
+void
+key_insert(unsigned int mod, KeySym ks, enum keyfuncid kfid, char *spawn_name)
+{
+ struct key *kp;
+
+ DNPRINTF(SWM_D_KEY, "key_insert: enter %s [%s]\n",
+ keyfuncs[kfid].name, spawn_name);
+
+ if ((kp = malloc(sizeof *kp)) == NULL)
+ err(1, "key_insert: malloc");
+
+ kp->mod = mod;
+ kp->keysym = ks;
+ kp->funcid = kfid;
+ kp->spawn_name = strdupsafe(spawn_name);
+ RB_INSERT(key_list, &keys, kp);
+
+ DNPRINTF(SWM_D_KEY, "key_insert: leave\n");
+}
+
+struct key *
+key_lookup(unsigned int mod, KeySym ks)
+{
+ struct key kp;
+
+ kp.keysym = ks;
+ kp.mod = mod;
+
+ return (RB_FIND(key_list, &keys, &kp));
+}
+
+void
+key_remove(struct key *kp)
+{
+ DNPRINTF(SWM_D_KEY, "key_remove: %s\n", keyfuncs[kp->funcid].name);
+
+ RB_REMOVE(key_list, &keys, kp);
+ free(kp->spawn_name);
+ free(kp);
+
+ DNPRINTF(SWM_D_KEY, "key_remove: leave\n");
+}
+
+void
+key_replace(struct key *kp, unsigned int mod, KeySym ks, enum keyfuncid kfid,
+ char *spawn_name)
+{
+ DNPRINTF(SWM_D_KEY, "key_replace: %s [%s]\n", keyfuncs[kp->funcid].name,
+ spawn_name);
+
+ key_remove(kp);
+ key_insert(mod, ks, kfid, spawn_name);
+
+ DNPRINTF(SWM_D_KEY, "key_replace: leave\n");
+}
+
+void
+setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid,
+ char *spawn_name)
+{
+ struct key *kp;
+
+ DNPRINTF(SWM_D_KEY, "setkeybinding: enter %s [%s]\n",
+ keyfuncs[kfid].name, spawn_name);
+
+ if ((kp = key_lookup(mod, ks)) != NULL) {
+ if (kfid == kf_invalid)
+ key_remove(kp);
+ else
+ key_replace(kp, mod, ks, kfid, spawn_name);
+ DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
+ return;
+ }
+ if (kfid == kf_invalid) {
+ warnx("error: setkeybinding: cannot find mod/key combination");
+ DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
+ return;
+ }
+
+ key_insert(mod, ks, kfid, spawn_name);
+ DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
+}
+
+int
+setconfbinding(char *selector, char *value, int flags)
+{
+ enum keyfuncid kfid;
+ unsigned int mod;
+ KeySym ks;
+ struct spawn_prog *sp;
+ DNPRINTF(SWM_D_KEY, "setconfbinding: enter\n");
+ if (selector == NULL) {
+ DNPRINTF(SWM_D_KEY, "setconfbinding: unbind %s\n", value);
+ if (parsekeys(value, mod_key, &mod, &ks) == 0) {
+ kfid = kf_invalid;
+ setkeybinding(mod, ks, kfid, NULL);
+ return (0);
+ } else
+ return (1);
+ }
+ /* search by key function name */
+ for (kfid = 0; kfid < kf_invalid; (kfid)++) {
+ if (strncasecmp(selector, keyfuncs[kfid].name,
+ SWM_FUNCNAME_LEN) == 0) {
+ DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match\n",
+ selector);
+ if (parsekeys(value, mod_key, &mod, &ks) == 0) {
+ setkeybinding(mod, ks, kfid, NULL);
+ return (0);
+ } else
+ return (1);
+ }
+ }
+ /* search by custom spawn name */
+ TAILQ_FOREACH(sp, &spawns, entry) {
+ if (strcasecmp(selector, sp->name) == 0) {
+ DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match\n",
+ selector);
+ if (parsekeys(value, mod_key, &mod, &ks) == 0) {
+ setkeybinding(mod, ks, kf_spawn_custom,
+ sp->name);
+ return (0);
+ } else
+ return (1);
+ }
+ }
+ DNPRINTF(SWM_D_KEY, "setconfbinding: no match\n");
+ return (1);
+}
+
+void
+setup_keys(void)
+{
+ setkeybinding(MODKEY, XK_space, kf_cycle_layout,NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_backslash, kf_flip_layout, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_space, kf_stack_reset, NULL);
+ setkeybinding(MODKEY, XK_h, kf_master_shrink,NULL);
+ setkeybinding(MODKEY, XK_l, kf_master_grow, NULL);
+ setkeybinding(MODKEY, XK_comma, kf_master_add, NULL);
+ setkeybinding(MODKEY, XK_period, kf_master_del, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_comma, kf_stack_inc, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_period, kf_stack_dec, NULL);
+ setkeybinding(MODKEY, XK_Return, kf_swap_main, NULL);
+ setkeybinding(MODKEY, XK_j, kf_focus_next, NULL);
+ setkeybinding(MODKEY, XK_k, kf_focus_prev, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_j, kf_swap_next, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_k, kf_swap_prev, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_Return, kf_spawn_term, NULL);
+ setkeybinding(MODKEY, XK_p, kf_spawn_custom,"menu");
+ setkeybinding(MODKEY|ShiftMask, XK_q, kf_quit, NULL);
+ setkeybinding(MODKEY, XK_q, kf_restart, NULL);
+ setkeybinding(MODKEY, XK_m, kf_focus_main, NULL);
+ setkeybinding(MODKEY, XK_1, kf_ws_1, NULL);
+ setkeybinding(MODKEY, XK_2, kf_ws_2, NULL);
+ setkeybinding(MODKEY, XK_3, kf_ws_3, NULL);
+ setkeybinding(MODKEY, XK_4, kf_ws_4, NULL);
+ setkeybinding(MODKEY, XK_5, kf_ws_5, NULL);
+ setkeybinding(MODKEY, XK_6, kf_ws_6, NULL);
+ setkeybinding(MODKEY, XK_7, kf_ws_7, NULL);
+ setkeybinding(MODKEY, XK_8, kf_ws_8, NULL);
+ setkeybinding(MODKEY, XK_9, kf_ws_9, NULL);
+ setkeybinding(MODKEY, XK_0, kf_ws_10, NULL);
+ setkeybinding(MODKEY, XK_Right, kf_ws_next, NULL);
+ setkeybinding(MODKEY, XK_Left, kf_ws_prev, NULL);
+ setkeybinding(MODKEY, XK_Up, kf_ws_next_all, NULL);
+ setkeybinding(MODKEY, XK_Down, kf_ws_prev_all, NULL);
+ setkeybinding(MODKEY, XK_a, kf_ws_prior, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_Right, kf_screen_next, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_Left, kf_screen_prev, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_1, kf_mvws_1, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_2, kf_mvws_2, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_3, kf_mvws_3, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_4, kf_mvws_4, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_5, kf_mvws_5, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_6, kf_mvws_6, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_7, kf_mvws_7, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_8, kf_mvws_8, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_9, kf_mvws_9, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_0, kf_mvws_10, NULL);
+ setkeybinding(MODKEY, XK_b, kf_bar_toggle, NULL);
+ setkeybinding(MODKEY, XK_Tab, kf_focus_next, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_Tab, kf_focus_prev, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_x, kf_wind_kill, NULL);
+ setkeybinding(MODKEY, XK_x, kf_wind_del, NULL);
+ setkeybinding(MODKEY, XK_s, kf_spawn_custom,"screenshot_all");
+ setkeybinding(MODKEY|ShiftMask, XK_s, kf_spawn_custom,"screenshot_wind");
+ setkeybinding(MODKEY, XK_t, kf_float_toggle,NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_v, kf_version, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_Delete, kf_spawn_custom,"lock");
+ setkeybinding(MODKEY|ShiftMask, XK_i, kf_spawn_custom,"initscr");
+ setkeybinding(MODKEY, XK_w, kf_iconify, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_w, kf_uniconify, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_r, kf_raise_toggle,NULL);
+ setkeybinding(MODKEY, XK_v, kf_button2, NULL);
+ setkeybinding(MODKEY, XK_equal, kf_width_grow, NULL);
+ setkeybinding(MODKEY, XK_minus, kf_width_shrink,NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_equal, kf_height_grow, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_minus, kf_height_shrink,NULL);
+ setkeybinding(MODKEY, XK_bracketleft, kf_move_left, NULL);
+ setkeybinding(MODKEY, XK_bracketright,kf_move_right, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_bracketleft, kf_move_up, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_bracketright,kf_move_down, NULL);
+ setkeybinding(MODKEY|ShiftMask, XK_slash, kf_name_workspace,NULL);
+ setkeybinding(MODKEY, XK_slash, kf_search_workspace,NULL);
+ setkeybinding(MODKEY, XK_f, kf_search_win, NULL);
+#ifdef SWM_DEBUG
+ setkeybinding(MODKEY|ShiftMask, XK_d, kf_dumpwins, NULL);
+#endif
+}
+
+void
+clear_keys(void)
+{
+ struct key *kp;
+
+ while (RB_EMPTY(&keys) == 0) {
+ kp = RB_ROOT(&keys);
+ key_remove(kp);
+ }
+}
+
+int
+setkeymapping(char *selector, char *value, int flags)
+{
+ char keymapping_file[PATH_MAX];
+ DNPRINTF(SWM_D_KEY, "setkeymapping: enter\n");
+ if (value[0] == '~')
+ snprintf(keymapping_file, sizeof keymapping_file, "%s/%s",
+ pwd->pw_dir, &value[1]);
+ else
+ strlcpy(keymapping_file, value, sizeof keymapping_file);
+ clear_keys();
+ /* load new key bindings; if it fails, revert to default bindings */
+ if (conf_load(keymapping_file, SWM_CONF_KEYMAPPING)) {
+ clear_keys();
+ setup_keys();
+ }
+ DNPRINTF(SWM_D_KEY, "setkeymapping: leave\n");
+ return (0);
+}
+
+void
+updatenumlockmask(void)
+{
+ unsigned int i, j;
+ XModifierKeymap *modmap;
+
+ DNPRINTF(SWM_D_MISC, "updatenumlockmask\n");
+ numlockmask = 0;
+ modmap = XGetModifierMapping(display);
+ for (i = 0; i < 8; i++)
+ for (j = 0; j < modmap->max_keypermod; j++)
+ if (modmap->modifiermap[i * modmap->max_keypermod + j]
+ == XKeysymToKeycode(display, XK_Num_Lock))
+ numlockmask = (1 << i);
+
+ XFreeModifiermap(modmap);
+}
+
+void
+grabkeys(void)
+{
+ unsigned int j, k;
+ KeyCode code;
+ unsigned int modifiers[] =
+ { 0, LockMask, numlockmask, numlockmask | LockMask };
+ struct key *kp;
+
+ DNPRINTF(SWM_D_MISC, "grabkeys\n");
+ updatenumlockmask();
+
+ for (k = 0; k < ScreenCount(display); k++) {
+ if (TAILQ_EMPTY(&screens[k].rl))
+ continue;
+ XUngrabKey(display, AnyKey, AnyModifier, screens[k].root);
+ RB_FOREACH(kp, key_list, &keys) {
+ if ((code = XKeysymToKeycode(display, kp->keysym)))
+ for (j = 0; j < LENGTH(modifiers); j++)
+ XGrabKey(display, code,
+ kp->mod | modifiers[j],
+ screens[k].root, True,
+ GrabModeAsync, GrabModeAsync);
+ }
+ }
+}
+
+void
+grabbuttons(struct ws_win *win, int focused)
+{
+ unsigned int i, j;
+ unsigned int modifiers[] =
+ { 0, LockMask, numlockmask, numlockmask|LockMask };
+
+ updatenumlockmask();
+ XUngrabButton(display, AnyButton, AnyModifier, win->id);
+ if (focused) {
+ for (i = 0; i < LENGTH(buttons); i++)
+ if (buttons[i].action == client_click)
+ for (j = 0; j < LENGTH(modifiers); j++)
+ XGrabButton(display, buttons[i].button,
+ buttons[i].mask | modifiers[j],
+ win->id, False, BUTTONMASK,
+ GrabModeAsync, GrabModeSync, None,
+ None);
+ } else
+ XGrabButton(display, AnyButton, AnyModifier, win->id, False,
+ BUTTONMASK, GrabModeAsync, GrabModeSync, None, None);
+}
+
+const char *quirkname[] = {
+ "NONE", /* config string for "no value" */
+ "FLOAT",
+ "TRANSSZ",
+ "ANYWHERE",
+ "XTERM_FONTADJ",
+ "FULLSCREEN",
+ "FOCUSPREV",
+};
+
+/* SWM_Q_WS: retain '|' for back compat for now (2009-08-11) */
+#define SWM_Q_WS "\n|+ \t"
+int
+parsequirks(char *qstr, unsigned long *quirk)
+{
+ char *cp, *name;
+ int i;
+
+ if (quirk == NULL)
+ return (1);
+
+ cp = qstr;
+ *quirk = 0;
+ while ((name = strsep(&cp, SWM_Q_WS)) != NULL) {
+ if (cp)
+ cp += (long)strspn(cp, SWM_Q_WS);
+ for (i = 0; i < LENGTH(quirkname); i++) {
+ if (!strncasecmp(name, quirkname[i], SWM_QUIRK_LEN)) {
+ DNPRINTF(SWM_D_QUIRK,
+ "parsequirks: %s\n", name);
+ if (i == 0) {
+ *quirk = 0;
+ return (0);
+ }
+ *quirk |= 1 << (i-1);
+ break;