X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=spectrwm.c;h=132743fa21014e43f26cda140826c1e5390477a2;hb=0f584636013cbb5fd6efef904857ac1695e72953;hp=7597cd48c7e5c70657f8ed058a16cecdda8857c0;hpb=ec0be0e55015e1a7771b668a7c659d67c696f452;p=spectrwm.git diff --git a/spectrwm.c b/spectrwm.c index 7597cd4..132743f 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 @@ -2504,7 +2539,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), @@ -3315,11 +3351,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 +3366,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 +3976,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 +4217,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 +4235,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 +4256,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 +4276,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 +4286,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 +4454,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 +4479,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 +4509,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 +4524,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 +4724,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 +4736,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 +5838,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,15 +6019,25 @@ setspawn(const char *name, const char *args) int setconfspawn(char *selector, char *value, int flags) { - char *args; - - /* suppress unused warning since var is needed */ - (void)flags; + char *args; + char which[PATH_MAX]; + size_t i; args = expand_tilde(value); DNPRINTF(SWM_D_SPAWN, "setconfspawn: [%s] [%s]\n", selector, args); + /* verify we have the goods */ + snprintf(which, sizeof which, "which %s", value); + for (i = strlen("which "); i < strlen(which); i++) + if (which[i] == ' ') { + which[i] = '\0'; + break; + } + if (flags == 0 && system(which) != 0) + add_startup_exception("could not find %s", + &which[strlen("which ")]); + setspawn(selector, args); free(args); @@ -6000,10 +6050,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 +6070,12 @@ 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 +6714,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 +6871,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 +6906,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 +7181,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 +7197,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 = NULL; size_t linelen, lineno = 0; int wordlen, i, optidx; struct config_option *opt = NULL; @@ -7152,8 +7243,11 @@ conf_load(const char *filename, int keymapping) } 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 @@ -7163,15 +7257,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 +7276,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 +7297,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 +7316,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 @@ -8104,7 +8209,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; } @@ -8119,7 +8224,7 @@ enternotify(xcb_enter_notify_event_t *e) focus_win(get_focus_magic(win)); } - focus_flush(); + xcb_flush(conn); } #ifdef SWM_DEBUG @@ -8638,6 +8743,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; @@ -8711,6 +8817,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"); } @@ -8737,10 +8853,22 @@ 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(); }