X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=spectrwm.c;h=a6ef35f8456e78e3c33aaad594139050ebc1892b;hb=749dbb28b26faa84871dcd745ca4b4e6150c3955;hp=1f092e9d9dd383cb043f8ba98325daa788927044;hpb=3facef670188a80de6984ca186e5d28385ec9e68;p=spectrwm.git diff --git a/spectrwm.c b/spectrwm.c index 1f092e9..a6ef35f 100644 --- a/spectrwm.c +++ b/spectrwm.c @@ -251,10 +251,11 @@ u_int32_t swm_debug = 0 #define SWM_FOCUS_MANUAL (2) #define SWM_CK_NONE 0 -#define SWM_CK_ALL 0x7 +#define SWM_CK_ALL 0xf #define SWM_CK_FOCUS 0x1 #define SWM_CK_POINTER 0x2 #define SWM_CK_FALLBACK 0x4 +#define SWM_CK_REGION 0x8 #define SWM_CONF_DEFAULT (0) #define SWM_CONF_KEYMAPPING (1) @@ -279,7 +280,6 @@ volatile sig_atomic_t restart_wm = 0; xcb_timestamp_t last_event_time = 0; int outputs = 0; int other_wm; -int ss_enabled = 0; int xrandr_support; int xrandr_eventbase; unsigned int numlockmask = 0; @@ -349,15 +349,13 @@ double dialog_ratio = 0.6; char *bar_argv[] = { NULL, NULL }; int bar_pipe[2]; char bar_ext[SWM_BAR_MAX]; +char bar_ext_buf[SWM_BAR_MAX]; char bar_vertext[SWM_BAR_MAX]; int bar_version = 0; -sig_atomic_t bar_alarm = 0; -int bar_delay = 30; int bar_enabled = 1; int bar_border_width = 1; int bar_at_bottom = 0; -int bar_extra = 1; -int bar_extra_running = 0; +int bar_extra = 0; int bar_verbose = 1; int bar_height = 0; int bar_justify = SWM_BAR_JUSTIFY_LEFT; @@ -376,6 +374,8 @@ int focus_default = SWM_STACK_TOP; int spawn_position = SWM_STACK_TOP; int disable_border = 0; int border_width = 1; +int region_padding = 0; +int tile_gap = 0; int verbose_layout = 0; time_t time_started; pid_t bar_pid; @@ -386,6 +386,8 @@ int bar_font_legacy = 1; char *bar_fonts; XftColor bar_font_color; struct passwd *pwd; +char *startup_exception; +unsigned int nr_exceptions = 0; /* layout manager data */ struct swm_geometry { @@ -407,6 +409,7 @@ struct swm_bar { /* virtual "screens" */ struct swm_region { TAILQ_ENTRY(swm_region) entry; + xcb_window_t id; struct swm_geometry g; struct workspace *ws; /* current workspace on this region */ struct workspace *ws_prior; /* prior workspace on this region */ @@ -521,6 +524,7 @@ struct workspace { enum { SWM_S_COLOR_BAR, SWM_S_COLOR_BAR_BORDER, + SWM_S_COLOR_BAR_BORDER_UNFOCUS, SWM_S_COLOR_BAR_FONT, SWM_S_COLOR_FOCUS, SWM_S_COLOR_UNFOCUS, @@ -537,6 +541,7 @@ struct swm_screen { struct swm_region_list orl; /* list of old regions */ xcb_window_t root; struct workspace ws[SWM_WS_MAX]; + struct swm_region *r_focus; /* colors */ struct { @@ -568,10 +573,12 @@ union arg { #define SWM_ARG_ID_STACKINIT (31) #define SWM_ARG_ID_CYCLEWS_UP (40) #define SWM_ARG_ID_CYCLEWS_DOWN (41) -#define SWM_ARG_ID_CYCLESC_UP (42) -#define SWM_ARG_ID_CYCLESC_DOWN (43) +#define SWM_ARG_ID_CYCLERG_UP (42) +#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) @@ -605,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); @@ -702,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); @@ -734,6 +746,15 @@ enum keyfuncid { KF_MOVE_LEFT, KF_MOVE_RIGHT, KF_MOVE_UP, + KF_MVRG_1, + KF_MVRG_2, + KF_MVRG_3, + KF_MVRG_4, + KF_MVRG_5, + KF_MVRG_6, + KF_MVRG_7, + KF_MVRG_8, + KF_MVRG_9, KF_MVWS_1, KF_MVWS_2, KF_MVWS_3, @@ -760,6 +781,17 @@ enum keyfuncid { KF_QUIT, KF_RAISE_TOGGLE, KF_RESTART, + KF_RG_1, + KF_RG_2, + KF_RG_3, + KF_RG_4, + KF_RG_5, + KF_RG_6, + KF_RG_7, + KF_RG_8, + KF_RG_9, + KF_RG_NEXT, + KF_RG_PREV, KF_SCREEN_NEXT, KF_SCREEN_PREV, KF_SEARCH_WIN, @@ -801,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 @@ -822,22 +856,20 @@ void adjust_font(struct ws_win *); void bar_class_name(char *, size_t, struct swm_region *); void bar_class_title_name(char *, size_t, struct swm_region *); void bar_cleanup(struct swm_region *); +void bar_extra_setup(void); void bar_extra_stop(void); +int bar_extra_update(void); void bar_fmt(const char *, char *, struct swm_region *, size_t); void bar_fmt_expand(char *, size_t); -void bar_fmt_print(void); +void bar_draw(void); void bar_print(struct swm_region *, const char *); void bar_print_legacy(struct swm_region *, const char *); -void bar_refresh(void); 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_signal(int); void bar_title_name(char *, size_t, struct swm_region *); void bar_toggle(struct swm_region *, union arg *); -void bar_update(void); void bar_urgent(char *, size_t); void bar_window_float(char *, size_t, struct swm_region *); void bar_window_name(char *, size_t, struct swm_region *); @@ -856,7 +888,7 @@ int count_win(struct workspace *, int); void cursors_cleanup(void); void cursors_load(void); void custom_region(char *); -void cyclescr(struct swm_region *, union arg *); +void cyclerg(struct swm_region *, union arg *); void cyclews(struct swm_region *, union arg *); void cycle_layout(struct swm_region *, union arg *); void destroynotify(xcb_destroy_notify_event_t *); @@ -885,6 +917,8 @@ void focusin(xcb_focus_in_event_t *); void focusout(xcb_focus_out_event_t *); #endif void focus_flush(void); +void focus_region(struct swm_region *); +void focusrg(struct swm_region *, union arg *); void focus_win(struct ws_win *); void fontset_init(void); void free_window(struct ws_win *); @@ -900,7 +934,9 @@ char *get_notify_mode_label(uint8_t); #endif 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 @@ -920,6 +956,7 @@ struct key *key_lookup(unsigned int, KeySym); void key_remove(struct key *); void key_replace(struct key *, unsigned int, KeySym, enum keyfuncid, const char *); +void kill_bar_extra_atexit(void); void kill_refs(struct ws_win *); #ifdef SWM_DEBUG void leavenotify(xcb_leave_notify_event_t *); @@ -930,9 +967,10 @@ void map_window(struct ws_win *); void mapnotify(xcb_map_notify_event_t *); void mappingnotify(xcb_mapping_notify_event_t *); 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 *); @@ -957,14 +995,16 @@ 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 *); +void send_to_rg(struct swm_region *, union arg *); void send_to_ws(struct swm_region *, union arg *); +void set_region(struct swm_region *); int setautorun(char *, char *, int); int setconfbinding(char *, char *, int); int setconfcolor(char *, char *, int); @@ -978,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); @@ -995,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); @@ -1016,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 *);*/ @@ -1024,6 +1065,8 @@ pid_t window_get_pid(xcb_window_t); void wkill(struct swm_region *, union arg *); void workaround(void); void xft_init(struct swm_region *); +void _add_startup_exception(const char *, va_list); +void add_startup_exception(const char *, ...); RB_PROTOTYPE(key_tree, key, entry, key_cmp); RB_GENERATE(key_tree, key, entry, key_cmp); @@ -1148,6 +1191,40 @@ get_screen(int screen) return (NULL); } +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; + int ridx = 0; + + if (r == NULL) + return -1; + + TAILQ_FOREACH(rr, &r->s->rl, entry) { + if (rr == r) + break; + ++ridx; + } + + if (rr == NULL) + return -1; + + return ridx; +} + void focus_flush(void) { @@ -1221,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(). */ @@ -1245,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 */ @@ -1594,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] = "#"; @@ -1604,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 */ @@ -1646,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"); @@ -1697,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++"); @@ -1925,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) { @@ -2163,8 +2240,9 @@ bar_fmt_expand(char *fmtexp, size_t sz) #endif } +/* Redraws the bar; need to follow with xcb_flush() or focus_flush(). */ void -bar_fmt_print(void) +bar_draw(void) { char fmtexp[SWM_BAR_MAX], fmtnew[SWM_BAR_MAX]; char fmtrep[SWM_BAR_MAX]; @@ -2174,21 +2252,28 @@ bar_fmt_print(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) continue; - if (r->ws->bar_enabled) + if (bar_enabled && r->ws->bar_enabled) xcb_map_window(conn, r->bar->id); else { xcb_unmap_window(conn, r->bar->id); continue; } - bar_fmt(fmtexp, fmtnew, r, sizeof fmtnew); - bar_replace(fmtnew, fmtrep, r, sizeof fmtrep); + if (startup_exception) + snprintf(fmtrep, sizeof fmtrep, "total " + "exceptions: %d, first exception: %s", + nr_exceptions, + startup_exception); + else { + bar_fmt(fmtexp, fmtnew, r, sizeof fmtnew); + bar_replace(fmtnew, fmtrep, r, sizeof fmtrep); + } if (bar_font_legacy) bar_print_legacy(r, fmtrep); else @@ -2197,39 +2282,52 @@ bar_fmt_print(void) } } -void -bar_update(void) +/* + * Reads external script output; call when stdin is readable. + * Returns 1 if bar_ext was updated; otherwise 0. + */ +int +bar_extra_update(void) { - size_t len; - char *b; + size_t len; + char b[SWM_BAR_MAX]; + int changed = 0; - if (!bar_enabled) - return; - if (bar_extra && bar_extra_running) { - /* ignore short reads; it'll correct itself */ - while ((b = fgetln(stdin, &len)) != NULL) - if (b && b[len - 1] == '\n') { - b[len - 1] = '\0'; - strlcpy(bar_ext, b, sizeof bar_ext); + if (!bar_extra) + return changed; + + while (fgets(b, sizeof(b), stdin) != NULL) { + if (bar_enabled) { + len = strlen(b); + if (b[len - 1] == '\n') { + /* Remove newline. */ + b[--len] = '\0'; + + /* "Clear" bar_ext. */ + bar_ext[0] = '\0'; + + /* Flush buffered output. */ + strlcpy(bar_ext, bar_ext_buf, sizeof(bar_ext)); + bar_ext_buf[0] = '\0'; + + /* Append new output to bar. */ + strlcat(bar_ext, b, sizeof(bar_ext)); + + changed = 1; + } else { + /* Buffer output. */ + strlcat(bar_ext_buf, b, sizeof(bar_ext_buf)); } - if (b == NULL && errno != EAGAIN) { - warn("bar_update: bar_extra failed"); - bar_extra_stop(); } - } else - strlcpy(bar_ext, "", sizeof bar_ext); - - bar_fmt_print(); - alarm(bar_delay); -} + } -void -bar_signal(int sig) -{ - /* suppress unused warning since var is needed */ - (void)sig; + if (errno != EAGAIN) { + warn("bar_action failed"); + bar_extra_stop(); + changed = 1; + } - bar_alarm = 1; + return changed; } void @@ -2258,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) { @@ -2271,30 +2369,31 @@ bar_toggle(struct swm_region *r, union arg *args) stack(); /* must be after stack */ - bar_update(); + bar_draw(); focus_flush(); } void -bar_refresh(void) +bar_extra_setup(void) { - struct swm_region *r; - uint32_t wa[2]; - int i, num_screens; - /* do this here because the conf file is in memory */ - if (bar_extra && !bar_extra_running && bar_argv[0]) { + if (!bar_extra && bar_argv[0]) { /* launch external status app */ - bar_extra_running = 1; + bar_extra = 1; if (pipe(bar_pipe) == -1) err(1, "pipe error"); socket_setnonblock(bar_pipe[0]); socket_setnonblock(bar_pipe[1]); /* XXX hmmm, really? */ - if (dup2(bar_pipe[0], 0) == -1) + + /* Set stdin to read from the pipe. */ + if (dup2(bar_pipe[0], STDIN_FILENO) == -1) err(1, "dup2"); - if (dup2(bar_pipe[1], 1) == -1) + + /* Set stdout to write to the pipe. */ + if (dup2(bar_pipe[1], STDOUT_FILENO) == -1) err(1, "dup2"); + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) err(1, "could not disable SIGPIPE"); switch (bar_pid = fork()) { @@ -2310,19 +2409,16 @@ bar_refresh(void) close(bar_pipe[1]); break; } + + atexit(kill_bar_extra_atexit); } +} - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); - for (i = 0; i < num_screens; i++) - TAILQ_FOREACH(r, &screens[i].rl, entry) { - if (r->bar == NULL) - continue; - wa[0] = screens[i].c[SWM_S_COLOR_BAR].pixel; - wa[1] = screens[i].c[SWM_S_COLOR_BAR_BORDER].pixel; - xcb_change_window_attributes(conn, r->bar->id, - XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL, wa); - } - bar_update(); +void +kill_bar_extra_atexit(void) +{ + if (bar_pid) + kill(bar_pid, SIGTERM); } int @@ -2462,10 +2558,12 @@ bar_setup(struct swm_region *r) WIDTH(r->bar) = WIDTH(r) - 2 * bar_border_width; HEIGHT(r->bar) = bar_height - 2 * bar_border_width; + /* Assume region is unfocused when we create the bar. */ r->bar->id = xcb_generate_id(conn); wa[0] = r->s->c[SWM_S_COLOR_BAR].pixel; - wa[1] = r->s->c[SWM_S_COLOR_BAR_BORDER].pixel; - wa[2] = XCB_EVENT_MASK_EXPOSURE; + wa[1] = r->s->c[SWM_S_COLOR_BAR_BORDER_UNFOCUS].pixel; + wa[2] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_POINTER_MOTION | + XCB_EVENT_MASK_POINTER_MOTION_HINT; xcb_create_window(conn, XCB_COPY_FROM_PARENT, r->bar->id, r->s->root, X(r->bar), Y(r->bar), WIDTH(r->bar), HEIGHT(r->bar), @@ -2488,9 +2586,7 @@ bar_setup(struct swm_region *r) "%d x %d\n", WINID(r->bar), X(r->bar), Y(r->bar), WIDTH(r->bar), HEIGHT(r->bar)); - if (signal(SIGALRM, bar_signal) == SIG_ERR) - err(1, "could not install bar_signal"); - bar_refresh(); + bar_extra_setup(); } void @@ -2553,8 +2649,8 @@ version(struct swm_region *r, union arg *args) "Version: %s Build: %s", SPECTRWM_VERSION, buildstr); else strlcpy(bar_vertext, "", sizeof bar_vertext); - bar_update(); + bar_draw(); xcb_flush(conn); } @@ -2745,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) @@ -2840,12 +2936,15 @@ 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; - if (check & SWM_CK_FOCUS) { + if (check & SWM_CK_REGION) + r = screens[i].r_focus; + + if (r == NULL && check & SWM_CK_FOCUS) { /* Try to find an actively focused window */ gifr = xcb_get_input_focus_reply(conn, xcb_get_input_focus(conn), NULL); @@ -2880,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); } @@ -2889,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, @@ -2906,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) @@ -3001,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++) { @@ -3024,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++) { @@ -3044,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++) { @@ -3124,6 +3225,9 @@ focus_win(struct ws_win *win) if (win->ws == NULL) goto out; + if (!win->mapped) + goto out; + ws = win->ws; if (validate_ws(ws)) @@ -3200,16 +3304,17 @@ focus_win(struct ws_win *win) TAILQ_FOREACH(w, &ws->winlist, entry) if (w->transient == win->id && !w->iconic) map_window(w); - } + set_region(ws->r); + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->s->root, ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 32, 1, &win->id); } out: - bar_update(); + bar_draw(); DNPRINTF(SWM_D_FOCUS, "focus_win: done.\n"); } @@ -3264,6 +3369,60 @@ event_drain(uint8_t rt) } void +set_region(struct swm_region *r) +{ + struct swm_region *rf; + + if (r == NULL) + return; + + rf = r->s->r_focus; + /* Unfocus old region bar. */ + if (rf) { + if (rf == r) + return; + + xcb_change_window_attributes(conn, rf->bar->id, + XCB_CW_BORDER_PIXEL, + &r->s->c[SWM_S_COLOR_BAR_BORDER_UNFOCUS].pixel); + } + + /* Set region bar border to focus_color. */ + xcb_change_window_attributes(conn, r->bar->id, + XCB_CW_BORDER_PIXEL, &r->s->c[SWM_S_COLOR_BAR_BORDER].pixel); + + r->s->r_focus = r; +} + +void +focus_region(struct swm_region *r) +{ + struct ws_win *nfw; + struct swm_region *old_r; + + if (r == NULL) + return; + + old_r = r->s->r_focus; + set_region(r); + + nfw = get_region_focus(r); + if (nfw) { + focus_win(nfw); + } else { + /* New region is empty; need to manually unfocus win. */ + if (old_r) + unfocus_win(old_r->ws->focus); + + xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, r->id, + XCB_CURRENT_TIME); + + /* Clear bar since empty. */ + bar_draw(); + } +} + +void switchws(struct swm_region *r, union arg *args) { int wsid = args->id, unmap_old = 0; @@ -3321,17 +3480,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) - bar_update(); + /* 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(); @@ -3344,30 +3502,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; @@ -3379,6 +3539,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); } @@ -3401,25 +3564,48 @@ priorws(struct swm_region *r, union arg *args) } void -cyclescr(struct swm_region *r, union arg *args) +focusrg(struct swm_region *r, union arg *args) +{ + int ridx = args->id, i, num_screens; + struct swm_region *rr = NULL; + + num_screens = get_screen_count(); + /* do nothing if we don't have more than one screen */ + if (!(num_screens > 1 || outputs > 1)) + return; + + DNPRINTF(SWM_D_FOCUS, "focusrg: id: %d\n", ridx); + + rr = TAILQ_FIRST(&r->s->rl); + for (i = 0; i < ridx; ++i) + rr = TAILQ_NEXT(rr, entry); + + if (rr == NULL) + return; + + focus_region(rr); + focus_flush(); +} + +void +cyclerg(struct swm_region *r, union arg *args) { - struct ws_win *nfw; 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; i = r->s->idx; switch (args->id) { - case SWM_ARG_ID_CYCLESC_UP: + case SWM_ARG_ID_CYCLERG_UP: rr = TAILQ_NEXT(r, entry); if (rr == NULL) rr = TAILQ_FIRST(&screens[i].rl); break; - case SWM_ARG_ID_CYCLESC_DOWN: + case SWM_ARG_ID_CYCLERG_DOWN: rr = TAILQ_PREV(r, swm_region_list, entry); if (rr == NULL) rr = TAILQ_LAST(&screens[i].rl, swm_region_list); @@ -3430,19 +3616,7 @@ cyclescr(struct swm_region *r, union arg *args) if (rr == NULL) return; - nfw = get_region_focus(rr); - if (nfw) { - focus_win(nfw); - } else { - /* New region is empty; unfocus old region and warp pointer. */ - unfocus_win(r->ws->focus); - xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, - rr->s[i].root, XCB_CURRENT_TIME); - - /* Clear bar since empty. */ - bar_update(); - } - + focus_region(rr); focus_flush(); } @@ -3592,37 +3766,70 @@ get_focus_prev(struct ws_win *win) if (winfocus == NULL || winfocus == win) { switch (focus_close) { case SWM_STACK_BOTTOM: - winfocus = TAILQ_FIRST(wl); + TAILQ_FOREACH(winfocus, wl, entry) + if (!winfocus->iconic && winfocus != cur_focus) + break; break; case SWM_STACK_TOP: - winfocus = TAILQ_LAST(wl, ws_win_list); + TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list, entry) + if (!winfocus->iconic && winfocus != cur_focus) + break; break; case SWM_STACK_ABOVE: - if ((winfocus = TAILQ_NEXT(cur_focus, entry)) == NULL) { - if (focus_close_wrap) - winfocus = TAILQ_FIRST(wl); - else - winfocus = TAILQ_PREV(cur_focus, - ws_win_list, entry); + winfocus = TAILQ_NEXT(cur_focus, entry); + while (winfocus && winfocus->iconic) + winfocus = TAILQ_NEXT(winfocus, entry); + + if (winfocus == NULL) { + if (focus_close_wrap) { + TAILQ_FOREACH(winfocus, wl, entry) + if (!winfocus->iconic && + winfocus != cur_focus) + break; + } else { + TAILQ_FOREACH_REVERSE(winfocus, wl, + ws_win_list, entry) + if (!winfocus->iconic && + winfocus != cur_focus) + break; + } } break; case SWM_STACK_BELOW: - if ((winfocus = TAILQ_PREV(cur_focus, ws_win_list, - entry)) == NULL) { - if (focus_close_wrap) - winfocus = TAILQ_LAST(wl, ws_win_list); - else - winfocus = TAILQ_NEXT(cur_focus, entry); + winfocus = TAILQ_PREV(cur_focus, ws_win_list, entry); + while (winfocus && winfocus->iconic) + winfocus = TAILQ_PREV(winfocus, ws_win_list, + entry); + + if (winfocus == NULL) { + if (focus_close_wrap) { + TAILQ_FOREACH_REVERSE(winfocus, wl, + ws_win_list, entry) + if (!winfocus->iconic && + winfocus != cur_focus) + break; + } else { + TAILQ_FOREACH(winfocus, wl, entry) + if (!winfocus->iconic && + winfocus != cur_focus) + break; + } } break; } } done: - if (winfocus == NULL) { - if (focus_default == SWM_STACK_TOP) - winfocus = TAILQ_LAST(wl, ws_win_list); - else - winfocus = TAILQ_FIRST(wl); + if (winfocus == NULL || + (winfocus && (winfocus->iconic || winfocus == cur_focus))) { + if (focus_default == SWM_STACK_TOP) { + TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list, entry) + if (!winfocus->iconic && winfocus != cur_focus) + break; + } else { + TAILQ_FOREACH(winfocus, wl, entry) + if (!winfocus->iconic && winfocus != cur_focus) + break; + } } kill_refs(win); @@ -3753,7 +3960,7 @@ cycle_layout(struct swm_region *r, union arg *args) ws->cur_layout = &layouts[0]; stack(); - bar_update(); + bar_draw(); focus_win(get_region_focus(r)); @@ -3773,7 +3980,7 @@ stack_config(struct swm_region *r, union arg *args) if (args->id != SWM_ARG_ID_STACKINIT) stack(); - bar_update(); + bar_draw(); focus_flush(); } @@ -3789,7 +3996,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; @@ -3798,10 +4005,12 @@ stack(void) { DNPRINTF(SWM_D_STACK, "stack: workspace: %d " "(screen: %d, region: %d)\n", r->ws->idx, i, j++); - /* start with screen geometry, adjust for bar */ + /* Adjust stack area for region bar and padding. */ g = r->g; - g.w -= 2 * border_width; - g.h -= 2 * border_width; + g.x += region_padding; + g.y += region_padding; + g.w -= 2 * border_width + 2 * region_padding; + g.h -= 2 * border_width + 2 * region_padding; if (bar_enabled && r->ws->bar_enabled) { if (!bar_at_bottom) g.y += bar_height; @@ -4037,9 +4246,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) /* stack all the tiled windows */ i = j = 0, s = stacks; TAILQ_FOREACH(win, &ws->winlist, entry) { - if (win->transient || win->floating) - continue; - if (win->iconic) + if (win->transient || win->floating || win->iconic) continue; if (win->ewmh_flags & EWMH_F_FULLSCREEN) { @@ -4057,16 +4264,20 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) if (flip) win_g.x = r_g.x; else - win_g.x += win_g.w + 2 * border_width; + win_g.x += win_g.w + 2 * border_width + + tile_gap; win_g.w = (r_g.w - msize - - (stacks * 2 * border_width)) / stacks; + (stacks * (2 * border_width + tile_gap))) / stacks; if (s == 1) win_g.w += (r_g.w - msize - - (stacks * 2 * border_width)) % stacks; + (stacks * (2 * border_width + tile_gap))) % + stacks; s--; j = 0; } - win_g.h = hrh - 2 * border_width; + + win_g.h = hrh - 2 * border_width - tile_gap; + if (rot) { h_inc = win->sh.width_inc; h_base = win->sh.base_width; @@ -4074,6 +4285,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) 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) { @@ -4093,7 +4305,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) if (j == 0) win_g.y = r_g.y; else - win_g.y += last_h + 2 * border_width; + win_g.y += last_h + 2 * border_width + tile_gap; if (disable_border && !(bar_enabled && ws->bar_enabled) && winno == 1){ @@ -4103,6 +4315,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) } else { bordered = 1; } + if (rot) { if (X(win) != win_g.y || Y(win) != win_g.x || WIDTH(win) != win_g.h || HEIGHT(win) != win_g.w) { @@ -4270,7 +4483,7 @@ max_stack(struct workspace *ws, struct swm_geometry *g) { struct swm_geometry gg = *g; struct ws_win *w, *win = NULL, *parent = NULL; - int winno, num_screens; + int winno; DNPRINTF(SWM_D_STACK, "max_stack: workspace: %d\n", ws->idx); @@ -4286,6 +4499,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); @@ -4295,11 +4510,13 @@ max_stack(struct workspace *ws, struct swm_geometry *g) DNPRINTF(SWM_D_STACK, "max_stack: win: 0x%x\n", win->id); /* maximize all top level windows */ - num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); TAILQ_FOREACH(w, &ws->winlist, entry) { if (w->transient || w->iconic) continue; + if (!w->mapped && w != win) + map_window(w); + if (w->floating && !w->floatmaxed) { /* * retain geometry for retrieval on exit from @@ -4323,14 +4540,9 @@ max_stack(struct workspace *ws, struct swm_geometry *g) update_window(w); } - - /* Unmap unwanted windows if not multi-screen. */ - if (!(num_screens > 1 || outputs > 1) && (w != win || - w != parent || w->transient != win->id)) - unmap_window(w); } - /* If a parent exists, map it first. */ + /* If a parent exists, map/raise it first. */ if (parent) { map_window(parent); @@ -4343,10 +4555,10 @@ max_stack(struct workspace *ws, struct swm_geometry *g) } } - /* Map focused window. */ + /* Map/raise focused window. */ map_window(win); - /* Finally, map children of focus window. */ + /* Finally, map/raise children of focus window. */ TAILQ_FOREACH(w, &ws->winlist, entry) if (w->transient == win->id && !w->iconic) { stack_floater(w, ws->r); @@ -4355,6 +4567,32 @@ max_stack(struct workspace *ws, struct swm_geometry *g) } void +send_to_rg(struct swm_region *r, union arg *args) +{ + int ridx = args->id, i, num_screens; + struct swm_region *rr = NULL; + union arg a; + + num_screens = get_screen_count(); + /* do nothing if we don't have more than one screen */ + if (!(num_screens > 1 || outputs > 1)) + return; + + DNPRINTF(SWM_D_FOCUS, "send_to_rg: id: %d\n", ridx); + + rr = TAILQ_FIRST(&r->s->rl); + for (i = 0; i < ridx; ++i) + rr = TAILQ_NEXT(rr, entry); + + if (rr == NULL) + return; + + a.id = rr->ws->idx; + + send_to_ws(r, &a); +} + +void send_to_ws(struct swm_region *r, union arg *args) { int wsid = args->id; @@ -4397,11 +4635,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 */ @@ -4446,8 +4683,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(); @@ -4517,7 +4760,7 @@ get_win_name(xcb_window_t win) free(r); /* Use WM_NAME instead; no UTF-8. */ c = xcb_get_property(conn, 0, win, XCB_ATOM_WM_NAME, - XCB_GET_PROPERTY_TYPE_ANY, 0, UINT_MAX); + XCB_GET_PROPERTY_TYPE_ANY, 0, UINT_MAX); r = xcb_get_property_reply(conn, c, NULL); if (!r) @@ -4529,7 +4772,7 @@ get_win_name(xcb_window_t win) } if (r->length > 0) name = strndup(xcb_get_property_value(r), - xcb_get_property_value_length(r)); + xcb_get_property_value_length(r)); free(r); } @@ -4759,7 +5002,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; @@ -4789,7 +5032,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; @@ -4885,7 +5128,7 @@ search_do_resp(void) { ssize_t rbytes; char *resp; - unsigned long len; + size_t len; DNPRINTF(SWM_D_MISC, "search_do_resp:\n"); @@ -5085,7 +5328,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; @@ -5115,6 +5358,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; @@ -5171,7 +5420,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"); @@ -5234,6 +5483,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); @@ -5243,7 +5498,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"); @@ -5271,7 +5526,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; @@ -5302,6 +5557,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: @@ -5344,7 +5605,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"); @@ -5366,6 +5627,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); @@ -5375,6 +5642,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"); @@ -5424,6 +5692,15 @@ struct keyfunc { { "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} }, + { "mvrg_1", send_to_rg, {.id = 0} }, + { "mvrg_2", send_to_rg, {.id = 1} }, + { "mvrg_3", send_to_rg, {.id = 2} }, + { "mvrg_4", send_to_rg, {.id = 3} }, + { "mvrg_5", send_to_rg, {.id = 4} }, + { "mvrg_6", send_to_rg, {.id = 5} }, + { "mvrg_7", send_to_rg, {.id = 6} }, + { "mvrg_8", send_to_rg, {.id = 7} }, + { "mvrg_9", send_to_rg, {.id = 8} }, { "mvws_1", send_to_ws, {.id = 0} }, { "mvws_2", send_to_ws, {.id = 1} }, { "mvws_3", send_to_ws, {.id = 2} }, @@ -5450,8 +5727,19 @@ struct keyfunc { { "quit", quit, {0} }, { "raise_toggle", raise_toggle, {0} }, { "restart", restart, {0} }, - { "screen_next", cyclescr, {.id = SWM_ARG_ID_CYCLESC_UP} }, - { "screen_prev", cyclescr, {.id = SWM_ARG_ID_CYCLESC_DOWN} }, + { "rg_1", focusrg, {.id = 0} }, + { "rg_2", focusrg, {.id = 1} }, + { "rg_3", focusrg, {.id = 2} }, + { "rg_4", focusrg, {.id = 3} }, + { "rg_5", focusrg, {.id = 4} }, + { "rg_6", focusrg, {.id = 5} }, + { "rg_7", focusrg, {.id = 6} }, + { "rg_8", focusrg, {.id = 7} }, + { "rg_9", focusrg, {.id = 8} }, + { "rg_next", cyclerg, {.id = SWM_ARG_ID_CYCLERG_UP} }, + { "rg_prev", cyclerg, {.id = SWM_ARG_ID_CYCLERG_DOWN} }, + { "screen_next", cyclerg, {.id = SWM_ARG_ID_CYCLERG_UP} }, + { "screen_prev", cyclerg, {.id = SWM_ARG_ID_CYCLERG_DOWN} }, { "search_win", search_win, {0} }, { "search_workspace", search_workspace, {0} }, { "spawn_custom", NULL, {0} }, @@ -5491,8 +5779,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} }, @@ -5611,6 +5901,13 @@ spawn_expand(struct swm_region *r, union arg *args, const char *spawn_name, strdup(r->s->c[SWM_S_COLOR_UNFOCUS].name)) == NULL) err(1, "spawn_custom color unfocus"); + } else if (!strcasecmp(ap, "$region_index")) { + if (asprintf(&real_args[i], "%d", + get_region_index(r) + 1) < 1) + err(1, "spawn_custom region index"); + } else if (!strcasecmp(ap, "$workspace_index")) { + if (asprintf(&real_args[i], "%d", r->ws->idx + 1) < 1) + err(1, "spawn_custom workspace index"); } else { /* no match --> copy as is */ if ((real_args[i] = strdup(ap)) == NULL) @@ -5672,9 +5969,9 @@ spawn_select(struct swm_region *r, union arg *args, const char *spawn_name, err(1, "cannot fork"); break; case 0: /* child */ - if (dup2(select_list_pipe[0], 0) == -1) + if (dup2(select_list_pipe[0], STDIN_FILENO) == -1) err(1, "dup2"); - if (dup2(select_resp_pipe[1], 1) == -1) + if (dup2(select_resp_pipe[1], STDOUT_FILENO) == -1) err(1, "dup2"); close(select_list_pipe[1]); close(select_resp_pipe[0]); @@ -5692,7 +5989,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; @@ -5721,6 +6018,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"); } @@ -5743,18 +6042,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; @@ -5763,38 +6051,31 @@ 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"); } int setconfspawn(char *selector, char *value, int flags) { - char *args; - - /* suppress unused warning since var is needed */ - (void)flags; + char *args; args = expand_tilde(value); DNPRINTF(SWM_D_SPAWN, "setconfspawn: [%s] [%s]\n", selector, args); - setspawn(selector, args); + setspawn(selector, args, flags); free(args); DNPRINTF(SWM_D_SPAWN, "setconfspawn: done.\n"); @@ -5802,20 +6083,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("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" @@ -5823,6 +6141,7 @@ setup_spawn(void) " -nf $bar_font_color" " -sb $bar_border" " -sf $bar_color", 0); + setconfspawn("name_workspace", "dmenu" " -p Workspace" " -fn $bar_font" @@ -5830,6 +6149,11 @@ setup_spawn(void) " -nf $bar_font_color" " -sb $bar_border" " -sf $bar_color", 0); + + /* 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 */ @@ -6003,8 +6327,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); @@ -6015,8 +6339,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); @@ -6033,25 +6357,94 @@ void setup_keys(void) { #define MODKEY_SHIFT MODKEY | XCB_MOD_MASK_SHIFT - setkeybinding(MODKEY, XK_space, KF_CYCLE_LAYOUT,NULL); - setkeybinding(MODKEY_SHIFT, XK_backslash, KF_FLIP_LAYOUT, NULL); - setkeybinding(MODKEY_SHIFT, XK_space, KF_STACK_RESET, NULL); + setkeybinding(MODKEY, XK_b, KF_BAR_TOGGLE, NULL); + setkeybinding(MODKEY_SHIFT, XK_b, KF_BAR_TOGGLE_WS,NULL); + setkeybinding(MODKEY, XK_v, KF_BUTTON2, NULL); + setkeybinding(MODKEY, XK_space, KF_CYCLE_LAYOUT,NULL); + setkeybinding(MODKEY_SHIFT, XK_backslash, KF_FLIP_LAYOUT, NULL); + setkeybinding(MODKEY, XK_t, KF_FLOAT_TOGGLE,NULL); + setkeybinding(MODKEY, XK_m, KF_FOCUS_MAIN, NULL); + setkeybinding(MODKEY, XK_j, KF_FOCUS_NEXT, NULL); + setkeybinding(MODKEY, XK_Tab, KF_FOCUS_NEXT, NULL); + setkeybinding(MODKEY, XK_k, KF_FOCUS_PREV, NULL); + setkeybinding(MODKEY_SHIFT, XK_Tab, KF_FOCUS_PREV, NULL); + setkeybinding(MODKEY_SHIFT, XK_equal, KF_HEIGHT_GROW,NULL); + setkeybinding(MODKEY_SHIFT, XK_minus, KF_HEIGHT_SHRINK,NULL); + setkeybinding(MODKEY, XK_w, KF_ICONIFY, 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_SHIFT, XK_bracketright,KF_MOVE_DOWN,NULL); + setkeybinding(MODKEY, XK_bracketleft, KF_MOVE_LEFT,NULL); + setkeybinding(MODKEY, XK_bracketright,KF_MOVE_RIGHT,NULL); + setkeybinding(MODKEY_SHIFT, XK_bracketleft, KF_MOVE_UP, NULL); + setkeybinding(MODKEY_SHIFT, XK_KP_End, KF_MVRG_1, NULL); + setkeybinding(MODKEY_SHIFT, XK_KP_Down, KF_MVRG_2, NULL); + setkeybinding(MODKEY_SHIFT, XK_KP_Next, KF_MVRG_3, NULL); + setkeybinding(MODKEY_SHIFT, XK_KP_Left, KF_MVRG_4, NULL); + setkeybinding(MODKEY_SHIFT, XK_KP_Begin, KF_MVRG_5, NULL); + setkeybinding(MODKEY_SHIFT, XK_KP_Right, KF_MVRG_6, NULL); + setkeybinding(MODKEY_SHIFT, XK_KP_Home, KF_MVRG_7, NULL); + setkeybinding(MODKEY_SHIFT, XK_KP_Up, KF_MVRG_8, NULL); + setkeybinding(MODKEY_SHIFT, XK_KP_Prior, KF_MVRG_9, NULL); + setkeybinding(MODKEY_SHIFT, XK_1, KF_MVWS_1, NULL); + setkeybinding(MODKEY_SHIFT, XK_2, KF_MVWS_2, NULL); + setkeybinding(MODKEY_SHIFT, XK_3, KF_MVWS_3, NULL); + setkeybinding(MODKEY_SHIFT, XK_4, KF_MVWS_4, NULL); + setkeybinding(MODKEY_SHIFT, XK_5, KF_MVWS_5, NULL); + setkeybinding(MODKEY_SHIFT, XK_6, KF_MVWS_6, NULL); + setkeybinding(MODKEY_SHIFT, XK_7, KF_MVWS_7, NULL); + setkeybinding(MODKEY_SHIFT, XK_8, KF_MVWS_8, NULL); + setkeybinding(MODKEY_SHIFT, XK_9, KF_MVWS_9, NULL); + setkeybinding(MODKEY_SHIFT, XK_0, KF_MVWS_10, NULL); + setkeybinding(MODKEY_SHIFT, XK_F1, KF_MVWS_11, NULL); + setkeybinding(MODKEY_SHIFT, XK_F2, KF_MVWS_12, NULL); + setkeybinding(MODKEY_SHIFT, XK_F3, KF_MVWS_13, NULL); + setkeybinding(MODKEY_SHIFT, XK_F4, KF_MVWS_14, NULL); + setkeybinding(MODKEY_SHIFT, XK_F5, KF_MVWS_15, NULL); + setkeybinding(MODKEY_SHIFT, XK_F6, KF_MVWS_16, NULL); + setkeybinding(MODKEY_SHIFT, XK_F7, KF_MVWS_17, NULL); + setkeybinding(MODKEY_SHIFT, XK_F8, KF_MVWS_18, NULL); + setkeybinding(MODKEY_SHIFT, XK_F9, KF_MVWS_19, NULL); + setkeybinding(MODKEY_SHIFT, XK_F10, KF_MVWS_20, NULL); + setkeybinding(MODKEY_SHIFT, XK_F11, KF_MVWS_21, NULL); + setkeybinding(MODKEY_SHIFT, XK_F12, KF_MVWS_22, NULL); + setkeybinding(MODKEY_SHIFT, XK_slash, KF_NAME_WORKSPACE,NULL); + setkeybinding(MODKEY_SHIFT, XK_q, KF_QUIT, NULL); + setkeybinding(MODKEY_SHIFT, XK_r, KF_RAISE_TOGGLE,NULL); + setkeybinding(MODKEY, XK_q, KF_RESTART, NULL); + setkeybinding(MODKEY, XK_KP_End, KF_RG_1, NULL); + setkeybinding(MODKEY, XK_KP_Down, KF_RG_2, NULL); + setkeybinding(MODKEY, XK_KP_Next, KF_RG_3, NULL); + setkeybinding(MODKEY, XK_KP_Left, KF_RG_4, NULL); + setkeybinding(MODKEY, XK_KP_Begin, KF_RG_5, NULL); + setkeybinding(MODKEY, XK_KP_Right, KF_RG_6, NULL); + setkeybinding(MODKEY, XK_KP_Home, KF_RG_7, NULL); + setkeybinding(MODKEY, XK_KP_Up, KF_RG_8, NULL); + setkeybinding(MODKEY, XK_KP_Prior, KF_RG_9, NULL); + setkeybinding(MODKEY_SHIFT, XK_Right, KF_RG_NEXT, NULL); + setkeybinding(MODKEY_SHIFT, XK_Left, KF_RG_PREV, NULL); + setkeybinding(MODKEY, XK_f, KF_SEARCH_WIN, NULL); + setkeybinding(MODKEY, XK_slash, KF_SEARCH_WORKSPACE,NULL); + setkeybinding(MODKEY_SHIFT, XK_i, KF_SPAWN_CUSTOM,"initscr"); + setkeybinding(MODKEY_SHIFT, XK_Delete, KF_SPAWN_CUSTOM,"lock"); + setkeybinding(MODKEY, XK_p, KF_SPAWN_CUSTOM,"menu"); + setkeybinding(MODKEY, XK_s, KF_SPAWN_CUSTOM,"screenshot_all"); + setkeybinding(MODKEY_SHIFT, XK_s, KF_SPAWN_CUSTOM,"screenshot_wind"); + setkeybinding(MODKEY_SHIFT, XK_Return, KF_SPAWN_CUSTOM,"term"); setkeybinding(MODKEY_SHIFT, XK_comma, KF_STACK_INC, NULL); setkeybinding(MODKEY_SHIFT, XK_period, KF_STACK_DEC, NULL); + setkeybinding(MODKEY_SHIFT, XK_space, KF_STACK_RESET, 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_SHIFT, XK_j, KF_SWAP_NEXT, NULL); setkeybinding(MODKEY_SHIFT, XK_k, KF_SWAP_PREV, NULL); - setkeybinding(MODKEY_SHIFT, XK_Return, KF_SPAWN_CUSTOM,"term"); - setkeybinding(MODKEY, XK_p, KF_SPAWN_CUSTOM,"menu"); - setkeybinding(MODKEY_SHIFT, XK_q, KF_QUIT, NULL); - setkeybinding(MODKEY, XK_q, KF_RESTART, NULL); - setkeybinding(MODKEY, XK_m, KF_FOCUS_MAIN, NULL); + setkeybinding(MODKEY_SHIFT, XK_w, KF_UNICONIFY, NULL); + setkeybinding(MODKEY_SHIFT, XK_v, KF_VERSION, NULL); + setkeybinding(MODKEY, XK_equal, KF_WIDTH_GROW, NULL); + setkeybinding(MODKEY, XK_minus, KF_WIDTH_SHRINK,NULL); + setkeybinding(MODKEY, XK_x, KF_WIND_DEL, NULL); + setkeybinding(MODKEY_SHIFT, XK_x, KF_WIND_KILL, 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); @@ -6078,58 +6471,9 @@ 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); - setkeybinding(MODKEY_SHIFT, XK_Right, KF_SCREEN_NEXT, NULL); - setkeybinding(MODKEY_SHIFT, XK_Left, KF_SCREEN_PREV, NULL); - setkeybinding(MODKEY_SHIFT, XK_1, KF_MVWS_1, NULL); - setkeybinding(MODKEY_SHIFT, XK_2, KF_MVWS_2, NULL); - setkeybinding(MODKEY_SHIFT, XK_3, KF_MVWS_3, NULL); - setkeybinding(MODKEY_SHIFT, XK_4, KF_MVWS_4, NULL); - setkeybinding(MODKEY_SHIFT, XK_5, KF_MVWS_5, NULL); - setkeybinding(MODKEY_SHIFT, XK_6, KF_MVWS_6, NULL); - setkeybinding(MODKEY_SHIFT, XK_7, KF_MVWS_7, NULL); - setkeybinding(MODKEY_SHIFT, XK_8, KF_MVWS_8, NULL); - setkeybinding(MODKEY_SHIFT, XK_9, KF_MVWS_9, NULL); - setkeybinding(MODKEY_SHIFT, XK_0, KF_MVWS_10, NULL); - setkeybinding(MODKEY_SHIFT, XK_F1, KF_MVWS_11, NULL); - setkeybinding(MODKEY_SHIFT, XK_F2, KF_MVWS_12, NULL); - setkeybinding(MODKEY_SHIFT, XK_F3, KF_MVWS_13, NULL); - setkeybinding(MODKEY_SHIFT, XK_F4, KF_MVWS_14, NULL); - setkeybinding(MODKEY_SHIFT, XK_F5, KF_MVWS_15, NULL); - setkeybinding(MODKEY_SHIFT, XK_F6, KF_MVWS_16, NULL); - setkeybinding(MODKEY_SHIFT, XK_F7, KF_MVWS_17, NULL); - setkeybinding(MODKEY_SHIFT, XK_F8, KF_MVWS_18, NULL); - setkeybinding(MODKEY_SHIFT, XK_F9, KF_MVWS_19, NULL); - setkeybinding(MODKEY_SHIFT, XK_F10, KF_MVWS_20, NULL); - setkeybinding(MODKEY_SHIFT, XK_F11, KF_MVWS_21, NULL); - setkeybinding(MODKEY_SHIFT, XK_F12, KF_MVWS_22, NULL); - setkeybinding(MODKEY, XK_b, KF_BAR_TOGGLE, NULL); - setkeybinding(MODKEY_SHIFT, XK_b, KF_BAR_TOGGLE_WS,NULL); - setkeybinding(MODKEY, XK_Tab, KF_FOCUS_NEXT, NULL); - setkeybinding(MODKEY_SHIFT, XK_Tab, KF_FOCUS_PREV, NULL); - setkeybinding(MODKEY_SHIFT, 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_SHIFT, XK_s, KF_SPAWN_CUSTOM,"screenshot_wind"); - setkeybinding(MODKEY, XK_t, KF_FLOAT_TOGGLE,NULL); - setkeybinding(MODKEY_SHIFT, XK_v, KF_VERSION, NULL); - setkeybinding(MODKEY_SHIFT, XK_Delete, KF_SPAWN_CUSTOM,"lock"); - setkeybinding(MODKEY_SHIFT, XK_i, KF_SPAWN_CUSTOM,"initscr"); - setkeybinding(MODKEY, XK_w, KF_ICONIFY, NULL); - setkeybinding(MODKEY_SHIFT, XK_w, KF_UNICONIFY, NULL); - setkeybinding(MODKEY_SHIFT, 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_SHIFT, XK_equal, KF_HEIGHT_GROW,NULL); - setkeybinding(MODKEY_SHIFT, XK_minus, KF_HEIGHT_SHRINK,NULL); - setkeybinding(MODKEY, XK_bracketleft, KF_MOVE_LEFT,NULL); - setkeybinding(MODKEY, XK_bracketright,KF_MOVE_RIGHT,NULL); - setkeybinding(MODKEY_SHIFT, XK_bracketleft, KF_MOVE_UP, NULL); - setkeybinding(MODKEY_SHIFT, XK_bracketright,KF_MOVE_DOWN,NULL); - setkeybinding(MODKEY_SHIFT, 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_SHIFT, XK_d, KF_DUMPWINS, NULL); #endif @@ -6150,24 +6494,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); } @@ -6218,13 +6563,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++) @@ -6232,7 +6587,7 @@ grabkeys(void) screens[k].root, kp->mod | modifiers[j], *code, XCB_GRAB_MODE_SYNC, - XCB_GRAB_MODE_ASYNC); + XCB_GRAB_MODE_SYNC); free(code); } } @@ -6271,6 +6626,8 @@ const char *quirkname[] = { "XTERM_FONTADJ", "FULLSCREEN", "FOCUSPREV", + "NOFOCUSONMAP", + "FOCUSONMAP_SINGLE", }; /* SWM_Q_WS: retain '|' for back compat for now (2009-08-11) */ @@ -6435,6 +6792,7 @@ enum { SWM_S_BAR_BORDER_WIDTH, SWM_S_BAR_DELAY, SWM_S_BAR_ENABLED, + SWM_S_BAR_ENABLED_WS, SWM_S_BAR_FONT, SWM_S_BAR_FORMAT, SWM_S_BAR_JUSTIFY, @@ -6449,12 +6807,14 @@ enum { SWM_S_FOCUS_CLOSE_WRAP, SWM_S_FOCUS_DEFAULT, SWM_S_FOCUS_MODE, + SWM_S_REGION_PADDING, SWM_S_SPAWN_ORDER, SWM_S_SPAWN_TERM, SWM_S_SS_APP, SWM_S_SS_ENABLED, SWM_S_STACK_ENABLED, SWM_S_TERM_WIDTH, + SWM_S_TILE_GAP, SWM_S_TITLE_CLASS_ENABLED, SWM_S_TITLE_NAME_ENABLED, SWM_S_URGENT_ENABLED, @@ -6466,8 +6826,9 @@ enum { int setconfvalue(char *selector, char *value, int flags) { - int i; - char *b; + struct workspace *ws; + int i, ws_id, num_screens; + char *b; /* suppress unused warning since var is needed */ (void)selector; @@ -6487,11 +6848,23 @@ setconfvalue(char *selector, char *value, int flags) bar_border_width = 0; break; case SWM_S_BAR_DELAY: - bar_delay = atoi(value); + /* No longer needed; leave to not break old conf files. */ break; case SWM_S_BAR_ENABLED: bar_enabled = atoi(value); break; + case SWM_S_BAR_ENABLED_WS: + ws_id = atoi(selector) - 1; + if (ws_id < 0 || ws_id >= workspace_limit) + errx(1, "setconfvalue: bar_enabled_ws: invalid " + "workspace %d.", ws_id + 1); + + 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); + } + break; case SWM_S_BAR_FONT: b = bar_fonts; if (asprintf(&bar_fonts, "%s,%s", value, bar_fonts) == -1) @@ -6591,6 +6964,11 @@ setconfvalue(char *selector, char *value, int flags) else errx(1, "focus_mode"); break; + case SWM_S_REGION_PADDING: + region_padding = atoi(value); + if (region_padding < 0) + region_padding = 0; + break; case SWM_S_SPAWN_ORDER: if (!strcmp(value, "first")) spawn_position = SWM_STACK_BOTTOM; @@ -6608,9 +6986,10 @@ setconfvalue(char *selector, char *value, int flags) setconfspawn("spawn_term", value, 0); break; case SWM_S_SS_APP: + /* No longer needed; leave to not break old conf files. */ break; case SWM_S_SS_ENABLED: - ss_enabled = atoi(value); + /* No longer needed; leave to not break old conf files. */ break; case SWM_S_STACK_ENABLED: stack_enabled = atoi(value); @@ -6620,6 +6999,11 @@ setconfvalue(char *selector, char *value, int flags) if (term_width < 0) term_width = 0; break; + case SWM_S_TILE_GAP: + tile_gap = atoi(value); + if (tile_gap < 0) + tile_gap = 0; + break; case SWM_S_TITLE_CLASS_ENABLED: title_class_enabled = atoi(value); break; @@ -6806,7 +7190,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]; @@ -6856,57 +7240,86 @@ struct config_option { int funcflags; }; struct config_option configopt[] = { - { "bar_enabled", setconfvalue, SWM_S_BAR_ENABLED }, + { "autorun", setautorun, 0 }, + { "bar_action", setconfvalue, SWM_S_BAR_ACTION }, { "bar_at_bottom", setconfvalue, SWM_S_BAR_AT_BOTTOM }, { "bar_border", setconfcolor, SWM_S_COLOR_BAR_BORDER }, + { "bar_border_unfocus", setconfcolor, SWM_S_COLOR_BAR_BORDER_UNFOCUS }, { "bar_border_width", setconfvalue, SWM_S_BAR_BORDER_WIDTH }, { "bar_color", setconfcolor, SWM_S_COLOR_BAR }, - { "bar_font_color", setconfcolor, SWM_S_COLOR_BAR_FONT }, - { "bar_font", setconfvalue, SWM_S_BAR_FONT }, - { "bar_action", setconfvalue, SWM_S_BAR_ACTION }, { "bar_delay", setconfvalue, SWM_S_BAR_DELAY }, - { "bar_justify", setconfvalue, SWM_S_BAR_JUSTIFY }, + { "bar_enabled", setconfvalue, SWM_S_BAR_ENABLED }, + { "bar_enabled_ws", setconfvalue, SWM_S_BAR_ENABLED_WS }, + { "bar_font", setconfvalue, SWM_S_BAR_FONT }, + { "bar_font_color", setconfcolor, SWM_S_COLOR_BAR_FONT }, { "bar_format", setconfvalue, SWM_S_BAR_FORMAT }, - { "keyboard_mapping", setkeymapping, 0 }, + { "bar_justify", setconfvalue, SWM_S_BAR_JUSTIFY }, { "bind", setconfbinding, 0 }, - { "stack_enabled", setconfvalue, SWM_S_STACK_ENABLED }, + { "border_width", setconfvalue, SWM_S_BORDER_WIDTH }, { "clock_enabled", setconfvalue, SWM_S_CLOCK_ENABLED }, { "clock_format", setconfvalue, SWM_S_CLOCK_FORMAT }, { "color_focus", setconfcolor, SWM_S_COLOR_FOCUS }, { "color_unfocus", setconfcolor, SWM_S_COLOR_UNFOCUS }, { "cycle_empty", setconfvalue, SWM_S_CYCLE_EMPTY }, { "cycle_visible", setconfvalue, SWM_S_CYCLE_VISIBLE }, - { "workspace_limit", setconfvalue, SWM_S_WORKSPACE_LIMIT }, { "dialog_ratio", setconfvalue, SWM_S_DIALOG_RATIO }, - { "verbose_layout", setconfvalue, SWM_S_VERBOSE_LAYOUT }, + { "disable_border", setconfvalue, SWM_S_DISABLE_BORDER }, + { "focus_close", setconfvalue, SWM_S_FOCUS_CLOSE }, + { "focus_close_wrap", setconfvalue, SWM_S_FOCUS_CLOSE_WRAP }, + { "focus_default", setconfvalue, SWM_S_FOCUS_DEFAULT }, + { "focus_mode", setconfvalue, SWM_S_FOCUS_MODE }, + { "keyboard_mapping", setkeymapping, 0 }, + { "layout", setlayout, 0 }, { "modkey", setconfmodkey, 0 }, { "program", setconfspawn, 0 }, { "quirk", setconfquirk, 0 }, { "region", setconfregion, 0 }, - { "spawn_term", setconfvalue, SWM_S_SPAWN_TERM }, - { "screenshot_enabled", setconfvalue, SWM_S_SS_ENABLED }, + { "region_padding", setconfvalue, SWM_S_REGION_PADDING }, { "screenshot_app", setconfvalue, SWM_S_SS_APP }, - { "window_name_enabled", setconfvalue, SWM_S_WINDOW_NAME_ENABLED }, - { "urgent_enabled", setconfvalue, SWM_S_URGENT_ENABLED }, + { "screenshot_enabled", setconfvalue, SWM_S_SS_ENABLED }, + { "spawn_position", setconfvalue, SWM_S_SPAWN_ORDER }, + { "spawn_term", setconfvalue, SWM_S_SPAWN_TERM }, + { "stack_enabled", setconfvalue, SWM_S_STACK_ENABLED }, { "term_width", setconfvalue, SWM_S_TERM_WIDTH }, + { "tile_gap", setconfvalue, SWM_S_TILE_GAP }, { "title_class_enabled", setconfvalue, SWM_S_TITLE_CLASS_ENABLED }, { "title_name_enabled", setconfvalue, SWM_S_TITLE_NAME_ENABLED }, - { "focus_mode", setconfvalue, SWM_S_FOCUS_MODE }, - { "focus_close", setconfvalue, SWM_S_FOCUS_CLOSE }, - { "focus_close_wrap", setconfvalue, SWM_S_FOCUS_CLOSE_WRAP }, - { "focus_default", setconfvalue, SWM_S_FOCUS_DEFAULT }, - { "spawn_position", setconfvalue, SWM_S_SPAWN_ORDER }, - { "disable_border", setconfvalue, SWM_S_DISABLE_BORDER }, - { "border_width", setconfvalue, SWM_S_BORDER_WIDTH }, - { "autorun", setautorun, 0 }, - { "layout", setlayout, 0 }, + { "urgent_enabled", setconfvalue, SWM_S_URGENT_ENABLED }, + { "verbose_layout", setconfvalue, SWM_S_VERBOSE_LAYOUT }, + { "window_name_enabled", setconfvalue, SWM_S_WINDOW_NAME_ENABLED }, + { "workspace_limit", setconfvalue, SWM_S_WORKSPACE_LIMIT }, }; +void +_add_startup_exception(const char *fmt, va_list ap) +{ + if (vasprintf(&startup_exception, fmt, ap) == -1) + warn("%s: asprintf", __func__); +} + +void +add_startup_exception(const char *fmt, ...) +{ + va_list ap; + + nr_exceptions++; + + if (startup_exception) + return; + + /* force bar to be enabled due to exception */ + bar_enabled = 1; + + va_start(ap, fmt); + _add_startup_exception(fmt, ap); + va_end(ap); +} + int conf_load(const char *filename, int keymapping) { FILE *config; - char *line, *cp, *optsub, *optval; + char *line = NULL, *cp, *optsub, *optval = NULL; size_t linelen, lineno = 0; int wordlen, i, optidx; struct config_option *opt = NULL; @@ -6917,14 +7330,20 @@ 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); } while (!feof(config)) { - if ((line = fparseln(config, &linelen, &lineno, NULL, 0)) - == NULL) { + if (line) + free(line); + + if ((line = fparseln(config, &linelen, &lineno, NULL, + FPARSELN_UNESCCOMM | FPARSELN_UNESCCONT)) == NULL) { if (ferror(config)) err(1, "%s", filename); else @@ -6934,15 +7353,14 @@ conf_load(const char *filename, int keymapping) cp += strspn(cp, " \t\n"); /* eat whitespace */ if (cp[0] == '\0') { /* empty line */ - free(line); continue; } /* get config option */ wordlen = strcspn(cp, "=[ \t\n"); if (wordlen == 0) { - warnx("%s: line %zd: no option found", + add_startup_exception("%s: line %zd: no option found", filename, lineno); - goto out; + continue; } optidx = -1; for (i = 0; i < LENGTH(configopt); i++) { @@ -6954,17 +7372,20 @@ conf_load(const char *filename, int keymapping) } } if (optidx == -1) { - warnx("%s: line %zd: unknown option %.*s", - filename, lineno, wordlen, cp); - goto out; + add_startup_exception("%s: line %zd: unknown option " + "%.*s", filename, lineno, wordlen, cp); + continue; } if (keymapping && opt && strcmp(opt->optname, "bind")) { - warnx("%s: line %zd: invalid option %.*s", - filename, lineno, wordlen, cp); - goto out; + add_startup_exception("%s: line %zd: invalid option " + "%.*s", filename, lineno, wordlen, cp); + continue; } cp += wordlen; cp += strspn(cp, " \t\n"); /* eat whitespace */ + + /* from here on out we call goto invalid to continue */ + /* get [selector] if any */ optsub = NULL; if (*cp == '[') { @@ -6972,17 +7393,17 @@ conf_load(const char *filename, int keymapping) wordlen = strcspn(cp, "]"); if (*cp != ']') { if (wordlen == 0) { - warnx("%s: line %zd: syntax error", - filename, lineno); - goto out; + add_startup_exception("%s: line %zd: " + "syntax error", filename, lineno); + goto invalid; } if (asprintf(&optsub, "%.*s", wordlen, cp) == -1) { - warnx("%s: line %zd: unable to allocate" - "memory for selector", filename, - lineno); - goto out; + add_startup_exception("%s: line %zd: " + "unable to allocatememory for " + "selector", filename, lineno); + goto invalid; } } cp += wordlen; @@ -6991,27 +7412,36 @@ conf_load(const char *filename, int keymapping) cp += strspn(cp, "= \t\n"); /* eat trailing */ /* get RHS value */ optval = strdup(cp); + if (strlen(optval) == 0) { + add_startup_exception("%s: line %zd: must supply value " + "to %s", filename, lineno, + configopt[optidx].optname); + goto invalid; + } /* call function to deal with it all */ if (configopt[optidx].func(optsub, optval, - configopt[optidx].funcflags) != 0) - errx(1, "%s: line %zd: invalid data for %s", - filename, lineno, configopt[optidx].optname); - free(optval); - free(optsub); - free(line); + configopt[optidx].funcflags) != 0) { + add_startup_exception("%s: line %zd: invalid data for " + "%s", filename, lineno, configopt[optidx].optname); + goto invalid; + } +invalid: + if (optval) { + free(optval); + optval = NULL; + } + if (optsub) { + free(optsub); + optsub = NULL; + } } + if (line) + free(line); fclose(config); DNPRINTF(SWM_D_CONF, "conf_load: end\n"); return (0); - -out: - free(line); - fclose(config); - DNPRINTF(SWM_D_CONF, "conf_load: end with error.\n"); - - return (1); } void @@ -7408,11 +7838,11 @@ 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)) - bar_update(); + bar_draw(); xcb_flush(conn); } @@ -7443,10 +7873,14 @@ keypress(xcb_key_press_event_t *e) keysym = xcb_key_press_lookup_keysym(syms, e, 0); - DNPRINTF(SWM_D_EVENT, "keypress: %u %u\n", e->detail, keysym); + DNPRINTF(SWM_D_EVENT, "keypress: keysym: %u, win (x,y): 0x%x (%d,%d), " + "detail: %u, time: %u, root (x,y): 0x%x (%d,%d), child: 0x%x, " + "state: %u, same_screen: %s\n", keysym, e->event, e->event_x, + e->event_y, e->detail, e->time, e->root, e->root_x, e->root_y, + e->child, e->state, YESNO(e->same_screen)); if ((kp = key_lookup(CLEANMASK(e->state), keysym)) == NULL) - return; + goto out; last_event_time = e->time; @@ -7456,6 +7890,13 @@ keypress(xcb_key_press_event_t *e) else if (keyfuncs[kp->funcid].func) keyfuncs[kp->funcid].func(root_to_region(e->root, SWM_CK_ALL), &(keyfuncs[kp->funcid].args)); + +out: + /* Unfreeze grab events. */ + xcb_allow_events(conn, XCB_ALLOW_ASYNC_KEYBOARD, e->time); + xcb_flush(conn); + + DNPRINTF(SWM_D_EVENT, "keypress: done.\n"); } void @@ -7497,7 +7938,7 @@ buttonpress(xcb_button_press_event_t *e) XCB_INPUT_FOCUS_PARENT, e->root, e->time); /* Clear bar since empty. */ - bar_update(); + bar_draw(); handled = 1; goto out; @@ -7764,6 +8205,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); } } @@ -7840,7 +8284,7 @@ void enternotify(xcb_enter_notify_event_t *e) { struct ws_win *win; - struct swm_region *old_r, *r; + struct swm_region *r; DNPRINTF(SWM_D_FOCUS, "enternotify: time: %u, win (x,y): 0x%x " "(%d,%d), mode: %s(%d), detail: %s(%d), root (x,y): 0x%x (%d,%d), " @@ -7864,33 +8308,21 @@ enternotify(xcb_enter_notify_event_t *e) /* If no windows on pointer region, then focus root. */ r = root_to_region(e->root, SWM_CK_POINTER); if (r == NULL) { - DNPRINTF(SWM_D_EVENT, "enterntoify: " + DNPRINTF(SWM_D_EVENT, "enternotify: " "NULL region; ignoring.\n"); return; } - if (TAILQ_EMPTY(&r->ws->winlist)) { - old_r = root_to_region(e->root, SWM_CK_FOCUS); - if (old_r && old_r != r) - unfocus_win(old_r->ws->focus); - - xcb_set_input_focus(conn, - XCB_INPUT_FOCUS_PARENT, e->root, e->time); - - /* Clear bar since empty. */ - bar_update(); - - focus_flush(); - } + focus_region(r); } else { DNPRINTF(SWM_D_EVENT, "enternotify: window is NULL; " "ignoring\n"); + return; } - return; + } else { + focus_win(get_focus_magic(win)); } - focus_win(get_focus_magic(win)); - xcb_flush(conn); } @@ -7945,7 +8377,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", @@ -7969,8 +8401,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) @@ -7984,6 +8433,38 @@ out: DNPRINTF(SWM_D_EVENT, "maprequest: done.\n"); } +void +motionnotify(xcb_motion_notify_event_t *e) +{ + struct swm_region *r; + int i, num_screens; + + DNPRINTF(SWM_D_FOCUS, "motionnotify: time: %u, win (x,y): 0x%x " + "(%d,%d), detail: %s(%d), root (x,y): 0x%x (%d,%d), " + "child: 0x%x, same_screen_focus: %s, state: %d\n", + e->time, e->event, e->event_x, e->event_y, + get_notify_detail_label(e->detail), e->detail, + e->root, e->root_x, e->root_y, e->child, + YESNO(e->same_screen), e->state); + + last_event_time = e->time; + + 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; + + TAILQ_FOREACH(r, &screens[i].rl, entry) + if (X(r) <= e->root_x && e->root_x < MAX_X(r) && + Y(r) <= e->root_y && e->root_y < MAX_Y(r)) + break; + + focus_region(r); +} + #ifdef SWM_DEBUG char * get_atom_name(xcb_atom_t atom) @@ -8022,6 +8503,7 @@ void propertynotify(xcb_property_notify_event_t *e) { struct ws_win *win; + struct workspace *ws; #ifdef SWM_DEBUG char *name; @@ -8035,28 +8517,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(); @@ -8068,15 +8561,15 @@ 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; } } } } else if (e->atom == XCB_ATOM_WM_CLASS || e->atom == XCB_ATOM_WM_NAME) { - bar_update(); + bar_draw(); } xcb_flush(conn); @@ -8092,7 +8585,7 @@ unmapnotify(xcb_unmap_notify_event_t *e) /* If we aren't managing the window, then ignore. */ win = find_window(e->window); - if (win == NULL) + if (win == NULL || win->id != e->window) return; ws = win->ws; @@ -8125,11 +8618,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); } } @@ -8263,7 +8757,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); @@ -8293,6 +8787,7 @@ new_region(struct swm_screen *s, int x, int y, int w, int h) struct swm_region *r, *n; struct workspace *ws = NULL; int i; + uint32_t wa[1]; DNPRINTF(SWM_D_MISC, "new region: screen[%d]:%dx%d+%d+%d\n", s->idx, w, h, x, y); @@ -8302,14 +8797,13 @@ new_region(struct swm_screen *s, int x, int y, int w, int h) while (n) { r = n; n = TAILQ_NEXT(r, entry); - if (X(r) < (x + w) && - (X(r) + WIDTH(r)) > x && - Y(r) < (y + h) && - (Y(r) + HEIGHT(r)) > y) { + if (X(r) < (x + w) && (X(r) + WIDTH(r)) > x && + Y(r) < (y + h) && (Y(r) + HEIGHT(r)) > y) { if (r->ws->r != NULL) r->ws->old_r = r->ws->r; r->ws->r = NULL; bar_cleanup(r); + xcb_destroy_window(conn, r->id); TAILQ_REMOVE(&s->rl, r, entry); TAILQ_INSERT_TAIL(&s->orl, r, entry); } @@ -8360,6 +8854,17 @@ new_region(struct swm_screen *s, int x, int y, int w, int h) ws->r = r; outputs++; TAILQ_INSERT_TAIL(&s->rl, r, entry); + + /* Invisible region window to detect pointer events on empty regions. */ + r->id = xcb_generate_id(conn); + wa[0] = XCB_EVENT_MASK_POINTER_MOTION | + XCB_EVENT_MASK_POINTER_MOTION_HINT; + + xcb_create_window(conn, XCB_COPY_FROM_PARENT, r->id, r->s->root, + X(r), Y(r), WIDTH(r), HEIGHT(r), 0, XCB_WINDOW_CLASS_INPUT_ONLY, + XCB_COPY_FROM_PARENT, XCB_CW_EVENT_MASK, wa); + + xcb_map_window(conn, r->id); } void @@ -8370,6 +8875,7 @@ scan_xrandr(int i) int ncrtc = 0; #endif /* SWM_XRR_HAS_CRTC */ struct swm_region *r; + struct ws_win *win; int num_screens; xcb_randr_get_screen_resources_current_cookie_t src; xcb_randr_get_screen_resources_current_reply_t *srr; @@ -8383,7 +8889,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"); @@ -8391,6 +8897,7 @@ scan_xrandr(int i) while ((r = TAILQ_FIRST(&screens[i].rl)) != NULL) { r->ws->old_r = r->ws->r = NULL; bar_cleanup(r); + xcb_destroy_window(conn, r->id); TAILQ_REMOVE(&screens[i].rl, r, entry); TAILQ_INSERT_TAIL(&screens[i].orl, r, entry); } @@ -8442,6 +8949,16 @@ scan_xrandr(int i) screen->height_in_pixels); out: + /* Cleanup unused previously visible workspaces. */ + TAILQ_FOREACH(r, &screens[i].orl, entry) { + TAILQ_FOREACH(win, &r->ws->winlist, entry) + unmap_window(win); + + /* The screen shouldn't focus on an unused region. */ + if (screens[i].r_focus == r) + screens[i].r_focus = NULL; + } + DNPRINTF(SWM_D_MISC, "scan_xrandr: done.\n"); } @@ -8453,7 +8970,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) @@ -8468,15 +8985,30 @@ screenchange(xcb_randr_screen_change_notify_event_t *e) print_win_geom(e->root); #endif /* add bars to all regions */ - for (i = 0; i < num_screens; i++) + for (i = 0; i < num_screens; i++) { TAILQ_FOREACH(r, &screens[i].rl, entry) bar_setup(r); + } + stack(); + + /* Make sure a region has focus on each screen. */ + for (i = 0; i < num_screens; i++) { + if (screens[i].r_focus == NULL) { + r = TAILQ_FIRST(&screens[i].rl); + if (r != NULL) + focus_region(r); + } + } + + bar_draw(); + focus_flush(); } void grab_windows(void) { + struct swm_region *r = NULL; xcb_window_t *wins = NULL, trans; int no; int i, j, num_screens; @@ -8484,12 +9016,12 @@ grab_windows(void) xcb_query_tree_cookie_t qtc; xcb_query_tree_reply_t *qtr; - xcb_get_window_attributes_cookie_t c; - xcb_get_window_attributes_reply_t *r; + xcb_get_window_attributes_cookie_t gac; + xcb_get_window_attributes_reply_t *gar; 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); @@ -8501,17 +9033,30 @@ grab_windows(void) /* normal windows */ DNPRINTF(SWM_D_INIT, "grab_windows: grab top level windows.\n"); for (j = 0; j < no; j++) { - c = xcb_get_window_attributes(conn, wins[j]); - r = xcb_get_window_attributes_reply(conn, c, NULL); - if (!r) { + TAILQ_FOREACH(r, &screens[i].rl, entry) { + if (r->id == wins[j]) { + DNPRINTF(SWM_D_INIT, "grab_windows: " + "skip %#x; region input window.\n", + wins[j]); + break; + } + } + + if (r) + continue; + + gac = xcb_get_window_attributes(conn, wins[j]); + gar = xcb_get_window_attributes_reply(conn, gac, NULL); + if (!gar) { DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; " "doesn't exist.\n", wins[j]); continue; } - if (r->override_redirect) { + + if (gar->override_redirect) { DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; " "override_redirect set.\n", wins[j]); - free(r); + free(gar); continue; } @@ -8520,42 +9065,43 @@ grab_windows(void) &trans, NULL)) { DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; " "is transient for %#x.\n", wins[j], trans); - free(r); + free(gar); continue; } state = getstate(wins[j]); manage = state != XCB_ICCCM_WM_STATE_WITHDRAWN; - mapped = r->map_state != XCB_MAP_STATE_UNMAPPED; + mapped = gar->map_state != XCB_MAP_STATE_UNMAPPED; if (mapped || manage) manage_window(wins[j], mapped); - free(r); + free(gar); } /* transient windows */ DNPRINTF(SWM_D_INIT, "grab_windows: grab transient windows.\n"); for (j = 0; j < no; j++) { - c = xcb_get_window_attributes(conn, wins[j]); - r = xcb_get_window_attributes_reply(conn, c, NULL); - if (!r) { + gac = xcb_get_window_attributes(conn, wins[j]); + gar = xcb_get_window_attributes_reply(conn, gac, NULL); + if (!gar) { DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; " "doesn't exist.\n", wins[j]); continue; } - if (r->override_redirect) { + + if (gar->override_redirect) { DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; " "override_redirect set.\n", wins[j]); - free(r); + free(gar); continue; } state = getstate(wins[j]); manage = state != XCB_ICCCM_WM_STATE_WITHDRAWN; - mapped = r->map_state != XCB_MAP_STATE_UNMAPPED; + mapped = gar->map_state != XCB_MAP_STATE_UNMAPPED; pc = xcb_icccm_get_wm_transient_for(conn, wins[j]); if (xcb_icccm_get_wm_transient_for_reply(conn, pc, &trans, NULL) && manage) manage_window(wins[j], mapped); - free(r); + free(gar); } free(qtr); } @@ -8573,7 +9119,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 " @@ -8600,6 +9146,8 @@ setup_screens(void) for (i = 0; i < num_screens; i++) { DNPRINTF(SWM_D_WS, "setup_screens: init screen: %d\n", i); screens[i].idx = i; + screens[i].r_focus = NULL; + TAILQ_INIT(&screens[i].rl); TAILQ_INIT(&screens[i].orl); if ((screen = get_screen(i)) == NULL) @@ -8610,6 +9158,8 @@ setup_screens(void) setscreencolor("red", i + 1, SWM_S_COLOR_FOCUS); setscreencolor("rgb:88/88/88", i + 1, SWM_S_COLOR_UNFOCUS); setscreencolor("rgb:00/80/80", i + 1, SWM_S_COLOR_BAR_BORDER); + setscreencolor("rgb:00/40/40", i + 1, + SWM_S_COLOR_BAR_BORDER_UNFOCUS); setscreencolor("black", i + 1, SWM_S_COLOR_BAR); setscreencolor("rgb:a0/a0/a0", i + 1, SWM_S_COLOR_BAR_FONT); @@ -8688,7 +9238,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; @@ -8717,14 +9267,13 @@ shutdown_cleanup(void) err(1, "can't disable alarm"); bar_extra_stop(); - bar_extra = 1; unmap_all(); cursors_cleanup(); 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); @@ -8795,7 +9344,7 @@ event_handle(xcb_generic_event_t *evt) EVENT(XCB_MAP_NOTIFY, mapnotify); EVENT(XCB_MAP_REQUEST, maprequest); EVENT(XCB_MAPPING_NOTIFY, mappingnotify); - /*EVENT(XCB_MOTION_NOTIFY, );*/ + EVENT(XCB_MOTION_NOTIFY, motionnotify); /*EVENT(XCB_NO_EXPOSURE, );*/ EVENT(XCB_PROPERTY_NOTIFY, propertynotify); /*EVENT(XCB_REPARENT_NOTIFY, );*/ @@ -8814,15 +9363,17 @@ event_handle(xcb_generic_event_t *evt) int main(int argc, char *argv[]) { - struct swm_region *r, *rr; - struct ws_win *winfocus = NULL; + struct swm_region *r; char conf[PATH_MAX], *cfile = NULL; struct stat sb; - int xfd, i, num_screens; + int xfd, i, num_screens, startup = 1; struct sigaction sact; xcb_generic_event_t *evt; struct timeval tv; fd_set rd; + int rd_max; + int stdin_ready = 0; + int num_readable; /* suppress unused warning since var is needed */ (void)argc; @@ -8929,6 +9480,8 @@ noconfig: if (cfile) conf_load(cfile, SWM_CONF_DEFAULT); + validate_spawns(); + setup_ewmh(); /* set some values to work around bad programs */ workaround(); @@ -8939,20 +9492,20 @@ 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) { - if (winfocus == NULL) - winfocus = TAILQ_FIRST(&r->ws->winlist); + TAILQ_FOREACH(r, &screens[i].rl, entry) bar_setup(r); - } grabkeys(); stack(); + bar_draw(); xcb_ungrab_server(conn); xcb_flush(conn); + rd_max = xfd > STDIN_FILENO ? xfd : STDIN_FILENO; + while (running) { while ((evt = xcb_poll_for_event(conn))) { if (!running) @@ -8962,38 +9515,50 @@ noconfig: } /* If just (re)started, set default focus if needed. */ - if (winfocus && focus_mode != SWM_FOCUS_FOLLOW) { - rr = winfocus->ws->r; - if (rr == NULL) { - /* not a visible window */ - winfocus = NULL; + if (startup) { + startup = 0; + + if (focus_mode != SWM_FOCUS_FOLLOW) { + r = TAILQ_FIRST(&screens[0].rl); + if (r) { + focus_region(r); + focus_flush(); + } continue; } - - focus_win(get_region_focus(rr)); - focus_flush(); - winfocus = NULL; - continue; } FD_ZERO(&rd); + + if (bar_extra) + FD_SET(STDIN_FILENO, &rd); + FD_SET(xfd, &rd); tv.tv_sec = 1; tv.tv_usec = 0; - if (select(xfd + 1, &rd, NULL, NULL, &tv) == -1) - if (errno != EINTR) { - DNPRINTF(SWM_D_MISC, "select failed"); - } + num_readable = select(rd_max + 1, &rd, NULL, NULL, &tv); + if (num_readable == -1 && errno != EINTR) { + DNPRINTF(SWM_D_MISC, "select failed"); + } else if (num_readable > 0 && FD_ISSET(STDIN_FILENO, &rd)) { + stdin_ready = 1; + } + if (restart_wm) restart(NULL, NULL); + if (search_resp) search_do_resp(); + if (!running) goto done; - if (bar_alarm) { - bar_alarm = 0; - bar_update(); + + if (stdin_ready) { + stdin_ready = 0; + bar_extra_update(); } + + bar_draw(); + xcb_flush(conn); } done: shutdown_cleanup();