+ unsigned int mask;
+ XWindowChanges wc;
+
+ bzero(&wc, sizeof wc);
+ mask = CWBorderWidth | CWWidth | CWHeight | CWX | CWY;
+ wc.border_width = border_width;
+ wc.x = win->g.x;
+ wc.y = win->g.y;
+ wc.width = win->g.w;
+ wc.height = win->g.h;
+
+ DNPRINTF(SWM_D_STACK, "update_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);
+}
+
+#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: win %lu floating %d trans %lu\n",
+ win->id, 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:
+ win->g.w -= SWM_RESIZE_STEPS;
+ resize_step = 1;
+ break;
+ case SWM_ARG_ID_WIDTHGROW:
+ win->g.w += SWM_RESIZE_STEPS;
+ resize_step = 1;
+ break;
+ case SWM_ARG_ID_HEIGHTSHRINK:
+ win->g.h -= SWM_RESIZE_STEPS;
+ resize_step = 1;
+ break;
+ case SWM_ARG_ID_HEIGHTGROW:
+ win->g.h += 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 < win->g.w / 2)
+ left = 1;
+
+ if (wy < win->g.h / 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;
+
+ win->g.y = g.y - dy;
+ win->g.h = g.h + 2 * dy;
+ } else {
+ if (g.h + dy < 1)
+ dy = 1 - g.h;
+
+ if (top)
+ win->g.y = g.y - dy;
+
+ win->g.h = 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;
+
+ win->g.x = g.x - dx;
+ win->g.w = g.w + 2 * dx;
+ } else {
+ if (g.w + dx < 1)
+ dx = 1 - g.w;
+
+ if (left)
+ win->g.x = g.x - dx;
+
+ win->g.w = 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: win %lu floating %d trans %lu\n",
+ win->id, 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) {
+ 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:
+ win->g.x -= (SWM_MOVE_STEPS - border_width);
+ move_step = 1;
+ break;
+ case SWM_ARG_ID_MOVERIGHT:
+ win->g.x += (SWM_MOVE_STEPS - border_width);
+ move_step = 1;
+ break;
+ case SWM_ARG_ID_MOVEUP:
+ win->g.y -= (SWM_MOVE_STEPS - border_width);
+ move_step = 1;
+ break;
+ case SWM_ARG_ID_MOVEDOWN:
+ win->g.y += (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:
+ win->g.x = ev.xmotion.x_root - wx - border_width;
+ win->g.y = 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_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} },
+ { "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 {
+ unsigned int mod;
+ KeySym keysym;
+ enum keyfuncid funcid;
+ char *spawn_name;
+};
+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;
+
+ mod_key = mod;
+ 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;
+}
+
+/* spawn */
+struct spawn_prog {
+ char *name;
+ int argc;
+ char **argv;
+};
+
+int spawns_size = 0, spawns_length = 0;
+struct spawn_prog *spawns = NULL;
+
+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 */
+ for (i = 0; i < spawns_length; i++) {
+ if (!strcasecmp(spawn_name, spawns[i].name))
+ prog = &spawns[i];
+ }
+ if (prog == NULL) {
+ fprintf(stderr, "spawn_custom: program %s not found\n",
+ 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[bar_fidx]))
+ == 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
+ if ((swm_debug & SWM_D_SPAWN) != 0) {
+ fprintf(stderr, "spawn_custom: result = ");
+ for (i = 0; i < prog->argc; i++)
+ fprintf(stderr, "\"%s\" ", real_args[i]);
+ fprintf(stderr, "\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
+setspawn(struct spawn_prog *prog)
+{
+ int i, j;
+
+ if (prog == NULL || prog->name == NULL)
+ return;
+
+ /* find existing */
+ for (i = 0; i < spawns_length; i++) {
+ if (!strcmp(spawns[i].name, prog->name)) {
+ /* found */
+ if (prog->argv == NULL) {
+ /* delete */
+ DNPRINTF(SWM_D_SPAWN,
+ "setspawn: delete #%d %s\n",
+ i, spawns[i].name);
+ free(spawns[i].name);
+ for (j = 0; j < spawns[i].argc; j++)
+ free(spawns[i].argv[j]);
+ free(spawns[i].argv);
+ j = spawns_length - 1;
+ if (i < j)
+ spawns[i] = spawns[j];
+ spawns_length--;
+ free(prog->name);
+ } else {
+ /* replace */
+ DNPRINTF(SWM_D_SPAWN,
+ "setspawn: replace #%d %s\n",
+ i, spawns[i].name);
+ free(spawns[i].name);
+ for (j = 0; j < spawns[i].argc; j++)
+ free(spawns[i].argv[j]);
+ free(spawns[i].argv);
+ spawns[i] = *prog;
+ }
+ /* found case handled */
+ free(prog);
+ return;
+ }
+ }
+
+ if (prog->argv == NULL) {
+ fprintf(stderr,
+ "error: setspawn: cannot find program %s", prog->name);
+ free(prog);
+ return;
+ }
+
+ /* not found: add */
+ if (spawns_size == 0 || spawns == NULL) {
+ spawns_size = 4;
+ DNPRINTF(SWM_D_SPAWN, "setspawn: init list %d\n", spawns_size);
+ spawns = malloc((size_t)spawns_size *
+ sizeof(struct spawn_prog));
+ if (spawns == NULL) {
+ fprintf(stderr, "setspawn: malloc failed\n");
+ perror(" failed");
+ quit(NULL, NULL);
+ }
+ } else if (spawns_length == spawns_size) {
+ spawns_size *= 2;
+ DNPRINTF(SWM_D_SPAWN, "setspawn: grow list %d\n", spawns_size);
+ spawns = realloc(spawns, (size_t)spawns_size *
+ sizeof(struct spawn_prog));
+ if (spawns == NULL) {
+ fprintf(stderr, "setspawn: realloc failed\n");
+ perror(" failed");
+ quit(NULL, NULL);
+ }
+ }
+
+ if (spawns_length < spawns_size) {
+ DNPRINTF(SWM_D_SPAWN, "setspawn: add #%d %s\n",
+ spawns_length, prog->name);
+ i = spawns_length++;
+ spawns[i] = *prog;
+ } else {
+ fprintf(stderr, "spawns array problem?\n");
+ if (spawns == NULL) {
+ fprintf(stderr, "spawns array is NULL!\n");
+ quit(NULL, NULL);
+ }
+ }
+ free(prog);
+}
+
+int
+setconfspawn(char *selector, char *value, int flags)
+{
+ struct spawn_prog *prog;
+ char *vp, *cp, *word;
+
+ DNPRINTF(SWM_D_SPAWN, "setconfspawn: [%s] [%s]\n", selector, value);
+ if ((prog = calloc(1, sizeof *prog)) == NULL)
+ err(1, "setconfspawn: calloc prog");
+ prog->name = strdup(selector);
+ if (prog->name == NULL)
+ err(1, "setconfspawn prog->name");
+ if ((cp = vp = strdup(value)) == NULL)
+ err(1, "setconfspawn: strdup(value) ");
+ while ((word = strsep(&cp, " \t")) != NULL) {
+ DNPRINTF(SWM_D_SPAWN, "setconfspawn: arg [%s]\n", word);
+ if (cp)
+ cp += (long)strspn(cp, " \t");
+ if (strlen(word) > 0) {
+ prog->argc++;
+ if ((prog->argv = realloc(prog->argv,
+ prog->argc * sizeof(char *))) == NULL)
+ err(1, "setconfspawn: realloc");
+ if ((prog->argv[prog->argc - 1] = strdup(word)) == NULL)
+ err(1, "setconfspawn: strdup");
+ }
+ }
+ free(vp);
+
+ setspawn(prog);
+
+ 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
+setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid,
+ char *spawn_name)
+{
+ int i, j;
+ DNPRINTF(SWM_D_KEY, "setkeybinding: enter %s [%s]\n",
+ keyfuncs[kfid].name, spawn_name);
+ /* find existing */
+ for (i = 0; i < keys_length; i++) {
+ if (keys[i].mod == mod && keys[i].keysym == ks) {
+ if (kfid == kf_invalid) {
+ /* found: delete */
+ DNPRINTF(SWM_D_KEY,
+ "setkeybinding: delete #%d %s\n",
+ i, keyfuncs[keys[i].funcid].name);
+ free(keys[i].spawn_name);
+ j = keys_length - 1;
+ if (i < j)
+ keys[i] = keys[j];
+ keys_length--;
+ DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
+ return;
+ } else {
+ /* found: replace */
+ DNPRINTF(SWM_D_KEY,
+ "setkeybinding: replace #%d %s %s\n",
+ i, keyfuncs[keys[i].funcid].name,
+ spawn_name);
+ free(keys[i].spawn_name);
+ keys[i].mod = mod;
+ keys[i].keysym = ks;
+ keys[i].funcid = kfid;
+ keys[i].spawn_name = strdupsafe(spawn_name);
+ DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
+ return;
+ }
+ }
+ }
+ if (kfid == kf_invalid) {
+ fprintf(stderr,
+ "error: setkeybinding: cannot find mod/key combination");
+ DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
+ return;
+ }
+ /* not found: add */
+ if (keys_size == 0 || keys == NULL) {
+ keys_size = 4;
+ DNPRINTF(SWM_D_KEY, "setkeybinding: init list %d\n", keys_size);
+ keys = malloc((size_t)keys_size * sizeof(struct key));
+ if (keys == NULL) {
+ fprintf(stderr, "malloc failed\n");
+ perror(" failed");
+ quit(NULL, NULL);
+ }
+ } else if (keys_length == keys_size) {
+ keys_size *= 2;
+ DNPRINTF(SWM_D_KEY, "setkeybinding: grow list %d\n", keys_size);
+ keys = realloc(keys, (size_t)keys_size * sizeof(struct key));
+ if (keys == NULL) {
+ fprintf(stderr, "realloc failed\n");
+ perror(" failed");
+ quit(NULL, NULL);
+ }
+ }
+ if (keys_length < keys_size) {
+ j = keys_length++;
+ DNPRINTF(SWM_D_KEY, "setkeybinding: add #%d %s %s\n",
+ j, keyfuncs[kfid].name, spawn_name);
+ keys[j].mod = mod;
+ keys[j].keysym = ks;
+ keys[j].funcid = kfid;
+ keys[j].spawn_name = strdupsafe(spawn_name);
+ } else {
+ fprintf(stderr, "keys array problem?\n");
+ if (keys == NULL) {
+ fprintf(stderr, "keys array problem\n");
+ quit(NULL, NULL);
+ }
+ }
+ DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
+}
+
+int
+setconfbinding(char *selector, char *value, int flags)
+{
+ enum keyfuncid kfid;
+ unsigned int mod;
+ KeySym ks;
+ int i;
+ 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 */
+ for (i = 0; i < spawns_length; i++) {
+ if (strcasecmp(selector, spawns[i].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,
+ spawns[i].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_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)
+{
+ int i;
+
+ /* clear all key bindings, if any */
+ for (i = 0; i < keys_length; i++)
+ free(keys[i].spawn_name);
+ keys_length = 0;
+}
+
+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 i, j, k;
+ KeyCode code;
+ unsigned int modifiers[] =
+ { 0, LockMask, numlockmask, numlockmask | LockMask };
+
+ 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);
+ for (i = 0; i < keys_length; i++) {
+ if ((code = XKeysymToKeycode(display, keys[i].keysym)))
+ for (j = 0; j < LENGTH(modifiers); j++)
+ XGrabKey(display, code,
+ keys[i].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;
+ }
+ }
+ if (i >= LENGTH(quirkname)) {
+ DNPRINTF(SWM_D_QUIRK,
+ "parsequirks: invalid quirk [%s]\n", name);
+ return (1);
+ }
+ }
+ return (0);
+}
+
+void
+setquirk(const char *class, const char *name, const int quirk)
+{
+ int i, j;
+
+ /* find existing */
+ for (i = 0; i < quirks_length; i++) {
+ if (!strcmp(quirks[i].class, class) &&
+ !strcmp(quirks[i].name, name)) {
+ if (!quirk) {
+ /* found: delete */
+ DNPRINTF(SWM_D_QUIRK,
+ "setquirk: delete #%d %s:%s\n",
+ i, quirks[i].class, quirks[i].name);
+ free(quirks[i].class);
+ free(quirks[i].name);
+ j = quirks_length - 1;
+ if (i < j)
+ quirks[i] = quirks[j];
+ quirks_length--;
+ return;
+ } else {
+ /* found: replace */
+ DNPRINTF(SWM_D_QUIRK,
+ "setquirk: replace #%d %s:%s\n",
+ i, quirks[i].class, quirks[i].name);
+ free(quirks[i].class);
+ free(quirks[i].name);
+ quirks[i].class = strdup(class);
+ quirks[i].name = strdup(name);
+ quirks[i].quirk = quirk;
+ return;
+ }
+ }
+ }
+ if (!quirk) {
+ fprintf(stderr,
+ "error: setquirk: cannot find class/name combination");
+ return;
+ }
+ /* not found: add */
+ if (quirks_size == 0 || quirks == NULL) {
+ quirks_size = 4;
+ DNPRINTF(SWM_D_QUIRK, "setquirk: init list %d\n", quirks_size);
+ quirks = malloc((size_t)quirks_size * sizeof(struct quirk));
+ if (quirks == NULL) {
+ fprintf(stderr, "setquirk: malloc failed\n");
+ perror(" failed");
+ quit(NULL, NULL);
+ }
+ } else if (quirks_length == quirks_size) {
+ quirks_size *= 2;
+ DNPRINTF(SWM_D_QUIRK, "setquirk: grow list %d\n", quirks_size);
+ quirks = realloc(quirks,
+ (size_t)quirks_size * sizeof(struct quirk));
+ if (quirks == NULL) {
+ fprintf(stderr, "setquirk: realloc failed\n");
+ perror(" failed");
+ quit(NULL, NULL);
+ }
+ }
+ if (quirks_length < quirks_size) {
+ DNPRINTF(SWM_D_QUIRK, "setquirk: add %d\n", quirks_length);
+ j = quirks_length++;
+ quirks[j].class = strdup(class);
+ quirks[j].name = strdup(name);
+ quirks[j].quirk = quirk;
+ } else {
+ fprintf(stderr, "quirks array problem?\n");
+ if (quirks == NULL) {
+ fprintf(stderr, "quirks array problem!\n");
+ quit(NULL, NULL);
+ }
+ }
+}
+
+int
+setconfquirk(char *selector, char *value, int flags)
+{
+ char *cp, *class, *name;
+ int retval;
+ unsigned long quirks;
+ if (selector == NULL)
+ return (0);
+ if ((cp = strchr(selector, ':')) == NULL)
+ return (0);
+ *cp = '\0';
+ class = selector;
+ name = cp + 1;
+ if ((retval = parsequirks(value, &quirks)) == 0)
+ setquirk(class, name, quirks);
+ return (retval);
+}
+
+void
+setup_quirks(void)
+{
+ setquirk("MPlayer", "xv", SWM_Q_FLOAT | SWM_Q_FULLSCREEN | SWM_Q_FOCUSPREV);
+ setquirk("OpenOffice.org 3.2", "VCLSalFrame", SWM_Q_FLOAT);
+ setquirk("Firefox-bin", "firefox-bin", SWM_Q_TRANSSZ);
+ setquirk("Firefox", "Dialog", SWM_Q_FLOAT);
+ setquirk("Gimp", "gimp", SWM_Q_FLOAT | SWM_Q_ANYWHERE);
+ setquirk("XTerm", "xterm", SWM_Q_XTERM_FONTADJ);
+ setquirk("xine", "Xine Window", SWM_Q_FLOAT | SWM_Q_ANYWHERE);
+ setquirk("Xitk", "Xitk Combo", SWM_Q_FLOAT | SWM_Q_ANYWHERE);
+ setquirk("xine", "xine Panel", SWM_Q_FLOAT | SWM_Q_ANYWHERE);
+ setquirk("Xitk", "Xine Window", SWM_Q_FLOAT | SWM_Q_ANYWHERE);
+ setquirk("xine", "xine Video Fullscreen Window", SWM_Q_FULLSCREEN | SWM_Q_FLOAT);
+ setquirk("pcb", "pcb", SWM_Q_FLOAT);
+ setquirk("SDL_App", "SDL_App", SWM_Q_FLOAT | SWM_Q_FULLSCREEN);
+}
+
+/* conf file stuff */
+#define SWM_CONF_FILE "scrotwm.conf"
+
+enum { SWM_S_BAR_DELAY, SWM_S_BAR_ENABLED, SWM_S_BAR_BORDER_WIDTH,
+ SWM_S_STACK_ENABLED, SWM_S_CLOCK_ENABLED, SWM_S_CLOCK_FORMAT,
+ SWM_S_CYCLE_EMPTY, SWM_S_CYCLE_VISIBLE, SWM_S_SS_ENABLED,
+ SWM_S_TERM_WIDTH, SWM_S_TITLE_CLASS_ENABLED,
+ SWM_S_TITLE_NAME_ENABLED, SWM_S_WINDOW_NAME_ENABLED, SWM_S_URGENT_ENABLED,
+ SWM_S_FOCUS_MODE, SWM_S_DISABLE_BORDER, SWM_S_BORDER_WIDTH,
+ SWM_S_BAR_FONT, SWM_S_BAR_ACTION, SWM_S_SPAWN_TERM,
+ SWM_S_SS_APP, SWM_S_DIALOG_RATIO, SWM_S_BAR_AT_BOTTOM,
+ SWM_S_VERBOSE_LAYOUT, SWM_S_BAR_JUSTIFY
+ };
+
+int
+setconfvalue(char *selector, char *value, int flags)
+{
+ int i;
+
+ switch (flags) {
+ case SWM_S_BAR_DELAY:
+ bar_delay = atoi(value);
+ break;
+ case SWM_S_BAR_ENABLED:
+ bar_enabled = atoi(value);
+ break;
+ case SWM_S_BAR_BORDER_WIDTH:
+ bar_border_width = atoi(value);
+ break;
+ case SWM_S_BAR_AT_BOTTOM:
+ bar_at_bottom = atoi(value);
+ break;
+ case SWM_S_BAR_JUSTIFY:
+ if (!strcmp(value, "left"))
+ bar_justify = SWM_BAR_JUSTIFY_LEFT;
+ else if (!strcmp(value, "center"))
+ bar_justify = SWM_BAR_JUSTIFY_CENTER;
+ else if (!strcmp(value, "right"))
+ bar_justify = SWM_BAR_JUSTIFY_RIGHT;
+ else
+ errx(1, "invalid bar_justify");
+ break;
+ case SWM_S_STACK_ENABLED:
+ stack_enabled = atoi(value);
+ break;
+ case SWM_S_CLOCK_ENABLED:
+ clock_enabled = atoi(value);
+ break;
+ case SWM_S_CLOCK_FORMAT:
+#ifndef SWM_DENY_CLOCK_FORMAT
+ free(clock_format);
+ if ((clock_format = strdup(value)) == NULL)
+ err(1, "setconfvalue: clock_format");
+#endif
+ break;
+ case SWM_S_CYCLE_EMPTY:
+ cycle_empty = atoi(value);
+ break;
+ case SWM_S_CYCLE_VISIBLE:
+ cycle_visible = atoi(value);
+ break;
+ case SWM_S_SS_ENABLED:
+ ss_enabled = atoi(value);
+ break;
+ case SWM_S_TERM_WIDTH:
+ term_width = atoi(value);
+ break;
+ case SWM_S_TITLE_CLASS_ENABLED:
+ title_class_enabled = atoi(value);
+ break;
+ case SWM_S_WINDOW_NAME_ENABLED:
+ window_name_enabled = atoi(value);
+ break;
+ case SWM_S_TITLE_NAME_ENABLED:
+ title_name_enabled = atoi(value);
+ break;
+ case SWM_S_URGENT_ENABLED:
+ urgent_enabled = atoi(value);
+ break;
+ case SWM_S_FOCUS_MODE:
+ if (!strcmp(value, "default"))
+ focus_mode = SWM_FOCUS_DEFAULT;
+ else if (!strcmp(value, "follow_cursor"))
+ focus_mode = SWM_FOCUS_FOLLOW;
+ else if (!strcmp(value, "synergy"))
+ focus_mode = SWM_FOCUS_SYNERGY;
+ else
+ errx(1, "focus_mode");
+ break;
+ case SWM_S_DISABLE_BORDER:
+ disable_border = atoi(value);
+ break;
+ case SWM_S_BORDER_WIDTH:
+ border_width = atoi(value);
+ break;
+ case SWM_S_BAR_FONT:
+ free(bar_fonts[0]);
+ if ((bar_fonts[0] = strdup(value)) == NULL)
+ err(1, "setconfvalue: bar_font");
+ break;
+ case SWM_S_BAR_ACTION:
+ free(bar_argv[0]);
+ if ((bar_argv[0] = strdup(value)) == NULL)
+ err(1, "setconfvalue: bar_action");
+ break;
+ case SWM_S_SPAWN_TERM:
+ free(spawn_term[0]);
+ if ((spawn_term[0] = strdup(value)) == NULL)
+ err(1, "setconfvalue: spawn_term");
+ break;
+ case SWM_S_SS_APP:
+ break;
+ case SWM_S_DIALOG_RATIO:
+ dialog_ratio = atof(value);
+ if (dialog_ratio > 1.0 || dialog_ratio <= .3)
+ dialog_ratio = .6;
+ break;
+ case SWM_S_VERBOSE_LAYOUT:
+ verbose_layout = atoi(value);
+ for (i = 0; layouts[i].l_stack != NULL; i++) {
+ if (verbose_layout)
+ layouts[i].l_string = fancy_stacker;
+ else
+ layouts[i].l_string = plain_stacker;
+ }
+ break;
+ default:
+ return (1);
+ }
+ return (0);
+}
+
+int
+setconfmodkey(char *selector, char *value, int flags)
+{
+ if (!strncasecmp(value, "Mod1", strlen("Mod1")))
+ update_modkey(Mod1Mask);
+ else if (!strncasecmp(value, "Mod2", strlen("Mod2")))
+ update_modkey(Mod2Mask);
+ else if (!strncasecmp(value, "Mod3", strlen("Mod3")))
+ update_modkey(Mod3Mask);
+ else if (!strncasecmp(value, "Mod4", strlen("Mod4")))
+ update_modkey(Mod4Mask);
+ else
+ return (1);
+ return (0);
+}
+
+int
+setconfcolor(char *selector, char *value, int flags)
+{
+ setscreencolor(value, ((selector == NULL)?-1:atoi(selector)), flags);
+ return (0);
+}
+
+int
+setconfregion(char *selector, char *value, int flags)
+{
+ custom_region(value);
+ return (0);
+}
+
+int
+setautorun(char *selector, char *value, int flags)
+{
+ int ws_id;
+ char s[1024];
+ char *ap, *sp = s;
+ union arg a;
+ int argc = 0;
+ long pid;
+ struct pid_e *p;
+
+ if (getenv("SWM_STARTED"))
+ return (0);
+
+ bzero(s, sizeof s);
+ if (sscanf(value, "ws[%d]:%1023c", &ws_id, s) != 2)
+ errx(1, "invalid autorun entry, should be 'ws[<idx>]:command'");
+ ws_id--;
+ if (ws_id < 0 || ws_id >= SWM_WS_MAX)
+ errx(1, "autorun: invalid workspace %d", ws_id + 1);
+
+ /*
+ * This is a little intricate
+ *
+ * If the pid already exists we simply reuse it because it means it was
+ * used before AND not claimed by manage_window. We get away with
+ * altering it in the parent after INSERT because this can not be a race
+ */
+ a.argv = NULL;
+ while ((ap = strsep(&sp, " \t")) != NULL) {
+ if (*ap == '\0')
+ continue;
+ DNPRINTF(SWM_D_SPAWN, "setautorun: arg [%s]\n", ap);
+ argc++;
+ if ((a.argv = realloc(a.argv, argc * sizeof(char *))) == NULL)
+ err(1, "setautorun: realloc");
+ a.argv[argc - 1] = ap;
+ }
+
+ if ((a.argv = realloc(a.argv, (argc + 1) * sizeof(char *))) == NULL)
+ err(1, "setautorun: realloc");
+ a.argv[argc] = NULL;
+
+ if ((pid = fork()) == 0) {
+ spawn(ws_id, &a, 1);
+ /* NOTREACHED */
+ _exit(1);
+ }
+ free(a.argv);
+
+ /* parent */
+ p = find_pid(pid);
+ if (p == NULL) {
+ p = calloc(1, sizeof *p);
+ if (p == NULL)
+ return (1);
+ TAILQ_INSERT_TAIL(&pidlist, p, entry);
+ }