X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=spectrwm.c;h=21310f73c94c05facad32cd1610ae99acedae9bd;hb=6314288a163adc8af40f35dc57e89a79e1e282f5;hp=7597cd48c7e5c70657f8ed058a16cecdda8857c0;hpb=ec0be0e55015e1a7771b668a7c659d67c696f452;p=spectrwm.git diff --git a/spectrwm.c b/spectrwm.c index 7597cd4..21310f7 100644 --- a/spectrwm.c +++ b/spectrwm.c @@ -374,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; @@ -384,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 { @@ -922,6 +926,7 @@ 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); #ifdef SWM_DEBUG char *get_stack_mode_name(uint8_t); @@ -1050,6 +1055,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); @@ -1174,6 +1181,27 @@ get_screen(int screen) return (NULL); } +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) { @@ -2214,8 +2242,15 @@ bar_draw(void) 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 @@ -3315,11 +3350,6 @@ set_region(struct swm_region *r) if (r == NULL) return; - /* Skip if only one region on this screen. */ - rf = TAILQ_FIRST(&r->s->rl); - if (TAILQ_NEXT(rf, entry) == NULL) - goto out; - rf = r->s->r_focus; /* Unfocus old region bar. */ if (rf) { @@ -3335,7 +3365,6 @@ set_region(struct swm_region *r) xcb_change_window_attributes(conn, r->bar->id, XCB_CW_BORDER_PIXEL, &r->s->c[SWM_S_COLOR_BAR_BORDER].pixel); -out: r->s->r_focus = r; } @@ -3946,10 +3975,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; @@ -4185,9 +4216,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) { @@ -4205,16 +4234,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; @@ -4222,6 +4255,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) { @@ -4241,7 +4275,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){ @@ -4251,6 +4285,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) { @@ -4418,7 +4453,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); @@ -4443,11 +4478,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 @@ -4471,14 +4508,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); @@ -4491,10 +4523,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); @@ -4691,7 +4723,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) @@ -4703,7 +4735,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); } @@ -5805,6 +5837,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) @@ -5979,7 +6018,9 @@ setspawn(const char *name, const char *args) int setconfspawn(char *selector, char *value, int flags) { - char *args; + char *args; + char which[PATH_MAX]; + size_t i; /* suppress unused warning since var is needed */ (void)flags; @@ -5988,6 +6029,17 @@ setconfspawn(char *selector, char *value, int flags) DNPRINTF(SWM_D_SPAWN, "setconfspawn: [%s] [%s]\n", selector, args); + /* verify we have the goods */ + snprintf(which, sizeof which, "which %s", value); + for (i = strlen("which "); i < strlen(which); i++) + if (which[i] == ' ') { + which[i] = '\0'; + break; + } + if (flags == 0 && system(which) != 0) + add_startup_exception("could not find %s", + &which[strlen("which ")]); + setspawn(selector, args); free(args); @@ -6000,10 +6052,6 @@ setup_spawn(void) { 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" @@ -6024,6 +6072,13 @@ setup_spawn(void) " -nf $bar_font_color" " -sb $bar_border" " -sf $bar_color", 0); + + /* these are not verified for existence */ + setconfspawn("lock", "xlock", 1); + setconfspawn("screenshot_all", "screenshot.sh full", 1); + setconfspawn("screenshot_wind", "screenshot.sh window", 1); + setconfspawn("initscr", "initscreen.sh", 1); + } /* key bindings */ @@ -6662,12 +6717,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, @@ -6817,6 +6874,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; @@ -6847,6 +6909,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; @@ -7117,12 +7184,14 @@ struct config_option configopt[] = { { "program", setconfspawn, 0 }, { "quirk", setconfquirk, 0 }, { "region", setconfregion, 0 }, + { "region_padding", setconfvalue, SWM_S_REGION_PADDING }, { "screenshot_app", setconfvalue, SWM_S_SS_APP }, { "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 }, { "urgent_enabled", setconfvalue, SWM_S_URGENT_ENABLED }, @@ -7131,11 +7200,36 @@ struct config_option configopt[] = { { "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; size_t linelen, lineno = 0; int wordlen, i, optidx; struct config_option *opt = NULL; @@ -7152,6 +7246,9 @@ conf_load(const char *filename, int keymapping) } while (!feof(config)) { + if (line) + free(line); + if ((line = fparseln(config, &linelen, &lineno, NULL, 0)) == NULL) { if (ferror(config)) @@ -7163,15 +7260,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++) { @@ -7183,17 +7279,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 == '[') { @@ -7201,17 +7300,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; @@ -7220,27 +7319,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 @@ -8662,6 +8770,9 @@ 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; @@ -8737,9 +8848,18 @@ 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); + + if (screens[0].r_focus == NULL) { + /* Focus on first region. */ + r = TAILQ_FIRST(&screens[0].rl); + if (r) + focus_region(r); + } + } + stack(); bar_draw(); focus_flush();