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 {
{"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);
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 *);
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);
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);
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 *);*/
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);
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),
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)
}
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);
}
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;
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");
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);
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");
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:
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");
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);
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");
}
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;
}
free(cp);
+ sp->flags = flags;
+
TAILQ_INSERT_TAIL(&spawns, sp, entry);
DNPRINTF(SWM_D_SPAWN, "spawn_insert: leave\n");
}
}
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;
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");
}
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"
" -nf $bar_font_color"
" -sb $bar_border"
" -sf $bar_color", 0);
+
setconfspawn("name_workspace", "dmenu"
" -p Workspace"
" -fn $bar_font"
" -sb $bar_border"
" -sf $bar_color", 0);
- /* only test dmenu for now, really should expand this */
- if (system("dmenu -v") != 0)
- add_startup_exception("you must install dmenu");
+ /* 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 */
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);
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;
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
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) {
/* 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;
}
focus_win(get_focus_magic(win));
}
- focus_flush();
+ xcb_flush(conn);
}
#ifdef SWM_DEBUG
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;
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;
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");
}
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();
}
if (cfile)
conf_load(cfile, SWM_CONF_DEFAULT);
+ validate_spawns();
+
setup_ewmh();
/* set some values to work around bad programs */
workaround();
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();