X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=spectrwm.c;h=1e205629f17ef33e09bc5ebed37c7e7abacfa722;hb=cfe055c6fc663ae4f4ab2520ba076c2409552dac;hp=a565162aec00cea1fae1f9257bbba4d4f3d2a145;hpb=9d5207775be4ae435c7836ce59cba4fa0f8ecaac;p=spectrwm.git diff --git a/spectrwm.c b/spectrwm.c index a565162..1e20562 100644 --- a/spectrwm.c +++ b/spectrwm.c @@ -5,10 +5,10 @@ * Copyright (c) 2009 Pierre-Yves Ritschard * Copyright (c) 2010 Tuukka Kataja * Copyright (c) 2011 Jason L. Wright - * Copyright (c) 2011-2012 Reginald Kennedy + * Copyright (c) 2011-2013 Reginald Kennedy * Copyright (c) 2011-2012 Lawrence Teo * Copyright (c) 2011-2012 Tiago Cunha - * Copyright (c) 2012 David Hill + * Copyright (c) 2012-2013 David Hill * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -577,6 +577,8 @@ union arg { #define SWM_ARG_ID_CYCLERG_DOWN (43) #define SWM_ARG_ID_CYCLEWS_UP_ALL (44) #define SWM_ARG_ID_CYCLEWS_DOWN_ALL (45) +#define SWM_ARG_ID_CYCLEWS_MOVE_UP (46) +#define SWM_ARG_ID_CYCLEWS_MOVE_DOWN (47) #define SWM_ARG_ID_STACKINC (50) #define SWM_ARG_ID_STACKDEC (51) #define SWM_ARG_ID_SS_ALL (60) @@ -610,6 +612,8 @@ struct quirk { #define SWM_Q_XTERM_FONTADJ (1<<3) /* adjust xterm fonts when resizing */ #define SWM_Q_FULLSCREEN (1<<4) /* remove border */ #define SWM_Q_FOCUSPREV (1<<5) /* focus on caller */ +#define SWM_Q_NOFOCUSONMAP (1<<6) /* Don't focus on window when mapped. */ +#define SWM_Q_FOCUSONMAP_SINGLE (1<<7) /* Only focus if single win of type. */ }; TAILQ_HEAD(quirk_list, quirk); struct quirk_list quirks = TAILQ_HEAD_INITIALIZER(quirks); @@ -707,12 +711,15 @@ struct cursors { {"top_right_corner", XC_top_right_corner, XCB_CURSOR_NONE}, }; +#define SWM_SPAWN_OPTIONAL 0x1 + /* spawn */ struct spawn_prog { TAILQ_ENTRY(spawn_prog) entry; char *name; int argc; char **argv; + int flags; }; TAILQ_HEAD(spawn_list, spawn_prog); struct spawn_list spawns = TAILQ_HEAD_INITIALIZER(spawns); @@ -826,8 +833,10 @@ enum keyfuncid { KF_WS_22, KF_WS_NEXT, KF_WS_NEXT_ALL, + KF_WS_NEXT_MOVE, KF_WS_PREV, KF_WS_PREV_ALL, + KF_WS_PREV_MOVE, KF_WS_PRIOR, KF_DUMPWINS, /* MUST BE LAST */ KF_INVALID @@ -857,8 +866,7 @@ void bar_print(struct swm_region *, const char *); void bar_print_legacy(struct swm_region *, const char *); void bar_replace(char *, char *, struct swm_region *, size_t); void bar_replace_pad(char *, int *, size_t); -char * bar_replace_seq(char *, char *, struct swm_region *, size_t *, - size_t); +char *bar_replace_seq(char *, char *, struct swm_region *, size_t *, size_t); void bar_setup(struct swm_region *); void bar_title_name(char *, size_t, struct swm_region *); void bar_toggle(struct swm_region *, union arg *); @@ -928,6 +936,7 @@ struct ws_win *get_pointer_win(xcb_window_t); struct ws_win *get_region_focus(struct swm_region *); int get_region_index(struct swm_region *); xcb_screen_t *get_screen(int); +int get_screen_count(void); #ifdef SWM_DEBUG char *get_stack_mode_name(uint8_t); #endif @@ -961,7 +970,7 @@ void maprequest(xcb_map_request_event_t *); void motionnotify(xcb_motion_notify_event_t *); void move(struct ws_win *, union arg *); void move_step(struct swm_region *, union arg *); -uint32_t name_to_pixel(const char *); +uint32_t name_to_pixel(int, const char *); void name_workspace(struct swm_region *, union arg *); void new_region(struct swm_screen *, int, int, int, int); int parsekeys(char *, unsigned int, unsigned int *, KeySym *); @@ -986,10 +995,10 @@ struct swm_region *root_to_region(xcb_window_t, int); void screenchange(xcb_randr_screen_change_notify_event_t *); void scan_xrandr(int); void search_do_resp(void); -void search_resp_name_workspace(const char *, unsigned long); +void search_resp_name_workspace(const char *, size_t); void search_resp_search_window(const char *); void search_resp_search_workspace(const char *); -void search_resp_uniconify(const char *, unsigned long); +void search_resp_uniconify(const char *, size_t); void search_win(struct swm_region *, union arg *); void search_win_cleanup(void); void search_workspace(struct swm_region *, union arg *); @@ -1009,7 +1018,7 @@ int setkeymapping(char *, char *, int); int setlayout(char *, char *, int); void setquirk(const char *, const char *, unsigned long); void setscreencolor(char *, int, int); -void setspawn(const char *, const char *); +void setspawn(const char *, const char *, int); void setup_ewmh(void); void setup_globals(void); void setup_keys(void); @@ -1026,15 +1035,15 @@ void sort_windows(struct ws_win_list *); void spawn(int, union arg *, int); void spawn_custom(struct swm_region *, union arg *, const char *); int spawn_expand(struct swm_region *, union arg *, const char *, char ***); -void spawn_insert(const char *, const char *); +void spawn_insert(const char *, const char *, int); void spawn_remove(struct spawn_prog *); -void spawn_replace(struct spawn_prog *, const char *, const char *); +void spawn_replace(struct spawn_prog *, const char *, const char *, int); void spawn_select(struct swm_region *, union arg *, const char *, int *); void stack_config(struct swm_region *, union arg *); void stack_floater(struct ws_win *, struct swm_region *); void stack_master(struct workspace *, struct swm_geometry *, int, int); void store_float_geom(struct ws_win *, struct swm_region *); -char * strdupsafe(const char *); +char *strdupsafe(const char *); void swapwin(struct swm_region *, union arg *); void switchws(struct swm_region *, union arg *); void teardown_ewmh(void); @@ -1047,6 +1056,7 @@ void unmap_window(struct ws_win *); void updatenumlockmask(void); void update_modkey(unsigned int); void update_window(struct ws_win *); +void validate_spawns(void); int validate_win(struct ws_win *); int validate_ws(struct workspace *); /*void visibilitynotify(xcb_visibility_notify_event_t *);*/ @@ -1182,6 +1192,19 @@ get_screen(int screen) } int +get_screen_count(void) +{ + const xcb_setup_t *r; + + if ((r = xcb_get_setup(conn)) == NULL) { + DNPRINTF(SWM_D_MISC, "get_screen_count: xcb_get_setup\n"); + check_conn(); + } + + return xcb_setup_roots_length(r); +} + +int get_region_index(struct swm_region *r) { struct swm_region *rr; @@ -1275,7 +1298,7 @@ setup_ewmh(void) for (i = 0; i < LENGTH(ewmh); i++) ewmh[i].atom = get_atom_from_string(ewmh[i].name); - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { /* Support check window will be created by workaround(). */ @@ -1299,7 +1322,7 @@ teardown_ewmh(void) sup_check = get_atom_from_string("_NET_SUPPORTING_WM_CHECK"); sup_list = get_atom_from_string("_NET_SUPPORTED"); - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { /* Get the support check window and destroy it */ @@ -1648,7 +1671,7 @@ find_pid(pid_t pid) } uint32_t -name_to_pixel(const char *colorname) +name_to_pixel(int sidx, const char *colorname) { uint32_t result = 0; char cname[32] = "#"; @@ -1658,7 +1681,7 @@ name_to_pixel(const char *colorname) xcb_alloc_named_color_reply_t *nr; uint16_t rr, gg, bb; - screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data; + screen = get_screen(sidx); cmap = screen->default_colormap; /* color is in format rgb://rr/gg/bb */ @@ -1700,15 +1723,15 @@ setscreencolor(char *val, int i, int c) { int num_screens; - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); if (i > 0 && i <= num_screens) { - screens[i - 1].c[c].pixel = name_to_pixel(val); + screens[i - 1].c[c].pixel = name_to_pixel(i - 1, val); free(screens[i - 1].c[c].name); if ((screens[i - 1].c[c].name = strdup(val)) == NULL) err(1, "strdup"); } else if (i == -1) { for (i = 0; i < num_screens; i++) { - screens[i].c[c].pixel = name_to_pixel(val); + screens[i].c[c].pixel = name_to_pixel(0, val); free(screens[i].c[c].name); if ((screens[i].c[c].name = strdup(val)) == NULL) err(1, "strdup"); @@ -1751,7 +1774,7 @@ custom_region(char *val) int sidx, num_screens; xcb_screen_t *screen; - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); if (sscanf(val, "screen[%d]:%ux%u+%u+%u", &sidx, &w, &h, &x, &y) != 5) errx(1, "invalid custom region, " "should be 'screen[]:x++"); @@ -1979,7 +2002,7 @@ bar_urgent(char *s, size_t sz) for (i = 0; i < workspace_limit; i++) urgent[i] = 0; - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) for (j = 0; j < workspace_limit; j++) TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) { @@ -2229,7 +2252,7 @@ bar_draw(void) /* expand the format by first passing it through strftime(3) */ bar_fmt_expand(fmtexp, sizeof fmtexp); - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { TAILQ_FOREACH(r, &screens[i].rl, entry) { if (r->bar == NULL) @@ -2333,7 +2356,7 @@ bar_toggle(struct swm_region *r, union arg *args) } /* update bars as necessary */ - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) TAILQ_FOREACH(tmpr, &screens[i].rl, entry) if (tmpr->bar) { @@ -2818,7 +2841,7 @@ unmap_all(void) struct ws_win *win; int i, j, num_screens; - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) for (j = 0; j < workspace_limit; j++) TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) @@ -2913,7 +2936,7 @@ root_to_region(xcb_window_t root, int check) DNPRINTF(SWM_D_MISC, "root_to_region: window: 0x%x\n", root); - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) if (screens[i].root == root) break; @@ -2956,6 +2979,8 @@ root_to_region(xcb_window_t root, int check) if (r == NULL && check & SWM_CK_FALLBACK) r = TAILQ_FIRST(&screens[i].rl); + DNPRINTF(SWM_D_MISC, "root_to_region: idx: %d\n", get_region_index(r)); + return (r); } @@ -2965,7 +2990,7 @@ find_unmanaged_window(xcb_window_t id) struct ws_win *win; int i, j, num_screens; - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) for (j = 0; j < workspace_limit; j++) TAILQ_FOREACH(win, &screens[i].ws[j].unmanagedlist, @@ -2982,7 +3007,7 @@ find_window(xcb_window_t id) int i, j, num_screens; xcb_query_tree_reply_t *r; - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) for (j = 0; j < workspace_limit; j++) TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) @@ -3077,7 +3102,7 @@ kill_refs(struct ws_win *win) if (win == NULL) return; - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) TAILQ_FOREACH(r, &screens[i].rl, entry) for (x = 0; x < workspace_limit; x++) { @@ -3100,7 +3125,7 @@ validate_win(struct ws_win *testwin) if (testwin == NULL) return (0); - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) TAILQ_FOREACH(r, &screens[i].rl, entry) for (x = 0; x < workspace_limit; x++) { @@ -3120,7 +3145,7 @@ validate_ws(struct workspace *testws) int i, x, num_screens; /* validate all ws */ - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) TAILQ_FOREACH(r, &screens[i].rl, entry) for (x = 0; x < workspace_limit; x++) { @@ -3389,7 +3414,7 @@ focus_region(struct swm_region *r) if (old_r) unfocus_win(old_r->ws->focus); - xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, r->s->root, + xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, r->id, XCB_CURRENT_TIME); /* Clear bar since empty. */ @@ -3440,8 +3465,9 @@ switchws(struct swm_region *r, union arg *args) this_r->ws = new_ws; new_ws->r = this_r; - /* Set focus_pending before stacking. */ - if (focus_mode != SWM_FOCUS_FOLLOW) + /* Set focus_pending before stacking, if needed. */ + if (focus_mode != SWM_FOCUS_FOLLOW && (!new_ws->focus_pending || + validate_win(new_ws->focus_pending))) new_ws->focus_pending = get_region_focus(new_ws->r); stack(); @@ -3455,17 +3481,16 @@ switchws(struct swm_region *r, union arg *args) if (old_ws->r && focus_mode != SWM_FOCUS_FOLLOW) { if (new_ws->focus_pending) { focus_win(new_ws->focus_pending); - } else { - /* Empty region, focus on root. */ - xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, - new_ws->r->s[new_ws->r->s->idx].root, - XCB_CURRENT_TIME); + new_ws->focus_pending = NULL; } } - /* Clear bar if new ws is empty. */ - if (new_ws->focus_pending == NULL) + /* Clear bar and set focus on region input win if new ws is empty. */ + if (new_ws->focus_pending == NULL && new_ws->focus == NULL) { + xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, r->id, + XCB_CURRENT_TIME); bar_draw(); + } focus_flush(); @@ -3478,30 +3503,32 @@ cyclews(struct swm_region *r, union arg *args) union arg a; struct swm_screen *s = r->s; int cycle_all = 0; + int move = 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_MOVE_UP: + move = 1; + /* FALLTHROUGH */ case SWM_ARG_ID_CYCLEWS_UP_ALL: cycle_all = 1; /* FALLTHROUGH */ case SWM_ARG_ID_CYCLEWS_UP: - if (a.id < workspace_limit - 1) - a.id++; - else - a.id = 0; + a.id = (a.id < workspace_limit - 1) ? a.id + 1 : 0; break; + case SWM_ARG_ID_CYCLEWS_MOVE_DOWN: + move = 1; + /* FALLTHROUGH */ 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 = workspace_limit - 1; + a.id = (a.id > 0) ? a.id - 1 : workspace_limit - 1; break; default: return; @@ -3513,6 +3540,9 @@ cyclews(struct swm_region *r, union arg *args) if (!cycle_visible && s->ws[a.id].r != NULL) continue; + if (move) + send_to_ws(r, &a); + switchws(r, &a); } while (a.id != r->ws->idx); } @@ -3540,7 +3570,7 @@ focusrg(struct swm_region *r, union arg *args) int ridx = args->id, i, num_screens; struct swm_region *rr = NULL; - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); /* do nothing if we don't have more than one screen */ if (!(num_screens > 1 || outputs > 1)) return; @@ -3564,7 +3594,7 @@ cyclerg(struct swm_region *r, union arg *args) struct swm_region *rr = NULL; int i, num_screens; - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); /* do nothing if we don't have more than one screen */ if (!(num_screens > 1 || outputs > 1)) return; @@ -3967,7 +3997,7 @@ stack(void) { DNPRINTF(SWM_D_STACK, "stack: begin\n"); - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { #ifdef SWM_DEBUG j = 0; @@ -4470,6 +4500,8 @@ max_stack(struct workspace *ws, struct swm_geometry *g) win = ws->focus_pending; else if (ws->focus) win = ws->focus; + else if (ws->focus_prev) + win = ws->focus_prev; else win = TAILQ_FIRST(&ws->winlist); @@ -4542,7 +4574,7 @@ send_to_rg(struct swm_region *r, union arg *args) struct swm_region *rr = NULL; union arg a; - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); /* do nothing if we don't have more than one screen */ if (!(num_screens > 1 || outputs > 1)) return; @@ -4604,11 +4636,10 @@ send_to_ws(struct swm_region *r, union arg *args) unfocus_win(parent); - if (focus_mode != SWM_FOCUS_FOLLOW) + if (focus_mode != SWM_FOCUS_FOLLOW) { pws->focus = pws->focus_pending; - - if (focus_mode != SWM_FOCUS_FOLLOW) pws->focus_pending = NULL; + } } /* Don't unmap parent if new ws is visible */ @@ -4653,8 +4684,14 @@ send_to_ws(struct swm_region *r, union arg *args) stack(); if (focus_mode != SWM_FOCUS_FOLLOW) { - focus_win(ws->focus_pending); - ws->focus_pending = NULL; + if (ws->focus_pending) { + focus_win(ws->focus_pending); + ws->focus_pending = NULL; + } else { + xcb_set_input_focus(conn, + XCB_INPUT_FOCUS_PARENT, r->id, + XCB_CURRENT_TIME); + } } focus_flush(); @@ -4966,7 +5003,7 @@ search_win(struct swm_region *r, union arg *args) } void -search_resp_uniconify(const char *resp, unsigned long len) +search_resp_uniconify(const char *resp, size_t len) { char *name; struct ws_win *win; @@ -4996,7 +5033,7 @@ search_resp_uniconify(const char *resp, unsigned long len) } void -search_resp_name_workspace(const char *resp, unsigned long len) +search_resp_name_workspace(const char *resp, size_t len) { struct workspace *ws; @@ -5092,7 +5129,7 @@ search_do_resp(void) { ssize_t rbytes; char *resp; - unsigned long len; + size_t len; DNPRINTF(SWM_D_MISC, "search_do_resp:\n"); @@ -5292,7 +5329,7 @@ resize(struct ws_win *win, union arg *args) int top = 0, left = 0, resizing; int dx, dy; xcb_cursor_t cursor; - xcb_query_pointer_reply_t *xpr; + xcb_query_pointer_reply_t *xpr = NULL; xcb_generic_event_t *evt; xcb_motion_notify_event_t *mne; @@ -5322,6 +5359,12 @@ resize(struct ws_win *win, union arg *args) focus_flush(); + /* It's possible for win to have been freed during focus_flush(). */ + if (validate_win(win)) { + DNPRINTF(SWM_D_EVENT, "move: invalid win.\n"); + goto out; + } + switch (args->id) { case SWM_ARG_ID_WIDTHSHRINK: WIDTH(win) -= SWM_RESIZE_STEPS; @@ -5378,7 +5421,7 @@ resize(struct ws_win *win, union arg *args) xcb_flush(conn); resizing = 1; - while ((evt = xcb_wait_for_event(conn)) && resizing) { + while (resizing && (evt = xcb_wait_for_event(conn))) { switch (XCB_EVENT_RESPONSE_TYPE(evt)) { case XCB_BUTTON_RELEASE: DNPRINTF(SWM_D_EVENT, "resize: BUTTON_RELEASE\n"); @@ -5441,6 +5484,12 @@ resize(struct ws_win *win, union arg *args) break; default: event_handle(evt); + + /* It's possible for win to have been freed above. */ + if (validate_win(win)) { + DNPRINTF(SWM_D_EVENT, "move: invalid win.\n"); + goto out; + } break; } free(evt); @@ -5450,7 +5499,7 @@ resize(struct ws_win *win, union arg *args) xcb_flush(conn); } store_float_geom(win,r); - +out: xcb_ungrab_pointer(conn, XCB_CURRENT_TIME); free(xpr); DNPRINTF(SWM_D_EVENT, "resize: done.\n"); @@ -5478,7 +5527,7 @@ move(struct ws_win *win, union arg *args) xcb_timestamp_t timestamp = 0; int move_stp = 0, moving; struct swm_region *r = NULL; - xcb_query_pointer_reply_t *qpr; + xcb_query_pointer_reply_t *qpr = NULL; xcb_generic_event_t *evt; xcb_motion_notify_event_t *mne; @@ -5509,6 +5558,12 @@ move(struct ws_win *win, union arg *args) focus_flush(); + /* It's possible for win to have been freed during focus_flush(). */ + if (validate_win(win)) { + DNPRINTF(SWM_D_EVENT, "move: invalid win.\n"); + goto out; + } + move_stp = 0; switch (args->id) { case SWM_ARG_ID_MOVELEFT: @@ -5551,7 +5606,7 @@ move(struct ws_win *win, union arg *args) xcb_flush(conn); moving = 1; - while ((evt = xcb_wait_for_event(conn)) && moving) { + while (moving && (evt = xcb_wait_for_event(conn))) { switch (XCB_EVENT_RESPONSE_TYPE(evt)) { case XCB_BUTTON_RELEASE: DNPRINTF(SWM_D_EVENT, "move: BUTTON_RELEASE\n"); @@ -5573,6 +5628,12 @@ move(struct ws_win *win, union arg *args) break; default: event_handle(evt); + + /* It's possible for win to have been freed above. */ + if (validate_win(win)) { + DNPRINTF(SWM_D_EVENT, "move: invalid win.\n"); + goto out; + } break; } free(evt); @@ -5582,6 +5643,7 @@ move(struct ws_win *win, union arg *args) xcb_flush(conn); } store_float_geom(win, r); +out: free(qpr); xcb_ungrab_pointer(conn, XCB_CURRENT_TIME); DNPRINTF(SWM_D_EVENT, "move: done.\n"); @@ -5718,8 +5780,10 @@ struct keyfunc { { "ws_22", switchws, {.id = 21} }, { "ws_next", cyclews, {.id = SWM_ARG_ID_CYCLEWS_UP} }, { "ws_next_all", cyclews, {.id = SWM_ARG_ID_CYCLEWS_UP_ALL} }, + { "ws_next_move", cyclews, {.id = SWM_ARG_ID_CYCLEWS_MOVE_UP} }, { "ws_prev", cyclews, {.id = SWM_ARG_ID_CYCLEWS_DOWN} }, { "ws_prev_all", cyclews, {.id = SWM_ARG_ID_CYCLEWS_DOWN_ALL} }, + { "ws_prev_move", cyclews, {.id = SWM_ARG_ID_CYCLEWS_MOVE_DOWN} }, { "ws_prior", priorws, {0} }, { "dumpwins", dumpwins, {0} }, /* MUST BE LAST */ { "invalid key func", NULL, {0} }, @@ -5926,7 +5990,7 @@ spawn_select(struct swm_region *r, union arg *args, const char *spawn_name, } void -spawn_insert(const char *name, const char *args) +spawn_insert(const char *name, const char *args, int flags) { char *arg, *cp, *ptr; struct spawn_prog *sp; @@ -5955,6 +6019,8 @@ spawn_insert(const char *name, const char *args) } free(cp); + sp->flags = flags; + TAILQ_INSERT_TAIL(&spawns, sp, entry); DNPRINTF(SWM_D_SPAWN, "spawn_insert: leave\n"); } @@ -5977,18 +6043,7 @@ spawn_remove(struct spawn_prog *sp) } void -spawn_replace(struct spawn_prog *sp, const char *name, const 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(const char *name, const char *args) +setspawn(const char *name, const char *args, int flags) { struct spawn_prog *sp; @@ -5997,22 +6052,18 @@ setspawn(const char *name, const char *args) if (name == NULL) return; - TAILQ_FOREACH(sp, &spawns, entry) { + /* Remove any old spawn under the same name. */ + 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; + spawn_remove(sp); + break; } - } - if (*args == '\0') { + + if (*args != '\0') + spawn_insert(name, args, flags); + else warnx("error: setspawn: cannot find program: %s", name); - return; - } - spawn_insert(name, args); DNPRINTF(SWM_D_SPAWN, "setspawn: leave\n"); } @@ -6020,25 +6071,12 @@ int setconfspawn(char *selector, char *value, int flags) { char *args; - char which[PATH_MAX]; - size_t i; args = expand_tilde(value); DNPRINTF(SWM_D_SPAWN, "setconfspawn: [%s] [%s]\n", selector, args); - /* verify we have the goods */ - snprintf(which, sizeof which, "which %s", value); - for (i = strlen("which "); i < strlen(which); i++) - if (which[i] == ' ') { - which[i] = '\0'; - break; - } - if (flags == 0 && system(which) != 0) - add_startup_exception("could not find %s", - &which[strlen("which ")]); - - setspawn(selector, args); + setspawn(selector, args, flags); free(args); DNPRINTF(SWM_D_SPAWN, "setconfspawn: done.\n"); @@ -6046,16 +6084,57 @@ setconfspawn(char *selector, char *value, int flags) } void +validate_spawns(void) +{ + struct spawn_prog *sp; + char which[PATH_MAX]; + size_t i; + + struct key *kp; + + RB_FOREACH(kp, key_tree, &keys) { + if (kp->funcid != KF_SPAWN_CUSTOM) + continue; + + /* find program */ + TAILQ_FOREACH(sp, &spawns, entry) { + if (!strcasecmp(kp->spawn_name, sp->name)) + break; + } + + if (sp == NULL || sp->flags & SWM_SPAWN_OPTIONAL) + continue; + + /* verify we have the goods */ + snprintf(which, sizeof which, "which %s", sp->argv[0]); + DNPRINTF(SWM_D_CONF, "validate_spawns: which %s\n", + sp->argv[0]); + for (i = strlen("which "); i < strlen(which); i++) + if (which[i] == ' ') { + which[i] = '\0'; + break; + } + if (system(which) != 0) + add_startup_exception("could not find %s", + &which[strlen("which ")]); + } +} + +void setup_spawn(void) { + setconfspawn("lock", "xlock", 0); + setconfspawn("term", "xterm", 0); setconfspawn("spawn_term", "xterm", 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" @@ -6063,6 +6142,7 @@ setup_spawn(void) " -nf $bar_font_color" " -sb $bar_border" " -sf $bar_color", 0); + setconfspawn("name_workspace", "dmenu" " -p Workspace" " -fn $bar_font" @@ -6071,11 +6151,10 @@ setup_spawn(void) " -sb $bar_border" " -sf $bar_color", 0); - /* these are not verified for existence */ - setconfspawn("lock", "xlock", 1); - setconfspawn("screenshot_all", "screenshot.sh full", 1); - setconfspawn("screenshot_wind", "screenshot.sh window", 1); - setconfspawn("initscr", "initscreen.sh", 1); + /* These are not verified for existence, even with a binding set. */ + setconfspawn("screenshot_all", "screenshot.sh full", SWM_SPAWN_OPTIONAL); + setconfspawn("screenshot_wind", "screenshot.sh window", SWM_SPAWN_OPTIONAL); + setconfspawn("initscr", "initscreen.sh", SWM_SPAWN_OPTIONAL); } /* key bindings */ @@ -6249,8 +6328,8 @@ setconfbinding(char *selector, char *value, int flags) 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); + DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match " + "keyfunc\n", selector); if (parsekeys(value, mod_key, &mod, &ks) == 0) { setkeybinding(mod, ks, kfid, NULL); return (0); @@ -6261,8 +6340,8 @@ setconfbinding(char *selector, char *value, int flags) /* 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); + DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match " + "spawn\n", selector); if (parsekeys(value, mod_key, &mod, &ks) == 0) { setkeybinding(mod, ks, KF_SPAWN_CUSTOM, sp->name); @@ -6393,6 +6472,8 @@ setup_keys(void) 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_SHIFT, XK_Up, KF_WS_NEXT_MOVE,NULL); + setkeybinding(MODKEY_SHIFT, XK_Down, KF_WS_PREV_MOVE,NULL); setkeybinding(MODKEY, XK_a, KF_WS_PRIOR, NULL); #ifdef SWM_DEBUG setkeybinding(MODKEY_SHIFT, XK_d, KF_DUMPWINS, NULL); @@ -6414,24 +6495,25 @@ clear_keys(void) int setkeymapping(char *selector, char *value, int flags) { - char keymapping_file[PATH_MAX]; + char *keymapping_file; /* suppress unused warnings since vars are needed */ (void)selector; (void)flags; 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); + + keymapping_file = expand_tilde(value); + 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(); } + + free(keymapping_file); + DNPRINTF(SWM_D_KEY, "setkeymapping: leave\n"); return (0); } @@ -6482,13 +6564,23 @@ grabkeys(void) modifiers[2] = XCB_MOD_MASK_LOCK; modifiers[3] = numlockmask | XCB_MOD_MASK_LOCK; - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); for (k = 0; k < num_screens; k++) { if (TAILQ_EMPTY(&screens[k].rl)) continue; xcb_ungrab_key(conn, XCB_GRAB_ANY, screens[k].root, XCB_MOD_MASK_ANY); RB_FOREACH(kp, key_tree, &keys) { + /* Skip unused ws binds. */ + if ((int)kp->funcid > KF_WS_1 + workspace_limit - 1 && + kp->funcid <= KF_WS_22) + continue; + + /* Skip unused mvws binds. */ + if ((int)kp->funcid > KF_MVWS_1 + workspace_limit - 1 && + kp->funcid <= KF_MVWS_22) + continue; + if ((code = xcb_key_symbols_get_keycode(syms, kp->keysym))) { for (j = 0; j < LENGTH(modifiers); j++) @@ -6535,6 +6627,8 @@ const char *quirkname[] = { "XTERM_FONTADJ", "FULLSCREEN", "FOCUSPREV", + "NOFOCUSONMAP", + "FOCUSONMAP_SINGLE", }; /* SWM_Q_WS: retain '|' for back compat for now (2009-08-11) */ @@ -6766,7 +6860,7 @@ setconfvalue(char *selector, char *value, int flags) errx(1, "setconfvalue: bar_enabled_ws: invalid " "workspace %d.", ws_id + 1); - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { ws = (struct workspace *)&screens[i].ws; ws[ws_id].bar_enabled = atoi(value); @@ -7097,7 +7191,7 @@ setlayout(char *selector, char *value, int flags) "::::" "'"); - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { ws = (struct workspace *)&screens[i].ws; ws[ws_id].cur_layout = &layouts[st]; @@ -7226,7 +7320,7 @@ int conf_load(const char *filename, int keymapping) { FILE *config; - char *line = NULL, *cp, *optsub, *optval; + char *line = NULL, *cp, *optsub, *optval = NULL; size_t linelen, lineno = 0; int wordlen, i, optidx; struct config_option *opt = NULL; @@ -7237,6 +7331,9 @@ conf_load(const char *filename, int keymapping) warnx("conf_load: no filename"); return (1); } + + DNPRINTF(SWM_D_CONF, "conf_load: open %s\n", filename); + if ((config = fopen(filename, "r")) == NULL) { warn("conf_load: fopen: %s", filename); return (1); @@ -7742,7 +7839,7 @@ expose(xcb_expose_event_t *e) DNPRINTF(SWM_D_EVENT, "expose: window: 0x%x\n", e->window); - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) TAILQ_FOREACH(r, &screens[i].rl, entry) if (e->window == WINID(r->bar)) @@ -8109,6 +8206,9 @@ destroynotify(xcb_destroy_notify_event_t *e) if (win->ws->focus_pending) { focus_win(win->ws->focus_pending); win->ws->focus_pending = NULL; + } else if (win == win->ws->focus && win->ws->r) { + xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, + win->ws->r->id, XCB_CURRENT_TIME); } } @@ -8278,7 +8378,7 @@ mappingnotify(xcb_mapping_notify_event_t *e) void maprequest(xcb_map_request_event_t *e) { - struct ws_win *win; + struct ws_win *win, *w = NULL; xcb_get_window_attributes_reply_t *war; DNPRINTF(SWM_D_EVENT, "maprequest: win 0x%x\n", @@ -8302,8 +8402,25 @@ maprequest(xcb_map_request_event_t *e) (war->map_state == XCB_MAP_STATE_VIEWABLE)); /* The new window should get focus; prepare. */ - if (focus_mode != SWM_FOCUS_FOLLOW) - win->ws->focus_pending = get_focus_magic(win); + if (focus_mode != SWM_FOCUS_FOLLOW && + !(win->quirks & SWM_Q_NOFOCUSONMAP)) { + if (win->quirks & SWM_Q_FOCUSONMAP_SINGLE) { + /* See if other wins of same type are already mapped. */ + TAILQ_FOREACH(w, &win->ws->winlist, entry) { + if (w == win || !w->mapped) + continue; + + if (!strcmp(w->ch.class_name, + win->ch.class_name) && + !strcmp(w->ch.instance_name, + win->ch.instance_name)) + break; + } + } + + if (w == NULL) + win->ws->focus_pending = get_focus_magic(win); + } /* All windows need to be mapped if they are in the current workspace.*/ if (win->ws->r) @@ -8333,7 +8450,10 @@ motionnotify(xcb_motion_notify_event_t *e) last_event_time = e->time; - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + if (focus_mode == SWM_FOCUS_MANUAL) + return; + + num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) if (screens[i].root == e->root) break; @@ -8384,6 +8504,7 @@ void propertynotify(xcb_property_notify_event_t *e) { struct ws_win *win; + struct workspace *ws; #ifdef SWM_DEBUG char *name; @@ -8397,28 +8518,39 @@ propertynotify(xcb_property_notify_event_t *e) if (win == NULL) return; + ws = win->ws; + last_event_time = e->time; if (e->atom == a_swm_iconic) { if (e->state == XCB_PROPERTY_NEW_VALUE) { if (focus_mode != SWM_FOCUS_FOLLOW) - win->ws->focus_pending = get_focus_prev(win); + ws->focus_pending = get_focus_prev(win); unfocus_win(win); unmap_window(win); - if (win->ws->r) { + if (ws->r) { stack(); + if (focus_mode != SWM_FOCUS_FOLLOW) { - focus_win(win->ws->focus_pending); - win->ws->focus_pending = NULL; + if (ws->focus_pending) { + focus_win(ws->focus_pending); + ws->focus_pending = NULL; + } else { + xcb_set_input_focus(conn, + XCB_INPUT_FOCUS_PARENT, + ws->r->id, + XCB_CURRENT_TIME); + } } + focus_flush(); } } else if (e->state == XCB_PROPERTY_DELETE) { /* The window is no longer iconic, restack ws. */ if (focus_mode != SWM_FOCUS_FOLLOW) - win->ws->focus_pending = get_focus_magic(win); + ws->focus_pending = get_focus_magic(win); stack(); @@ -8430,9 +8562,9 @@ propertynotify(xcb_property_notify_event_t *e) if (e->state == XCB_PROPERTY_NEW_VALUE) { if (focus_mode != SWM_FOCUS_FOLLOW) { if (win->mapped && - win->ws->focus_pending == win) { - focus_win(win->ws->focus_pending); - win->ws->focus_pending = NULL; + ws->focus_pending == win) { + focus_win(ws->focus_pending); + ws->focus_pending = NULL; } } } @@ -8487,11 +8619,12 @@ unmapnotify(xcb_unmap_notify_event_t *e) if (focus_mode == SWM_FOCUS_FOLLOW) { if (ws->r) focus_win(get_pointer_win(ws->r->s->root)); - } else { - if (ws->focus_pending) { - focus_win(ws->focus_pending); - ws->focus_pending = NULL; - } + } else if (ws->focus_pending) { + focus_win(ws->focus_pending); + ws->focus_pending = NULL; + } else if (ws->focus == NULL && ws->r) { + xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, + ws->r->id, XCB_CURRENT_TIME); } } @@ -8625,7 +8758,7 @@ enable_wm(void) xcb_generic_error_t *error; /* this causes an error if some other window manager is running */ - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { if ((sc = get_screen(i)) == NULL) errx(1, "ERROR: can't get screen %d.", i); @@ -8757,7 +8890,7 @@ scan_xrandr(int i) if ((screen = get_screen(i)) == NULL) errx(1, "ERROR: can't get screen %d.", i); - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); if (i >= num_screens) errx(1, "scan_xrandr: invalid screen"); @@ -8838,7 +8971,7 @@ screenchange(xcb_randr_screen_change_notify_event_t *e) DNPRINTF(SWM_D_EVENT, "screenchange: root: 0x%x\n", e->root); - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); /* silly event doesn't include the screen index */ for (i = 0; i < num_screens; i++) if (screens[i].root == e->root) @@ -8889,7 +9022,7 @@ grab_windows(void) xcb_get_property_cookie_t pc; DNPRINTF(SWM_D_INIT, "grab_windows: begin\n"); - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { qtc = xcb_query_tree(conn, screens[i].root); qtr = xcb_query_tree_reply(conn, qtc, NULL); @@ -8987,7 +9120,7 @@ setup_screens(void) xcb_randr_query_version_cookie_t c; xcb_randr_query_version_reply_t *r; - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); if ((screens = calloc(num_screens, sizeof(struct swm_screen))) == NULL) err(1, "setup_screens: calloc: failed to allocate memory for " @@ -9106,7 +9239,7 @@ workaround(void) /* work around sun jdk bugs, code from wmname */ netwmcheck = get_atom_from_string("_NET_SUPPORTING_WM_CHECK"); - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { root = screens[i].root; @@ -9141,7 +9274,7 @@ shutdown_cleanup(void) teardown_ewmh(); - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); for (i = 0; i < num_screens; ++i) { if (screens[i].bar_gc != 0) xcb_free_gc(conn, screens[i].bar_gc); @@ -9348,6 +9481,8 @@ noconfig: if (cfile) conf_load(cfile, SWM_CONF_DEFAULT); + validate_spawns(); + setup_ewmh(); /* set some values to work around bad programs */ workaround(); @@ -9358,7 +9493,7 @@ noconfig: setenv("SWM_STARTED", "YES", 1); /* setup all bars */ - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); + num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) TAILQ_FOREACH(r, &screens[i].rl, entry) bar_setup(r); @@ -9420,11 +9555,11 @@ noconfig: if (stdin_ready) { stdin_ready = 0; - if (bar_extra_update()) { - bar_draw(); - xcb_flush(conn); - } + bar_extra_update(); } + + bar_draw(); + xcb_flush(conn); } done: shutdown_cleanup();