X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=spectrwm.c;h=606dcbe4ed9cfd11c72bfd872e837aa0350f3f74;hb=945f07afd629eef0bfcb4f1a0b285b482e1811ac;hp=cfd2a4db1f7ec4b37c1eb33d4e0b1ef68c1e56f6;hpb=b58b056511183145f449bc39c397aba7b8cbef35;p=spectrwm.git diff --git a/spectrwm.c b/spectrwm.c index cfd2a4d..606dcbe 100644 --- a/spectrwm.c +++ b/spectrwm.c @@ -387,7 +387,7 @@ char *bar_fonts; XftColor bar_font_color; struct passwd *pwd; char *startup_exception; -unsigned int nr_exceptions = 0; +unsigned int nr_exceptions = 0; /* layout manager data */ struct swm_geometry { @@ -707,12 +707,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); @@ -857,8 +860,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 *); @@ -1009,7 +1011,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 +1028,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 +1049,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 *);*/ @@ -1055,8 +1058,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 *, ...); +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); @@ -2539,7 +2542,8 @@ bar_setup(struct swm_region *r) 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_UNFOCUS].pixel; - wa[2] = XCB_EVENT_MASK_EXPOSURE; + 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), @@ -4723,7 +4727,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) @@ -4735,7 +4739,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); } @@ -5321,6 +5325,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; @@ -5377,7 +5387,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"); @@ -5440,6 +5450,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); @@ -5449,7 +5465,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"); @@ -5508,6 +5524,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: @@ -5550,7 +5572,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"); @@ -5572,6 +5594,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); @@ -5581,6 +5609,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"); @@ -5925,7 +5954,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; @@ -5954,6 +5983,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"); } @@ -5976,18 +6007,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; @@ -5996,22 +6016,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"); } @@ -6019,28 +6035,12 @@ int setconfspawn(char *selector, char *value, int flags) { char *args; - char which[PATH_MAX]; - size_t i; - - /* suppress unused warning since var is needed */ - (void)flags; 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 (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"); @@ -6048,27 +6048,65 @@ 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", "damenu" + + setconfspawn("search", "dmenu" " -i" " -fn $bar_font" " -nb $bar_color" " -nf $bar_font_color" " -sb $bar_border" " -sf $bar_color", 0); + setconfspawn("name_workspace", "dmenu" " -p Workspace" " -fn $bar_font" @@ -6076,6 +6114,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 */ @@ -6266,6 +6309,8 @@ setconfbinding(char *selector, char *value, int flags) if (parsekeys(value, mod_key, &mod, &ks) == 0) { setkeybinding(mod, ks, KF_SPAWN_CUSTOM, sp->name); + /* Custom binding; validate spawn. */ + sp->flags ^= SWM_SPAWN_OPTIONAL; return (0); } else return (1); @@ -7226,7 +7271,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; @@ -7246,8 +7291,8 @@ conf_load(const char *filename, int keymapping) if (line) free(line); - if ((line = fparseln(config, &linelen, &lineno, NULL, 0)) - == NULL) { + if ((line = fparseln(config, &linelen, &lineno, NULL, + FPARSELN_UNESCCOMM | FPARSELN_UNESCCONT)) == NULL) { if (ferror(config)) err(1, "%s", filename); else @@ -7316,6 +7361,12 @@ 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) { @@ -8203,7 +8254,7 @@ 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; } @@ -8218,7 +8269,7 @@ enternotify(xcb_enter_notify_event_t *e) focus_win(get_focus_magic(win)); } - focus_flush(); + xcb_flush(conn); } #ifdef SWM_DEBUG @@ -8737,6 +8788,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; @@ -8761,9 +8813,6 @@ scan_xrandr(int i) xcb_destroy_window(conn, r->id); TAILQ_REMOVE(&screens[i].rl, r, entry); TAILQ_INSERT_TAIL(&screens[i].orl, r, entry); - - if (r->s->r_focus == r) - r->s->r_focus = NULL; } outputs = 0; @@ -8813,6 +8862,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"); } @@ -8842,16 +8901,19 @@ screenchange(xcb_randr_screen_change_notify_event_t *e) for (i = 0; i < num_screens; i++) { TAILQ_FOREACH(r, &screens[i].rl, entry) bar_setup(r); + } - if (screens[0].r_focus == NULL) { - /* Focus on first region. */ - r = TAILQ_FIRST(&screens[0].rl); - if (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); } } - stack(); bar_draw(); focus_flush(); } @@ -9331,6 +9393,8 @@ noconfig: if (cfile) conf_load(cfile, SWM_CONF_DEFAULT); + validate_spawns(); + setup_ewmh(); /* set some values to work around bad programs */ workaround(); @@ -9403,11 +9467,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();