char *bar_format = NULL;
int stack_enabled = 1;
int clock_enabled = 1;
+int iconic_enabled = 0;
int urgent_enabled = 0;
char *clock_format = NULL;
int window_class_enabled = 0;
struct swm_geometry g; /* current geometry */
struct swm_geometry g_float; /* region coordinates */
int g_floatvalid; /* g_float geometry validity */
- int floatmaxed; /* whether maxed by max_stack */
int floating;
int manual;
int32_t mapped;
int32_t iconic;
+ int32_t maximized;
int bordered;
unsigned int ewmh_flags;
int font_size_boundary[SWM_MAX_FONT_STEPS];
struct swm_region *old_r; /* may be NULL */
struct ws_win_list winlist; /* list of windows in ws */
struct ws_win_list unmanagedlist; /* list of dead windows in ws */
+ int32_t state;
char stacker[10]; /* display stacker and layout */
/* stacker state */
};
enum {
+ SWM_WS_STATE_HIDDEN,
+ SWM_WS_STATE_MAPPING,
+ SWM_WS_STATE_MAPPED,
+};
+
+enum {
SWM_S_COLOR_BAR,
SWM_S_COLOR_BAR_BORDER,
SWM_S_COLOR_BAR_BORDER_UNFOCUS,
KF_FOCUS_NEXT,
KF_FOCUS_PREV,
KF_FOCUS_URGENT,
+ KF_MAXIMIZE_TOGGLE,
KF_HEIGHT_GROW,
KF_HEIGHT_SHRINK,
KF_ICONIFY,
/* function prototypes */
void adjust_font(struct ws_win *);
+char *argsep(char **);
void bar_cleanup(struct swm_region *);
void bar_extra_setup(void);
void bar_extra_stop(void);
void bar_window_float(char *, size_t, struct swm_region *);
void bar_window_instance(char *, size_t, struct swm_region *);
void bar_window_name(char *, size_t, struct swm_region *);
+void bar_window_state(char *, size_t, struct swm_region *);
void bar_workspace_name(char *, size_t, struct swm_region *);
void buttonpress(xcb_button_press_event_t *);
void check_conn(void);
void clear_keys(void);
+int clear_maximized(struct workspace *);
void clientmessage(xcb_client_message_event_t *);
void client_msg(struct ws_win *, xcb_atom_t, xcb_timestamp_t);
int conf_load(const char *, int);
#endif
void load_float_geom(struct ws_win *);
struct ws_win *manage_window(xcb_window_t, uint16_t);
-void map_window(struct ws_win *, xcb_window_t);
+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 maximize_toggle(struct swm_region *, union arg *);
void motionnotify(xcb_motion_notify_event_t *);
void move(struct ws_win *, union arg *);
void move_step(struct swm_region *, union arg *);
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_floater(struct ws_win *);
void stack_master(struct workspace *, struct swm_geometry *, int, int);
+void reorder_window(struct ws_win *, uint32_t);
void store_float_geom(struct ws_win *);
char *strdupsafe(const char *);
void swapwin(struct swm_region *, union arg *);
unsigned int mask = 0;
unsigned int changed = 0;
unsigned int orig_flags;
+#ifdef SWM_DEBUG
+ char *name;
+#endif
if (win == NULL)
return;
- DNPRINTF(SWM_D_PROP, "ewmh_update_win_state: window: 0x%x, state: %ld, "
- "action: %ld\n", win->id, (unsigned long)state, action);
+#ifdef SWM_DEBUG
+ name = get_atom_name(state);
+ DNPRINTF(SWM_D_PROP, "ewmh_update_win_state: window: 0x%x, state: %s, "
+ "action: %ld\n", win->id, name, action);
+ free(name);
+#endif
if (state == ewmh[_NET_WM_STATE_FULLSCREEN].atom)
mask = EWMH_F_FULLSCREEN;
}
void
-bar_window_float(char *s, size_t sz, struct swm_region *r)
+bar_window_state(char *s, size_t sz, struct swm_region *r)
{
if (r == NULL || r ->ws == NULL || r->ws->focus == NULL)
return;
- if (r->ws->focus->floating)
+ if (r->ws->focus->maximized)
+ strlcat(s, "(m)", sz);
+ else if (r->ws->focus->floating)
strlcat(s, "(f)", sz);
}
void
bar_fmt(const char *fmtexp, char *fmtnew, struct swm_region *r, size_t sz)
{
+ struct ws_win *w;
+
/* if format provided, just copy the buffers */
if (bar_format != NULL) {
strlcpy(fmtnew, fmtexp, sz);
/* only show the workspace name if there's actually one */
if (r != NULL && r->ws != NULL && r->ws->name != NULL)
strlcat(fmtnew, "<+D>", sz);
+
+ /* If enabled, only show the iconic count if there are iconic wins. */
+ if (iconic_enabled && r != NULL && r->ws != NULL)
+ TAILQ_FOREACH(w, &r->ws->winlist, entry)
+ if (w->iconic) {
+ strlcat(fmtnew, "{+M}", sz);
+ break;
+ }
+
strlcat(fmtnew, "+3<", sz);
if (clock_enabled) {
strlcat(fmtnew, "+T+4<", sz);
}
if (window_name_enabled) {
- if (r->ws->focus->floating)
+ if (r->ws->focus->floating || r->ws->focus->maximized)
strlcat(fmtnew, "+F ", sz);
strlcat(fmtnew, "+64W ", sz);
}
bar_replace_seq(char *fmt, char *fmtrep, struct swm_region *r, size_t *offrep,
size_t sz)
{
+ struct ws_win *w;
char *ptr;
char tmp[SWM_BAR_MAX];
- int limit, size;
+ int limit, size, count;
size_t len;
/* reset strlcat(3) buffer */
bar_workspace_name(tmp, sizeof tmp, r);
break;
case 'F':
- bar_window_float(tmp, sizeof tmp, r);
+ bar_window_state(tmp, sizeof tmp, r);
break;
case 'I':
snprintf(tmp, sizeof tmp, "%d", r->ws->idx + 1);
break;
+ case 'M':
+ count = 0;
+ TAILQ_FOREACH(w, &r->ws->winlist, entry)
+ if (w->iconic)
+ ++count;
+
+ snprintf(tmp, sizeof tmp, "%d", count);
+ break;
case 'N':
snprintf(tmp, sizeof tmp, "%d", r->s->idx + 1);
break;
}
void
-map_window(struct ws_win *win, xcb_window_t sibling)
+reorder_window(struct ws_win *win, uint32_t mode)
{
- uint16_t mode = XCB_CONFIG_WINDOW_STACK_MODE;
+ struct ws_win *w = win;
+ struct swm_region *r;
+ struct workspace *ws;
uint32_t val[2];
- int i = 0;
- /* If sibling is specified, stack right above it. */
- if (sibling != XCB_WINDOW_NONE) {
- mode |= XCB_CONFIG_WINDOW_SIBLING;
- val[i++] = sibling;
+ if (win == NULL || (r = win->ws->r) == NULL)
+ return;
+ ws = win->ws;
+
+ DNPRINTF(SWM_D_EVENT, "reorder_window: win 0x%x, mode: %d\n",
+ win->id, mode);
+
+ switch (mode) {
+ case SWM_STACK_TOP:
+ /* Position above top-most sibling. */
+ TAILQ_FOREACH_REVERSE(w, &ws->winlist, ws_win_list, entry) {
+ if (w == win || w->iconic)
+ continue;
+ if ((win->ewmh_flags & EWMH_F_FULLSCREEN) !=
+ (w->ewmh_flags & EWMH_F_FULLSCREEN))
+ continue;
+ if (!win->maximized && w->maximized)
+ continue;
+ if (win->maximized) {
+ if (w->floating)
+ break;
+ continue;
+ }
+ if (win->floating == w->floating)
+ break;
+ }
+ break;
+ case SWM_STACK_ABOVE:
+ /* Stack above win directly prior. */
+ while ((w = TAILQ_PREV(w, ws_win_list, entry)) != NULL) {
+ if (w == win || w->iconic)
+ continue;
+ if ((win->ewmh_flags & EWMH_F_FULLSCREEN) !=
+ (w->ewmh_flags & EWMH_F_FULLSCREEN))
+ continue;
+ if (win->maximized != w->maximized)
+ continue;
+ if (win->floating == w->floating)
+ break;
+ }
+ break;
+ case SWM_STACK_BELOW:
+ /* Stack above win directly prior. */
+ while ((w = TAILQ_NEXT(w, entry)) != NULL) {
+ if (w == win || w->iconic)
+ continue;
+ if ((win->ewmh_flags & EWMH_F_FULLSCREEN) !=
+ (w->ewmh_flags & EWMH_F_FULLSCREEN))
+ continue;
+ if (win->maximized != w->maximized)
+ continue;
+ if (win->floating == w->floating)
+ break;
+ }
+ break;
+ case SWM_STACK_BOTTOM:
+ /* Position above top-most sibling. */
+ TAILQ_FOREACH(w, &ws->winlist, entry) {
+ if (w == win || w->iconic)
+ continue;
+ if ((win->ewmh_flags & EWMH_F_FULLSCREEN) !=
+ (w->ewmh_flags & EWMH_F_FULLSCREEN))
+ continue;
+ if (win->maximized != w->maximized)
+ continue;
+ if (win->floating == w->floating)
+ break;
+ }
+ break;
+ }
+
+ if (w == NULL) {
+ if (win->floating || (win->ewmh_flags & EWMH_F_FULLSCREEN)
+ || win->maximized)
+ val[0] = r->bar->id;
+ else
+ val[0] = r->id;
+ } else {
+ val[0] = w->id;
}
+ DNPRINTF(SWM_D_EVENT, "reorder_window: sibling: 0x%x\n", val[0]);
- val[i] = XCB_STACK_MODE_ABOVE;
+ val[1] = XCB_STACK_MODE_ABOVE;
+
+ xcb_configure_window(conn, win->id, XCB_CONFIG_WINDOW_STACK_MODE |
+ XCB_CONFIG_WINDOW_SIBLING, val);
+}
+void
+map_window(struct ws_win *win)
+{
if (win == NULL)
return;
- DNPRINTF(SWM_D_EVENT, "map_window: win 0x%x, mapped: %s, "
- "sibling: 0x%x\n", win->id, YESNO(win->mapped), sibling);
-
- xcb_configure_window(conn, win->id, mode, val);
+ DNPRINTF(SWM_D_EVENT, "map_window: win 0x%x, mapped: %s\n",
+ win->id, YESNO(win->mapped));
if (win->mapped)
return;
return (1);
}
-xcb_window_t
-get_sibling(struct ws_win *win, int mode)
-{
- struct ws_win *w = win;
-
- switch (mode) {
- case SWM_STACK_TOP:
- TAILQ_FOREACH_REVERSE(w, &w->ws->winlist, ws_win_list, entry)
- if (w != win && !w->floating && !w->iconic)
- break;
- break;
- case SWM_STACK_ABOVE:
- do {
- w = TAILQ_NEXT(w, entry);
- } while (w != NULL && (w == win || w->floating || w->iconic));
- break;
- case SWM_STACK_BELOW:
- do {
- w = TAILQ_PREV(w, ws_win_list, entry);
- } while (w != NULL && (w == win || w->floating || w->iconic));
- break;
- case SWM_STACK_BOTTOM:
- TAILQ_FOREACH(w, &w->ws->winlist, entry)
- if (w != win && !w->floating && !w->iconic)
- break;
- break;
- default:
- w = NULL;
- }
-
- if (w == NULL)
- return (win->ws->r->id);
- else
- return (w->id);
-}
-
void
unfocus_win(struct ws_win *win)
{
}
if (win->ws->focus == win) {
- if (tile_gap < 0 && !win->floating)
- map_window(win, get_sibling(win, SWM_STACK_BELOW));
+ if (tile_gap < 0 && !win->floating && !win->maximized)
+ reorder_window(win, SWM_STACK_ABOVE);
win->ws->focus = NULL;
win->ws->focus_prev = win;
}
ws->always_raise) {
/* If a parent exists, map it first. */
if (parent) {
- map_window(parent, XCB_WINDOW_NONE);
+ reorder_window(parent, SWM_STACK_TOP);
+ map_window(parent);
/* Map siblings next. */
TAILQ_FOREACH(w, &ws->winlist, entry)
if (w != win && !w->iconic &&
- w->transient == parent->id)
- map_window(w, XCB_WINDOW_NONE);
+ w->transient == parent->id) {
+ reorder_window(w, SWM_STACK_ABOVE);
+ map_window(w);
+ }
}
/* Map focused window. */
- map_window(win, XCB_WINDOW_NONE);
+ reorder_window(win, SWM_STACK_TOP);
+ map_window(win);
/* Finally, map children of focus window. */
TAILQ_FOREACH(w, &ws->winlist, entry)
- if (w->transient == win->id && !w->iconic)
- map_window(w, XCB_WINDOW_NONE);
+ if (w->transient == win->id && !w->iconic) {
+ reorder_window(w, SWM_STACK_ABOVE);
+ map_window(w);
+ }
} else if (tile_gap < 0 && !win->floating) {
/*
* Windows overlap in the layout.
* Raise focused win above all tiled wins.
*/
- if (tile_gap < 0 && !win->floating)
- map_window(win,
- get_sibling(win, SWM_STACK_TOP));
+ reorder_window(win, SWM_STACK_TOP);
+ map_window(win);
}
set_region(ws->r);
new_ws->focus_prev = NULL;
}
+ new_ws->state = SWM_WS_STATE_MAPPING;
stack();
/* unmap old windows */
- if (unmap_old)
+ if (unmap_old) {
TAILQ_FOREACH(win, &old_ws->winlist, entry)
unmap_window(win);
+ old_ws->state = SWM_WS_STATE_HIDDEN;
+ }
/* if workspaces were swapped, then don't wait to set focus */
if (old_ws->r && focus_mode != SWM_FOCUS_FOLLOW) {
}
focus_flush();
+ new_ws->state = SWM_WS_STATE_MAPPED;
DNPRINTF(SWM_D_WS, "switchws: done.\n");
}
if (cur_focus == NULL)
return;
+ clear_maximized(r->ws);
+
source = cur_focus;
wl = &source->ws->winlist;
wl = &ws->winlist;
cur_focus = ws->focus;
- DNPRINTF(SWM_D_FOCUS, "get_focus_prev: window: 0x%x, cur_focus: 0x%x\n",
- WINID(win), WINID(cur_focus));
+ DNPRINTF(SWM_D_FOCUS, "get_focus_prev: window: 0x%x, cur_focus: 0x%x, "
+ "focus_prev: 0x%x\n", WINID(win), WINID(cur_focus),
+ WINID(ws->focus_prev));
/* pickle, just focus on whatever */
if (cur_focus == NULL) {
/* use prev_focus if valid */
- if (ws->focus_prev && ws->focus_prev != cur_focus &&
- find_window(WINID(ws->focus_prev)))
+ if (ws->focus_prev && find_window(ws->focus_prev->id))
winfocus = ws->focus_prev;
goto done;
}
goto out;
}
+ if (clear_maximized(ws) > 0)
+ stack();
+
focus_win(get_focus_magic(winfocus));
focus_flush();
if (ws->cur_layout->l_stack == NULL)
ws->cur_layout = &layouts[0];
+ clear_maximized(ws);
+
stack();
bar_draw();
DNPRINTF(SWM_D_STACK, "stack_config: id: %d workspace: %d\n",
args->id, ws->idx);
+ if (clear_maximized(ws) > 0)
+ stack();
+
if (ws->cur_layout->l_config != NULL)
ws->cur_layout->l_config(ws, args->id);
j = 0;
#endif
TAILQ_FOREACH(r, &screens[i].rl, entry) {
- DNPRINTF(SWM_D_STACK, "stack: workspace: %d "
- "(screen: %d, region: %d)\n", r->ws->idx, i, j++);
-
/* Adjust stack area for region bar and padding. */
g = r->g;
g.x += region_padding;
g.y += bar_height;
g.h -= bar_height;
}
+
+ DNPRINTF(SWM_D_STACK, "stack: workspace: %d (screen: "
+ "%d, region: %d), (x,y) WxH: (%d,%d) %d x %d\n",
+ r->ws->idx, i, j++, g.x, g.y, g.w, g.h);
+
r->ws->cur_layout->l_stack(r->ws, &g);
r->ws->cur_layout->l_string(r->ws);
/* save r so we can track region changes */
}
void
-stack_floater(struct ws_win *win, struct swm_region *r)
+stack_floater(struct ws_win *win)
{
+ struct workspace *ws;
+ struct swm_region *r;
+
if (win == NULL)
return;
- DNPRINTF(SWM_D_MISC, "stack_floater: window: 0x%x\n", win->id);
+ ws = win->ws;
- /*
- * to allow windows to change their size (e.g. mplayer fs) only retrieve
- * geom on ws switches or return from max mode
- */
- if (win->floatmaxed || (r != r->ws->old_r &&
- !(win->ewmh_flags & EWMH_F_FULLSCREEN))) {
- /* update geometry for the new region */
- load_float_geom(win);
- }
+ if ((r = ws->r) == NULL)
+ return;
- win->floatmaxed = 0;
+ DNPRINTF(SWM_D_MISC, "stack_floater: window: 0x%x\n", win->id);
- /*
- * if set to fullscreen mode, configure window to maximum size.
- */
if (win->ewmh_flags & EWMH_F_FULLSCREEN) {
+ /* _NET_WM_FULLSCREEN: fullscreen without border. */
if (!win->g_floatvalid)
store_float_geom(win);
win->g = r->g;
- }
+ win->bordered = 0;
+ } else if (win->maximized) {
+ /* Maximize: like a single stacked window. */
+ if (!win->g_floatvalid)
+ store_float_geom(win);
- /*
- * remove border on fullscreen floater when in fullscreen mode or when
- * the quirk is present.
- */
- if ((win->ewmh_flags & EWMH_F_FULLSCREEN) ||
- ((win->quirks & SWM_Q_FULLSCREEN) &&
- (WIDTH(win) >= WIDTH(r)) && (HEIGHT(win) >= HEIGHT(r)))) {
- if (win->bordered) {
+ win->g = r->g;
+
+ if (bar_enabled && ws->bar_enabled) {
+ win->bordered = 1;
+ if (!bar_at_bottom)
+ Y(win) += bar_height;
+ HEIGHT(win) -= bar_height;
+ } else if (disable_border) {
win->bordered = 0;
- X(win) += border_width;
- Y(win) += border_width;
+ } else {
+ win->bordered = 1;
}
- } else if (!win->bordered) {
- win->bordered = 1;
- X(win) -= border_width;
- Y(win) -= border_width;
- }
-
- if (win->transient && (win->quirks & SWM_Q_TRANSSZ)) {
- WIDTH(win) = (double)WIDTH(r) * dialog_ratio;
- HEIGHT(win) = (double)HEIGHT(r) * dialog_ratio;
- }
- if (!win->manual && !(win->ewmh_flags & EWMH_F_FULLSCREEN) &&
- !(win->quirks & SWM_Q_ANYWHERE)) {
- /*
- * floaters and transients are auto-centred unless moved,
- * resized or ANYWHERE quirk is set.
- */
- X(win) = X(r) + (WIDTH(r) - WIDTH(win)) / 2 - BORDER(win);
- Y(win) = Y(r) + (HEIGHT(r) - HEIGHT(win)) / 2 - BORDER(win);
+ if (win->bordered) {
+ HEIGHT(win) -= 2 * border_width;
+ WIDTH(win) -= 2 * border_width;
+ }
+ } else {
+ /* Normal floating window. */
+ /* Update geometry if new region. */
+ if (r != ws->old_r)
+ load_float_geom(win);
+
+ if ((win->quirks & SWM_Q_FULLSCREEN) &&
+ WIDTH(win) >= WIDTH(r) && HEIGHT(win) >= HEIGHT(r)) {
+ /* Remove border for FULLSCREEN quirk. */
+ win->bordered = 0;
+ } else if (!win->manual) {
+ if (win->transient && (win->quirks & SWM_Q_TRANSSZ)) {
+ /* Adjust size on TRANSSZ quirk. */
+ WIDTH(win) = (double)WIDTH(r) * dialog_ratio;
+ HEIGHT(win) = (double)HEIGHT(r) * dialog_ratio;
+ }
- store_float_geom(win);
+ if (!(win->quirks & SWM_Q_ANYWHERE)) {
+ /*
+ * Floaters and transients are auto-centred
+ * unless manually moved, resized or ANYWHERE
+ * quirk is set.
+ */
+ X(win) = X(r) + (WIDTH(r) - WIDTH(win)) / 2 -
+ BORDER(win);
+ Y(win) = Y(r) + (HEIGHT(r) - HEIGHT(win)) / 2 -
+ BORDER(win);
+ store_float_geom(win);
+ }
+ }
}
/* Ensure at least 1 pixel of the window is in the region. */
stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
{
struct swm_geometry win_g, r_g = *g;
- struct ws_win *win, *fs_win = NULL;
+ struct ws_win *win, *fs_win = NULL, *max_win = NULL;
int i, j, s, stacks;
int w_inc = 1, h_inc, w_base = 1, h_base;
int hrh, extra = 0, h_slice, last_h = 0;
if (winno == 0 && count_win(ws, 1) == 0)
return;
+ /* Find first tiled window. */
TAILQ_FOREACH(win, &ws->winlist, entry)
if (!win->transient && !win->floating && !win->iconic)
break;
continue;
}
+ if (win->maximized) {
+ max_win = win;
+ continue;
+ }
+
if (split && i == split) {
colno = (winno - mwin) / stacks;
if (s <= (winno - mwin) % stacks)
update_window(win);
}
- map_window(win, get_sibling(win, SWM_STACK_BELOW));
+ reorder_window(win, SWM_STACK_ABOVE);
+ map_window(win);
last_h = win_g.h;
i++;
}
/* Map/raise focused tiled window to top if windows could overlap. */
- if (tile_gap < 0 && ws->focus != NULL && !ws->focus->floating)
- map_window(ws->focus, get_sibling(ws->focus, SWM_STACK_TOP));
+ if (tile_gap < 0 && ws->focus != NULL && !ws->focus->floating &&
+ !ws->focus->maximized) {
+ reorder_window(ws->focus, SWM_STACK_TOP);
+ map_window(ws->focus);
+ }
notiles:
/* now, stack all the floaters and transients */
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) {
fs_win = win;
continue;
}
- stack_floater(win, ws->r);
- map_window(win, XCB_WINDOW_NONE);
+ if (win->maximized) {
+ max_win = win;
+ continue;
+ }
+
+ stack_floater(win);
+ reorder_window(win, SWM_STACK_ABOVE);
+ map_window(win);
}
- /* Make sure fs_win is stacked last so it's on top. */
+ /* Make sure fs_win is stacked on top. */
if (fs_win) {
- stack_floater(fs_win, ws->r);
- map_window(fs_win, XCB_WINDOW_NONE);
+ stack_floater(fs_win);
+ reorder_window(fs_win, SWM_STACK_TOP);
+ map_window(fs_win);
+ } else if (max_win) {
+ stack_floater(max_win);
+ reorder_window(max_win, SWM_STACK_TOP);
+ map_window(max_win);
}
+
+ DNPRINTF(SWM_D_STACK, "stack_master: max_win: 0x%x\n", WINID(max_win));
}
void
if (w->transient || w->iconic)
continue;
- if (!w->mapped && w != win)
- map_window(w, XCB_WINDOW_NONE);
+ if (!w->mapped && w != win) {
+ reorder_window(w, SWM_STACK_TOP);
+ map_window(w);
+ }
- if (w->floating && !w->floatmaxed) {
+ if (w->floating && !w->maximized) {
/*
* retain geometry for retrieval on exit from
* max_stack mode
*/
store_float_geom(w);
- w->floatmaxed = 1;
+ w->maximized = 1;
}
/* only reconfigure if necessary */
/* If a parent exists, map/raise it first. */
if (parent) {
- map_window(parent, XCB_WINDOW_NONE);
+ reorder_window(parent, SWM_STACK_TOP);
+ map_window(parent);
/* Map siblings next. */
TAILQ_FOREACH(w, &ws->winlist, entry)
if (w != win && !w->iconic &&
w->transient == parent->id) {
- stack_floater(w, ws->r);
- map_window(w, XCB_WINDOW_NONE);
+ stack_floater(w);
+ reorder_window(w, SWM_STACK_ABOVE);
+ map_window(w);
}
}
/* Map/raise focused window. */
- map_window(win, XCB_WINDOW_NONE);
+ reorder_window(win, SWM_STACK_TOP);
+ map_window(win);
/* 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);
- map_window(w, XCB_WINDOW_NONE);
+ stack_floater(w);
+ reorder_window(w, SWM_STACK_ABOVE);
+ map_window(w);
}
}
DNPRINTF(SWM_D_MOVE, "send_to_ws: win 0x%x, ws %d\n", win->id, wsid);
+ if (wsid >= workspace_limit)
+ return;
+
+ if (win->ws->idx == wsid)
+ return;
+
win_to_ws(win, wsid, 1);
+ win->maximized = 0;
+
/* Restack and set new focus. */
stack();
if (r == NULL || r->ws == NULL)
return;
+ if (r->ws->focus && r->ws->focus->maximized)
+ return;
+
r->ws->always_raise = !r->ws->always_raise;
/* bring floaters back to top */
}
int
-floating_toggle_win(struct ws_win *win)
+clear_maximized(struct workspace *ws)
+{
+ struct ws_win *w;
+ int count = 0;
+
+ /* Clear any maximized win(s) on ws. */
+ TAILQ_FOREACH(w, &ws->winlist, entry)
+ if (w->maximized) {
+ w->maximized = 0;
+ if (w->floating)
+ load_float_geom(w);
+ ++count;
+ }
+
+ return count;
+}
+
+void
+maximize_toggle(struct swm_region *r, union arg *args)
{
+ struct ws_win *win = r->ws->focus;
+
+ /* suppress unused warning since var is needed */
+ (void)args;
+
if (win == NULL)
- return (0);
+ return;
- if (win->ws->r == NULL)
+ DNPRINTF(SWM_D_MISC, "maximize_toggle: win %#x\n", win->id);
+
+ if (win->ewmh_flags & EWMH_F_FULLSCREEN)
+ return;
+
+ if (win->ws->cur_layout == &layouts[SWM_MAX_STACK])
+ return;
+
+ if (win->floating) {
+ if (win->maximized)
+ load_float_geom(win);
+ else
+ store_float_geom(win);
+ }
+
+ win->maximized = !win->maximized;
+
+ stack();
+
+ if (win == win->ws->focus)
+ focus_win(win);
+
+ focus_flush();
+ DNPRINTF(SWM_D_MISC, "maximize_toggle: done\n");
+}
+
+int
+floating_toggle_win(struct ws_win *win)
+{
+ if (win == NULL)
return (0);
/* reject floating toggles in max stack mode */
if (win->ws->cur_layout == &layouts[SWM_MAX_STACK])
return (0);
+ if (win->ws->r == NULL)
+ return (0);
+
if (win->floating) {
- if (!win->floatmaxed) {
- /* retain position for refloat */
+ if (!win->maximized)
store_float_geom(win);
- }
- win->floating = 0;
} else {
load_float_geom(win);
- win->floating = 1;
}
+ win->floating = !win->floating;
+ win->maximized = 0;
+
ewmh_update_actions(win);
return (1);
if (!win->transient && !win->floating)
return;
- /* reject resizes in max mode for floaters (transient ok) */
- if (win->floatmaxed)
+ /* In max_stack mode, should only resize transients. */
+ if (win->ws->cur_layout == &layouts[SWM_MAX_STACK] && !win->transient)
return;
- win->manual = 1;
- ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom,
- _NET_WM_STATE_ADD);
+ win->maximized = 0;
+
+ if (!win->manual) {
+ win->manual = 1;
+ ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom,
+ _NET_WM_STATE_ADD);
+ }
stack();
if (win->ws->cur_layout == &layouts[SWM_MAX_STACK] && !win->transient)
return;
+ win->maximized = 0;
+
if (!win->manual) {
win->manual = 1;
ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom,
{ "focus_next", focus, {.id = SWM_ARG_ID_FOCUSNEXT} },
{ "focus_prev", focus, {.id = SWM_ARG_ID_FOCUSPREV} },
{ "focus_urgent", focus, {.id = SWM_ARG_ID_FOCUSURGENT} },
+ { "maximize_toggle", maximize_toggle,{0} },
{ "height_grow", resize_step, {.id = SWM_ARG_ID_HEIGHTGROW} },
{ "height_shrink", resize_step, {.id = SWM_ARG_ID_HEIGHTSHRINK} },
{ "iconify", iconify, {0} },
free(real_args);
}
+/* Argument tokenizer. */
+char *
+argsep(char **sp) {
+ int single_quoted = 0, double_quoted = 0;
+ char *arg, *cp, *next;
+
+ if (*sp == NULL)
+ return NULL;
+
+ /* Eat and move characters until end of argument is found. */
+ for (arg = next = cp = *sp; *cp != '\0'; ++cp) {
+ if (!double_quoted && *cp == '\'') {
+ /* Eat single-quote. */
+ single_quoted = !single_quoted;
+ } else if (!single_quoted && *cp == '"') {
+ /* Eat double-quote. */
+ double_quoted = !double_quoted;
+ } else if (!single_quoted && *cp == '\\' && *(cp + 1) == '"') {
+ /* Eat backslash; copy escaped character to arg. */
+ *next++ = *(++cp);
+ } else if (!single_quoted && !double_quoted && *cp == '\\' &&
+ (*(cp + 1) == '\'' || *(cp + 1) == ' ')) {
+ /* Eat backslash; move escaped character. */
+ *next++ = *(++cp);
+ } else if (!single_quoted && !double_quoted &&
+ (*cp == ' ' || *cp == '\t')) {
+ /* Terminate argument. */
+ *next++ = '\0';
+ /* Point sp to beginning of next argument. */
+ *sp = ++cp;
+ break;
+ } else {
+ /* Move regular character. */
+ *next++ = *cp;
+ }
+ }
+
+ /* Terminate argument if end of string. */
+ if (*cp == '\0') {
+ *next = '\0';
+ *sp = NULL;
+ }
+
+ return arg;
+}
+
void
spawn_insert(const char *name, const char *args, int flags)
{
- char *arg, *cp, *ptr;
struct spawn_prog *sp;
+ char *arg, *dup, *ptr;
+
+ DNPRINTF(SWM_D_SPAWN, "spawn_insert: %s[%s]\n", name, args);
- DNPRINTF(SWM_D_SPAWN, "spawn_insert: %s\n", name);
+ if (args == NULL || *args == '\0')
+ return;
if ((sp = calloc(1, sizeof *sp)) == NULL)
err(1, "spawn_insert: calloc");
if ((sp->name = strdup(name)) == NULL)
err(1, "spawn_insert: strdup");
- /* convert the arguments to an argument list */
- if ((ptr = cp = strdup(args)) == NULL)
+ /* Convert the arguments to an argument list. */
+ if ((ptr = dup = strdup(args)) == NULL)
err(1, "spawn_insert: strdup");
- while ((arg = strsep(&ptr, " \t")) != NULL) {
- /* empty field; skip it */
+ while ((arg = argsep(&ptr)) != NULL) {
+ /* Null argument; skip it. */
if (*arg == '\0')
continue;
if ((sp->argv[sp->argc - 1] = strdup(arg)) == NULL)
err(1, "spawn_insert: strdup");
}
- free(cp);
+ free(dup);
sp->flags = flags;
+ DNPRINTF(SWM_D_SPAWN, "arg %d: [%s]\n", sp->argc, sp->argv[sp->argc-1]);
TAILQ_INSERT_TAIL(&spawns, sp, entry);
DNPRINTF(SWM_D_SPAWN, "spawn_insert: leave\n");
}
{
char *args;
+ if (selector == NULL || strlen(selector) == 0)
+ return (1);
+
args = expand_tilde(value);
DNPRINTF(SWM_D_SPAWN, "setconfspawn: [%s] [%s]\n", selector, args);
/* suppress unused warning since var is needed */
(void)flags;
- DNPRINTF(SWM_D_KEY, "setconfbinding: enter\n");
- if (selector == NULL) {
+ DNPRINTF(SWM_D_KEY, "setconfbinding: enter selector: [%s], "
+ "value: [%s]\n", selector, value);
+ if (selector == NULL || strlen(selector) == 0) {
DNPRINTF(SWM_D_KEY, "setconfbinding: unbind %s\n", value);
if (parsekeys(value, mod_key, &mod, &ks) == 0) {
kfid = KF_INVALID;
setkeybinding(MODKEY, XK_k, KF_FOCUS_PREV, NULL);
setkeybinding(MODKEY_SHIFT, XK_Tab, KF_FOCUS_PREV, NULL);
setkeybinding(MODKEY, XK_u, KF_FOCUS_URGENT,NULL);
+ setkeybinding(MODKEY, XK_e, KF_MAXIMIZE_TOGGLE,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);
+ j];
keycode = xcb_key_symbols_get_keycode(syms,
XK_Num_Lock);
- if (kc == *keycode)
- numlockmask = (1 << i);
- free(keycode);
+ if (keycode) {
+ if (kc == *keycode)
+ numlockmask = (1 << i);
+ free(keycode);
+ }
}
}
free(modmap_r);
/* suppress unused warning since var is needed */
(void)flags;
- if (selector == NULL)
+ if (selector == NULL || strlen(selector) == 0)
return (0);
if ((str = strdup(selector)) == NULL)
void
setup_quirks(void)
{
- setquirk("MPlayer", "xv", "", SWM_Q_FLOAT | SWM_Q_FULLSCREEN | SWM_Q_FOCUSPREV);
- setquirk("OpenOffice.org 3.2", "VCLSalFrame", "", SWM_Q_FLOAT);
- setquirk("Firefox-bin", "firefox-bin", "", SWM_Q_TRANSSZ);
- setquirk("Firefox", "Dialog", "", SWM_Q_FLOAT);
- setquirk("Gimp", "gimp", "", SWM_Q_FLOAT | SWM_Q_ANYWHERE);
- setquirk("XTerm", "xterm", "", SWM_Q_XTERM_FONTADJ);
- setquirk("xine", "Xine Window", "", SWM_Q_FLOAT | SWM_Q_ANYWHERE);
- setquirk("Xitk", "Xitk Combo", "", SWM_Q_FLOAT | SWM_Q_ANYWHERE);
- setquirk("xine", "xine Panel", "", SWM_Q_FLOAT | SWM_Q_ANYWHERE);
- setquirk("Xitk", "Xine Window", "", SWM_Q_FLOAT | SWM_Q_ANYWHERE);
- setquirk("xine", "xine Video Fullscreen Window", "", SWM_Q_FULLSCREEN | SWM_Q_FLOAT);
- setquirk("pcb", "pcb", "", SWM_Q_FLOAT);
- setquirk("SDL_App", "SDL_App", "", SWM_Q_FLOAT | SWM_Q_FULLSCREEN);
+ setquirk("MPlayer", "xv", ".*", SWM_Q_FLOAT | SWM_Q_FULLSCREEN | SWM_Q_FOCUSPREV);
+ setquirk("OpenOffice.org 3.2", "VCLSalFrame", ".*", SWM_Q_FLOAT);
+ setquirk("Firefox-bin", "firefox-bin", ".*", SWM_Q_TRANSSZ);
+ setquirk("Firefox", "Dialog", ".*", SWM_Q_FLOAT);
+ setquirk("Gimp", "gimp", ".*", SWM_Q_FLOAT | SWM_Q_ANYWHERE);
+ setquirk("XTerm", "xterm", ".*", SWM_Q_XTERM_FONTADJ);
+ setquirk("xine", "Xine Window", ".*", SWM_Q_FLOAT | SWM_Q_ANYWHERE);
+ setquirk("Xitk", "Xitk Combo", ".*", SWM_Q_FLOAT | SWM_Q_ANYWHERE);
+ setquirk("xine", "xine Panel", ".*", SWM_Q_FLOAT | SWM_Q_ANYWHERE);
+ setquirk("Xitk", "Xine Window", ".*", SWM_Q_FLOAT | SWM_Q_ANYWHERE);
+ setquirk("xine", "xine Video Fullscreen Window", ".*", SWM_Q_FULLSCREEN | SWM_Q_FLOAT);
+ setquirk("pcb", "pcb", ".*", SWM_Q_FLOAT);
+ setquirk("SDL_App", "SDL_App", ".*", SWM_Q_FLOAT | SWM_Q_FULLSCREEN);
}
/* conf file stuff */
SWM_S_FOCUS_CLOSE_WRAP,
SWM_S_FOCUS_DEFAULT,
SWM_S_FOCUS_MODE,
+ SWM_S_ICONIC_ENABLED,
SWM_S_REGION_PADDING,
SWM_S_SPAWN_ORDER,
SWM_S_SPAWN_TERM,
int i, ws_id, num_screens;
char *b, *str;
- /* suppress unused warning since var is needed */
- (void)selector;
-
switch (flags) {
case SWM_S_BAR_ACTION:
free(bar_argv[0]);
else
errx(1, "focus_mode");
break;
+ case SWM_S_ICONIC_ENABLED:
+ iconic_enabled = atoi(value);
+ break;
case SWM_S_REGION_PADDING:
region_padding = atoi(value);
if (region_padding < 0)
int
setconfcolor(const char *selector, const char *value, int flags)
{
- setscreencolor(value, ((selector == NULL)?-1:atoi(selector)), flags);
+ setscreencolor(value,
+ (selector == NULL || strlen(selector) == 0) ? -1 : atoi(selector),
+ flags);
return (0);
}
{ "focus_close_wrap", setconfvalue, SWM_S_FOCUS_CLOSE_WRAP },
{ "focus_default", setconfvalue, SWM_S_FOCUS_DEFAULT },
{ "focus_mode", setconfvalue, SWM_S_FOCUS_MODE },
+ { "iconic_enabled", setconfvalue, SWM_S_ICONIC_ENABLED },
{ "keyboard_mapping", setkeymapping, 0 },
{ "layout", setlayout, 0 },
{ "modkey", setconfmodkey, 0 },
struct ws_win *win, *ww;
int ws_idx;
char ws_idx_str[SWM_PROPLEN];
- char *name;
+ char *class, *instance, *name;
struct swm_region *r;
struct pid_e *p;
struct quirk *qp;
Y(win) = gr->y + gr->border_width - border_width;
win->bordered = 1;
win->mapped = mapped;
- win->floatmaxed = 0;
+ win->maximized = 0;
win->ewmh_flags = 0;
win->s = r->s; /* this never changes */
ewmh_autoquirk(win);
/* Determine initial quirks. */
- if (xcb_icccm_get_wm_class_reply(conn,
+ xcb_icccm_get_wm_class_reply(conn,
xcb_icccm_get_wm_class(conn, win->id),
- &win->ch, NULL)) {
- name = get_win_name(win->id);
+ &win->ch, NULL);
- DNPRINTF(SWM_D_CLASS, "manage_window: class: %s, instance: %s, "
- "name: %s\n",
- win->ch.class_name, win->ch.instance_name, name);
-
- /* java is retarded so treat it special */
- if (strstr(win->ch.instance_name, "sun-awt")) {
- DNPRINTF(SWM_D_CLASS, "manage_window: java window "
- "detected.\n");
- win->java = 1;
- }
-
- TAILQ_FOREACH(qp, &quirks, entry) {
- if (regexec(&qp->regex_class, win->ch.class_name, 0,
- NULL, 0) == 0 && regexec(&qp->regex_instance,
- win->ch.instance_name, 0, NULL, 0) == 0 &&
- regexec(&qp->regex_name, name, 0, NULL, 0) == 0) {
- DNPRINTF(SWM_D_CLASS, "manage_window: matched "
- "quirk: %s:%s:%s mask: %#lx\n", qp->class,
- qp->instance, qp->name, qp->quirk);
- if (qp->quirk & SWM_Q_FLOAT)
- win->floating = 1;
- win->quirks = qp->quirk;
- }
+ class = win->ch.class_name ? win->ch.class_name : "";
+ instance = win->ch.instance_name ? win->ch.instance_name : "";
+ name = get_win_name(win->id);
- }
+ DNPRINTF(SWM_D_CLASS, "manage_window: class: %s, instance: %s, "
+ "name: %s\n", class, instance, name);
- free(name);
+ /* java is retarded so treat it special */
+ if (win->ch.instance_name && strstr(win->ch.instance_name, "sun-awt")) {
+ DNPRINTF(SWM_D_CLASS, "manage_window: java window detected.\n");
+ win->java = 1;
}
+ TAILQ_FOREACH(qp, &quirks, entry) {
+ if (regexec(&qp->regex_class, class, 0, NULL, 0) == 0 &&
+ regexec(&qp->regex_instance, instance, 0, NULL, 0) == 0 &&
+ regexec(&qp->regex_name, name, 0, NULL, 0) == 0) {
+ DNPRINTF(SWM_D_CLASS, "manage_window: matched "
+ "quirk: %s:%s:%s mask: %#lx\n", qp->class,
+ qp->instance, qp->name, qp->quirk);
+ if (qp->quirk & SWM_Q_FLOAT)
+ win->floating = 1;
+ win->quirks = qp->quirk;
+ }
+ }
+
+ free(name);
+
/* Alter window position if quirky */
if (win->quirks & SWM_Q_ANYWHERE)
win->manual = 1;
xcb_flush(conn);
}
} else if ((!win->manual || win->quirks & SWM_Q_ANYWHERE) &&
- !(win->ewmh_flags & EWMH_F_FULLSCREEN)) {
+ !(win->ewmh_flags & EWMH_F_FULLSCREEN) && !win->maximized) {
if (win->ws->r)
r = win->ws->r;
else if (win->ws->old_r)
win->g_floatvalid = 1;
if (win->floating && r && (win->transient ||
- win->ws->cur_layout != &layouts[SWM_MAX_STACK])) {
+ win->ws->cur_layout != &layouts[SWM_MAX_STACK])
+ && !win->maximized) {
WIDTH(win) = win->g_float.w;
HEIGHT(win) = win->g_float.h;
if (r) {
- stack_floater(win, r);
+ stack_floater(win);
focus_flush();
}
} else {
void
mapnotify(xcb_map_notify_event_t *e)
{
- struct ws_win *win;
+ struct ws_win *win, *parent = NULL;
+ struct workspace *ws;
DNPRINTF(SWM_D_EVENT, "mapnotify: window: 0x%x\n", e->window);
if ((win = manage_window(e->window, 1)) == NULL)
return;
+ ws = win->ws;
+
+ /* Need to know if win was mapped due to ws switch. */
+ if (ws->state == SWM_WS_STATE_MAPPED) {
+ if (ws->focus_pending && ws->focus_pending->transient)
+ parent = find_window(win->transient);
+
+ /* If window's parent is maximized, don't clear it. */
+ if ((parent == NULL) || !parent->maximized)
+ if (clear_maximized(ws) > 0)
+ stack();
+ }
win->mapped = 1;
set_win_state(win, XCB_ICCCM_WM_STATE_NORMAL);
if (focus_mode != SWM_FOCUS_FOLLOW && WS_FOCUSED(win->ws)) {
- if (win->ws->focus_pending == win) {
+ if (ws->focus_pending == win) {
focus_win(win);
- win->ws->focus_pending = NULL;
+ ws->focus_pending = NULL;
focus_flush();
}
}
void
mappingnotify(xcb_mapping_notify_event_t *e)
{
+ struct ws_win *w;
+ int i, j, num_screens;
+
xcb_refresh_keyboard_mapping(syms, e);
- if (e->request == XCB_MAPPING_KEYBOARD)
+ if (e->request == XCB_MAPPING_KEYBOARD) {
grabkeys();
+
+ /* Regrab buttons on all managed windows. */
+ num_screens = get_screen_count();
+ for (i = 0; i < num_screens; i++)
+ for (j = 0; j < workspace_limit; j++)
+ TAILQ_FOREACH(w, &screens[i].ws[j].winlist,
+ entry)
+ grabbuttons(w);
+ }
}
void
focus_flush();
}
} else if (e->state == XCB_PROPERTY_DELETE) {
+ /* Reload floating geometry in case region changed. */
+ if (win->floating)
+ load_float_geom(win);
+
/* The window is no longer iconic, restack ws. */
if (focus_mode != SWM_FOCUS_FOLLOW)
ws->focus_pending = get_focus_magic(win);
if (ws == NULL)
errx(1, "new_region: no free workspaces");
+ if (ws->state == SWM_WS_STATE_HIDDEN)
+ ws->state = SWM_WS_STATE_MAPPING;
+
X(r) = x;
Y(r) = y;
WIDTH(r) = w;
TAILQ_FOREACH(r, &screens[i].orl, entry) {
TAILQ_FOREACH(win, &r->ws->winlist, entry)
unmap_window(win);
+ r->ws->state = SWM_WS_STATE_HIDDEN;
/* The screen shouldn't focus on an unused region. */
if (screens[i].r_focus == r)
bar_draw();
focus_flush();
+
+ /* Update workspace state on all regions. */
+ for (i = 0; i < num_screens; i++)
+ TAILQ_FOREACH(r, &screens[i].rl, entry)
+ r->ws->state = SWM_WS_STATE_MAPPED;
}
void
ws->focus_pending = NULL;
ws->r = NULL;
ws->old_r = NULL;
+ ws->state = SWM_WS_STATE_HIDDEN;
TAILQ_INIT(&ws->winlist);
TAILQ_INIT(&ws->unmanagedlist);