From: Reginald Kennedy Date: Tue, 23 Apr 2013 04:42:37 +0000 (+0800) Subject: Add ability to move/resize floating windows beyond the region boundary. X-Git-Url: https://jasonwoof.com/gitweb/?p=spectrwm.git;a=commitdiff_plain;h=0cef11f17d67e85505327ce278ab1f32fc8c7e87 Add ability to move/resize floating windows beyond the region boundary. Windows will change regions based on the cursor location or, if being moved by a key binding, window center. Add 'soft boundary' window containment behavior to region boundaries. When moving a window past the region boundary, the window will 'snap' to the region boundary until it is moved more than boundary_width distance beyond the edge. Add constraint to ensure windows always have at least 1 pixel in a region. Add new boundary_width configuration option. Disable the window containment behavior by setting this option to 0. Fix floating window stacking order issue on multiple-region setups. Eliminate unneeded region parameter on load_float_geom() and store_float_geom(). --- diff --git a/spectrwm.1 b/spectrwm.1 index 7ab0a11..7c711f7 100644 --- a/spectrwm.1 +++ b/spectrwm.1 @@ -210,6 +210,11 @@ section below. .It Ic border_width Set window border thickness in pixels. Disable all borders by setting to 0. +.It Ic boundary_width +Set region containment boundary width in pixels. This is how far a window +must be dragged/resized beyond the region edge before it is allowed outside +the region. This has no effect when manipulating the window with key +bindings. Disable the window containment effect by setting to 0. .It Ic clock_enabled Enable or disable displaying the clock in the status bar. Disable by setting to 0 diff --git a/spectrwm.c b/spectrwm.c index 93b4244..6945965 100644 --- a/spectrwm.c +++ b/spectrwm.c @@ -246,6 +246,16 @@ u_int32_t swm_debug = 0 #define WINID(w) ((w) ? (w)->id : 0) #define YESNO(x) ((x) ? "yes" : "no") +/* Constrain Window flags */ +#define SWM_CW_RESIZABLE (0x01) +#define SWM_CW_SOFTBOUNDARY (0x02) +#define SWM_CW_HARDBOUNDARY (0x04) +#define SWM_CW_RIGHT (0x10) +#define SWM_CW_LEFT (0x20) +#define SWM_CW_BOTTOM (0x40) +#define SWM_CW_TOP (0x80) +#define SWM_CW_ALLSIDES (0xf0) + #define SWM_FOCUS_DEFAULT (0) #define SWM_FOCUS_FOLLOW (1) #define SWM_FOCUS_MANUAL (2) @@ -288,6 +298,7 @@ Display *display; xcb_connection_t *conn; xcb_key_symbols_t *syms; +int boundary_width = 50; int cycle_empty = 0; int cycle_visible = 0; int term_width = 0; @@ -883,7 +894,7 @@ int conf_load(const char *, int); void configurenotify(xcb_configure_notify_event_t *); void configurerequest(xcb_configure_request_event_t *); void config_win(struct ws_win *, xcb_configure_request_event_t *); -void constrain_window(struct ws_win *, struct swm_region *, int); +void constrain_window(struct ws_win *, struct swm_geometry *, int *); int count_win(struct workspace *, int); void cursors_cleanup(void); void cursors_load(void); @@ -961,9 +972,9 @@ void kill_refs(struct ws_win *); #ifdef SWM_DEBUG void leavenotify(xcb_leave_notify_event_t *); #endif -void load_float_geom(struct ws_win *, struct swm_region *); +void load_float_geom(struct ws_win *); struct ws_win *manage_window(xcb_window_t, uint16_t); -void map_window(struct ws_win *); +void map_window(struct ws_win *, xcb_window_t); void mapnotify(xcb_map_notify_event_t *); void mappingnotify(xcb_mapping_notify_event_t *); void maprequest(xcb_map_request_event_t *); @@ -988,6 +999,9 @@ void quirk_replace(struct quirk *, const char *, const char *, unsigned long); void quit(struct swm_region *, union arg *); void raise_toggle(struct swm_region *, union arg *); +void region_containment(struct ws_win *, struct swm_region *, int); +struct swm_region *region_under(struct swm_screen *, int, int); +void regionize(struct ws_win *, int, int); void resize(struct ws_win *, union arg *); void resize_step(struct swm_region *, union arg *); void restart(struct swm_region *, union arg *); @@ -1042,7 +1056,7 @@ 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 *); +void store_float_geom(struct ws_win *); char *strdupsafe(const char *); void swapwin(struct swm_region *, union arg *); void switchws(struct swm_region *, union arg *); @@ -1061,6 +1075,7 @@ int validate_win(struct ws_win *); int validate_ws(struct workspace *); /*void visibilitynotify(xcb_visibility_notify_event_t *);*/ void version(struct swm_region *, union arg *); +void win_to_ws(struct ws_win *, int, int); pid_t window_get_pid(xcb_window_t); void wkill(struct swm_region *, union arg *); void workaround(void); @@ -1401,12 +1416,12 @@ ewmh_set_win_fullscreen(struct ws_win *win, int fs) if (fs) { if (!win->g_floatvalid) - store_float_geom(win, win->ws->r); + store_float_geom(win); win->g = win->ws->r->g; win->bordered = 0; } else { - load_float_geom(win, win->ws->r); + load_float_geom(win); } return (1); @@ -2797,18 +2812,27 @@ quit(struct swm_region *r, union arg *args) } void -map_window(struct ws_win *win) +map_window(struct ws_win *win, xcb_window_t sibling) { - uint32_t val = XCB_STACK_MODE_ABOVE; + uint16_t mode = XCB_CONFIG_WINDOW_STACK_MODE; + 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; + } + + val[i] = XCB_STACK_MODE_ABOVE; if (win == NULL) return; - DNPRINTF(SWM_D_EVENT, "map_window: win 0x%x, mapped: %s\n", win->id, - YESNO(win->mapped)); + DNPRINTF(SWM_D_EVENT, "map_window: win 0x%x, mapped: %s, sibling: %x\n", + win->id, YESNO(win->mapped), sibling); - xcb_configure_window(conn, win->id, - XCB_CONFIG_WINDOW_STACK_MODE, &val); + xcb_configure_window(conn, win->id, mode, val); if (win->mapped) return; @@ -3288,22 +3312,22 @@ focus_win(struct ws_win *win) ws->always_raise) { /* If a parent exists, map it first. */ if (parent) { - map_window(parent); + map_window(parent, XCB_WINDOW_NONE); /* Map siblings next. */ TAILQ_FOREACH(w, &ws->winlist, entry) if (w != win && !w->iconic && w->transient == parent->id) - map_window(w); + map_window(w, XCB_WINDOW_NONE); } /* Map focused window. */ - map_window(win); + map_window(win, XCB_WINDOW_NONE); /* Finally, map children of focus window. */ TAILQ_FOREACH(w, &ws->winlist, entry) if (w->transient == win->id && !w->iconic) - map_window(w); + map_window(w, XCB_WINDOW_NONE); } set_region(ws->r); @@ -4030,15 +4054,15 @@ stack(void) { } void -store_float_geom(struct ws_win *win, struct swm_region *r) +store_float_geom(struct ws_win *win) { - if (win == NULL || r == NULL) + if (win == NULL || win->ws->r == NULL) return; /* retain window geom and region geom */ win->g_float = win->g; - win->g_float.x -= X(r); - win->g_float.y -= Y(r); + win->g_float.x -= X(win->ws->r); + win->g_float.y -= Y(win->ws->r); win->g_floatvalid = 1; DNPRINTF(SWM_D_MISC, "store_float_geom: window: 0x%x, g: (%d,%d)" " %d x %d, g_float: (%d,%d) %d x %d\n", win->id, X(win), Y(win), @@ -4047,15 +4071,15 @@ store_float_geom(struct ws_win *win, struct swm_region *r) } void -load_float_geom(struct ws_win *win, struct swm_region *r) +load_float_geom(struct ws_win *win) { - if (win == NULL || r == NULL) + if (win == NULL || win->ws->r == NULL) return; if (win->g_floatvalid) { win->g = win->g_float; - X(win) += X(r); - Y(win) += Y(r); + X(win) += X(win->ws->r); + Y(win) += Y(win->ws->r); DNPRINTF(SWM_D_MISC, "load_float_geom: window: 0x%x, g: (%d,%d)" "%d x %d\n", win->id, X(win), Y(win), WIDTH(win), HEIGHT(win)); @@ -4080,7 +4104,7 @@ stack_floater(struct ws_win *win, struct swm_region *r) if (win->floatmaxed || (r != r->ws->old_r && !(win->ewmh_flags & EWMH_F_FULLSCREEN))) { /* update geometry for the new region */ - load_float_geom(win, r); + load_float_geom(win); } win->floatmaxed = 0; @@ -4090,7 +4114,7 @@ stack_floater(struct ws_win *win, struct swm_region *r) */ if (win->ewmh_flags & EWMH_F_FULLSCREEN) { if (!win->g_floatvalid) - store_float_geom(win, win->ws->r); + store_float_geom(win); win->g = r->g; } @@ -4127,11 +4151,11 @@ stack_floater(struct ws_win *win, struct swm_region *r) 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, r); + store_float_geom(win); } - /* keep window within region bounds */ - constrain_window(win, r, 0); + /* Ensure at least 1 pixel of the window is in the region. */ + region_containment(win, r, SWM_CW_ALLSIDES); update_window(win); } @@ -4174,7 +4198,8 @@ void 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, *wtmp; + xcb_window_t sibling; 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; @@ -4351,7 +4376,13 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) update_window(win); } - map_window(win); + wtmp = TAILQ_PREV(win, ws_win_list, entry); + if (wtmp) + sibling = wtmp->id; + else + sibling = ws->r->bar->id; + + map_window(win, sibling); last_h = win_g.h; i++; @@ -4371,13 +4402,13 @@ notiles: } stack_floater(win, ws->r); - map_window(win); + map_window(win, XCB_WINDOW_NONE); } /* Make sure fs_win is stacked last so it's on top. */ if (fs_win) { stack_floater(fs_win, ws->r); - map_window(fs_win); + map_window(fs_win, XCB_WINDOW_NONE); } } @@ -4520,14 +4551,14 @@ max_stack(struct workspace *ws, struct swm_geometry *g) continue; if (!w->mapped && w != win) - map_window(w); + map_window(w, XCB_WINDOW_NONE); if (w->floating && !w->floatmaxed) { /* * retain geometry for retrieval on exit from * max_stack mode */ - store_float_geom(w, ws->r); + store_float_geom(w); w->floatmaxed = 1; } @@ -4549,25 +4580,25 @@ max_stack(struct workspace *ws, struct swm_geometry *g) /* If a parent exists, map/raise it first. */ if (parent) { - map_window(parent); + map_window(parent, XCB_WINDOW_NONE); /* 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); + map_window(w, XCB_WINDOW_NONE); } } /* Map/raise focused window. */ - map_window(win); + map_window(win, XCB_WINDOW_NONE); /* 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); + map_window(w, XCB_WINDOW_NONE); } } @@ -4597,31 +4628,74 @@ send_to_rg(struct swm_region *r, union arg *args) send_to_ws(r, &a); } +struct swm_region * +region_under(struct swm_screen *s, int x, int y) +{ + struct swm_region *r = NULL; + + TAILQ_FOREACH(r, &s->rl, entry) { + DNPRINTF(SWM_D_MISC, "region_under: ws: %d, region g: (%d,%d) " + "%d x %d, coords: (%d,%d)\n", r->ws->idx, X(r), Y(r), + WIDTH(r), HEIGHT(r), x, y); + if (X(r) <= x && x < MAX_X(r)) + if (Y(r) <= y && y < MAX_Y(r)) + return (r); + } + + return (NULL); +} + void send_to_ws(struct swm_region *r, union arg *args) { int wsid = args->id; - struct ws_win *win = NULL, *parent; - struct workspace *ws, *nws, *pws; - char ws_idx_str[SWM_PROPLEN]; - - if (wsid >= workspace_limit) - return; + struct ws_win *win = NULL; if (r && r->ws && r->ws->focus) win = r->ws->focus; else return; - if (win->ws->idx == wsid) + DNPRINTF(SWM_D_MOVE, "send_to_ws: win 0x%x, ws %d\n", win->id, wsid); + + win_to_ws(win, wsid, 1); + + /* Restack and set new focus. */ + stack(); + + if (focus_mode != SWM_FOCUS_FOLLOW) { + if (r->ws->focus_pending) { + focus_win(r->ws->focus_pending); + r->ws->focus_pending = NULL; + } else { + xcb_set_input_focus(conn, + XCB_INPUT_FOCUS_PARENT, r->id, + XCB_CURRENT_TIME); + } + } + + focus_flush(); +} + +void +win_to_ws(struct ws_win *win, int wsid, int unfocus) +{ + struct ws_win *parent; + struct workspace *ws, *nws, *pws; + char ws_idx_str[SWM_PROPLEN]; + + if (wsid >= workspace_limit) return; - DNPRINTF(SWM_D_MOVE, "send_to_ws: win 0x%x, ws %d -> %d\n", win->id, - win->ws->idx, wsid); + if (win->ws->idx == wsid) + return; ws = win->ws; nws = &win->s->ws[wsid]; + DNPRINTF(SWM_D_MOVE, "win_to_ws: win 0x%x, ws %d -> %d\n", win->id, + ws->idx, wsid); + /* Update the window's workspace property: _SWM_WS */ if (snprintf(ws_idx_str, SWM_PROPLEN, "%d", nws->idx) < SWM_PROPLEN) { if (focus_mode != SWM_FOCUS_FOLLOW) @@ -4655,7 +4729,7 @@ send_to_ws(struct swm_region *r, union arg *args) TAILQ_INSERT_TAIL(&nws->winlist, parent, entry); parent->ws = nws; - DNPRINTF(SWM_D_PROP, "send_to_ws: set " + DNPRINTF(SWM_D_PROP, "win_to_ws: set " "property: _SWM_WS: %s\n", ws_idx_str); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, parent->id, a_swm_ws, XCB_ATOM_STRING, 8, @@ -4663,7 +4737,8 @@ send_to_ws(struct swm_region *r, union arg *args) } } - unfocus_win(win); + if (unfocus) + unfocus_win(win); /* Don't unmap if new ws is visible */ if (nws->r == NULL) @@ -4678,30 +4753,14 @@ send_to_ws(struct swm_region *r, union arg *args) unfocus_win(nws->focus); nws->focus = win; - DNPRINTF(SWM_D_PROP, "send_to_ws: set property: _SWM_WS: %s\n", + DNPRINTF(SWM_D_PROP, "win_to_ws: set property: _SWM_WS: %s\n", ws_idx_str); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id, a_swm_ws, XCB_ATOM_STRING, 8, strlen(ws_idx_str), ws_idx_str); - - /* Restack and set new focus. */ - stack(); - - if (focus_mode != SWM_FOCUS_FOLLOW) { - if (ws->focus_pending) { - focus_win(ws->focus_pending); - ws->focus_pending = NULL; - } else { - xcb_set_input_focus(conn, - XCB_INPUT_FOCUS_PARENT, r->id, - XCB_CURRENT_TIME); - } - } - - focus_flush(); } - DNPRINTF(SWM_D_MOVE, "send_to_ws: done.\n"); + DNPRINTF(SWM_D_MOVE, "win_to_ws: done.\n"); } void @@ -5222,11 +5281,11 @@ floating_toggle_win(struct ws_win *win) if (win->floating) { if (!win->floatmaxed) { /* retain position for refloat */ - store_float_geom(win, r); + store_float_geom(win); } win->floating = 0; } else { - load_float_geom(win, r); + load_float_geom(win); win->floating = 1; } @@ -5261,37 +5320,85 @@ floating_toggle(struct swm_region *r, union arg *args) } void -constrain_window(struct ws_win *win, struct swm_region *r, int resizable) +region_containment(struct ws_win *win, struct swm_region *r, int opts) { - if (MAX_X(win) + BORDER(win) > MAX_X(r)) { - if (resizable) - WIDTH(win) = MAX_X(r) - X(win) - BORDER(win); + struct swm_geometry g = r->g; + int rt, lt, tp, bm, bw; + + bw = (opts & SWM_CW_SOFTBOUNDARY) ? boundary_width : 0; + + /* + * Perpendicular distance of each side of the window to the respective + * side of the region boundary. Positive values indicate the side of + * the window has passed beyond the region boundary. + */ + rt = opts & SWM_CW_RIGHT ? MAX_X(win) + BORDER(win) - MAX_X(r) : bw; + lt = opts & SWM_CW_LEFT ? X(r) - X(win) + BORDER(win) : bw; + bm = opts & SWM_CW_BOTTOM ? MAX_Y(win) + BORDER(win) - MAX_Y(r) : bw; + tp = opts & SWM_CW_TOP ? Y(r) - Y(win) + BORDER(win) : bw; + + DNPRINTF(SWM_D_MISC, "region_containment: win 0x%x, rt: %d, lt: %d, " + "bm: %d, tp: %d, SOFTBOUNDARY: %s, HARDBOUNDARY: %s\n", win->id, rt, + lt, bm, tp, YESNO(opts & SWM_CW_SOFTBOUNDARY), + YESNO(opts & SWM_CW_HARDBOUNDARY)); + + /* + * Disable containment if any of the flagged sides went beyond the + * containment boundary, or if containment is disabled. + */ + if (!(opts & SWM_CW_HARDBOUNDARY || opts & SWM_CW_SOFTBOUNDARY) || + (bw != 0 && ((rt > bw) || (lt > bw) || (bm > bw) || (tp > bw)))) { + /* Make sure window has at least 1 pixel in the region */ + g.x += 1 - WIDTH(win); + g.y += 1 - HEIGHT(win); + g.w += 2 * WIDTH(win) - 2; + g.h += 2 * HEIGHT(win) - 2; + } + + constrain_window(win, &g, &opts); +} + +/* Move or resize a window so that flagged side(s) fit into the supplied box. */ +void +constrain_window(struct ws_win *win, struct swm_geometry *b, int *opts) +{ + DNPRINTF(SWM_D_MISC, "constrain_window: window: 0x%x, (x,y) w x h: " + "(%d,%d) %d x %d, box: (x,y) w x h: (%d,%d) %d x %d, rt: %s, " + "lt: %s, bt: %s, tp: %s, allow resize: %s\n", win->id, X(win), + Y(win), WIDTH(win), HEIGHT(win), b->x, b->y, b->w, b->h, + YESNO(*opts & SWM_CW_RIGHT), YESNO(*opts & SWM_CW_LEFT), + YESNO(*opts & SWM_CW_BOTTOM), YESNO(*opts & SWM_CW_TOP), + YESNO(*opts & SWM_CW_RESIZABLE)); + + if ((*opts & SWM_CW_RIGHT) && MAX_X(win) + BORDER(win) > b->x + b->w) { + if (*opts & SWM_CW_RESIZABLE) + WIDTH(win) = b->x + b->w - X(win) - BORDER(win); else - X(win) = MAX_X(r)- WIDTH(win) - BORDER(win); + X(win) = b->x + b->w - WIDTH(win) - BORDER(win); } - if (X(win) + BORDER(win) < X(r)) { - if (resizable) - WIDTH(win) -= X(r) - X(win) - BORDER(win); + if ((*opts & SWM_CW_LEFT) && X(win) + BORDER(win) < b->x) { + if (*opts & SWM_CW_RESIZABLE) + WIDTH(win) -= b->x - X(win) - BORDER(win); - X(win) = X(r) - BORDER(win); + X(win) = b->x - BORDER(win); } - if (MAX_Y(win) + BORDER(win) > MAX_Y(r)) { - if (resizable) - HEIGHT(win) = MAX_Y(r) - Y(win) - BORDER(win); + if ((*opts & SWM_CW_BOTTOM) && MAX_Y(win) + BORDER(win) > b->y + b->h) { + if (*opts & SWM_CW_RESIZABLE) + HEIGHT(win) = b->y + b->h - Y(win) - BORDER(win); else - Y(win) = MAX_Y(r) - HEIGHT(win) - BORDER(win); + Y(win) = b->y + b->h - HEIGHT(win) - BORDER(win); } - if (Y(win) + BORDER(win) < Y(r)) { - if (resizable) - HEIGHT(win) -= Y(r) - Y(win) - BORDER(win); + if ((*opts & SWM_CW_TOP) && Y(win) + BORDER(win) < b->y) { + if (*opts & SWM_CW_RESIZABLE) + HEIGHT(win) -= b->y - Y(win) - BORDER(win); - Y(win) = Y(r) - BORDER(win); + Y(win) = b->y - BORDER(win); } - if (resizable) { + if (*opts & SWM_CW_RESIZABLE) { if (WIDTH(win) < 1) WIDTH(win) = 1; if (HEIGHT(win) < 1) @@ -5365,7 +5472,7 @@ resize(struct ws_win *win, union arg *args) /* It's possible for win to have been freed during focus_flush(). */ if (validate_win(win)) { - DNPRINTF(SWM_D_EVENT, "move: invalid win.\n"); + DNPRINTF(SWM_D_EVENT, "resize: invalid win.\n"); goto out; } @@ -5390,12 +5497,17 @@ resize(struct ws_win *win, union arg *args) break; } if (resize_stp) { - constrain_window(win, r, 1); + region_containment(win, r, SWM_CW_ALLSIDES | SWM_CW_RESIZABLE | + SWM_CW_HARDBOUNDARY); update_window(win); - store_float_geom(win,r); + store_float_geom(win); return; } + region_containment(win, r, SWM_CW_ALLSIDES | SWM_CW_RESIZABLE | + SWM_CW_SOFTBOUNDARY); + update_window(win); + /* get cursor offset from window root */ xpr = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, win->id), NULL); @@ -5477,11 +5589,13 @@ resize(struct ws_win *win, union arg *args) WIDTH(win) = g.w + dx; } - constrain_window(win, r, 1); - /* not free, don't sync more than 120 times / second */ if ((mne->time - timestamp) > (1000 / 120) ) { timestamp = mne->time; + regionize(win, mne->root_x, mne->root_y); + region_containment(win, r, SWM_CW_ALLSIDES | + SWM_CW_RESIZABLE | SWM_CW_HARDBOUNDARY | + SWM_CW_SOFTBOUNDARY); update_window(win); xcb_flush(conn); } @@ -5491,7 +5605,7 @@ resize(struct ws_win *win, union arg *args) /* It's possible for win to have been freed above. */ if (validate_win(win)) { - DNPRINTF(SWM_D_EVENT, "move: invalid win.\n"); + DNPRINTF(SWM_D_EVENT, "resize: invalid win.\n"); goto out; } break; @@ -5499,10 +5613,12 @@ resize(struct ws_win *win, union arg *args) free(evt); } if (timestamp) { + region_containment(win, r, SWM_CW_ALLSIDES | SWM_CW_RESIZABLE | + SWM_CW_HARDBOUNDARY | SWM_CW_SOFTBOUNDARY); update_window(win); xcb_flush(conn); } - store_float_geom(win,r); + store_float_geom(win); out: xcb_ungrab_pointer(conn, XCB_CURRENT_TIME); free(xpr); @@ -5523,6 +5639,23 @@ resize_step(struct swm_region *r, union arg *args) focus_flush(); } +/* Try to set window region based on supplied coordinates or window center. */ +void +regionize(struct ws_win *win, int x, int y) +{ + struct swm_region *r = NULL; + + r = region_under(win->s, x, y); + if (r == NULL) + r = region_under(win->s, X(win) + WIDTH(win) / 2, + Y(win) + HEIGHT(win) / 2); + + if (r && r != win->ws->r) { + win_to_ws(win, r->ws->idx, 0); + set_region(r); + } +} + #define SWM_MOVE_STEPS (50) void @@ -5549,16 +5682,19 @@ move(struct ws_win *win, union arg *args) if (win->ws->cur_layout == &layouts[SWM_MAX_STACK] && !win->transient) return; - win->manual = 1; + if (!win->manual) { + win->manual = 1; + ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom, + _NET_WM_STATE_ADD); + } + + /* When a stacked win is moved, float it and restack. */ if (!win->floating && !win->transient) { - store_float_geom(win, r); + store_float_geom(win); ewmh_update_win_state(win, ewmh[_NET_WM_STATE_ABOVE].atom, _NET_WM_STATE_ADD); + stack(); } - ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom, - _NET_WM_STATE_ADD); - - stack(); focus_flush(); @@ -5590,9 +5726,10 @@ move(struct ws_win *win, union arg *args) break; } if (move_stp) { - constrain_window(win, r, 0); + regionize(win, -1, -1); + region_containment(win, win->ws->r, SWM_CW_ALLSIDES); update_window(win); - store_float_geom(win, r); + store_float_geom(win); return; } @@ -5608,6 +5745,10 @@ move(struct ws_win *win, union arg *args) return; } + regionize(win, qpr->root_x, qpr->root_y); + region_containment(win, win->ws->r, SWM_CW_ALLSIDES | + SWM_CW_SOFTBOUNDARY); + update_window(win); xcb_flush(conn); moving = 1; while (moving && (evt = xcb_wait_for_event(conn))) { @@ -5618,14 +5759,16 @@ move(struct ws_win *win, union arg *args) break; case XCB_MOTION_NOTIFY: mne = (xcb_motion_notify_event_t *)evt; + DNPRINTF(SWM_D_EVENT, "motion: root: 0x%x\n", mne->root); X(win) = mne->root_x - qpr->win_x - border_width; Y(win) = mne->root_y - qpr->win_y - border_width; - constrain_window(win, r, 0); - /* not free, don't sync more than 120 times / second */ if ((mne->time - timestamp) > (1000 / 120) ) { timestamp = mne->time; + regionize(win, mne->root_x, mne->root_y); + region_containment(win, win->ws->r, + SWM_CW_ALLSIDES | SWM_CW_SOFTBOUNDARY); update_window(win); xcb_flush(conn); } @@ -5643,10 +5786,12 @@ move(struct ws_win *win, union arg *args) free(evt); } if (timestamp) { + region_containment(win, win->ws->r, SWM_CW_ALLSIDES | + SWM_CW_SOFTBOUNDARY); update_window(win); xcb_flush(conn); } - store_float_geom(win, r); + store_float_geom(win); out: free(qpr); xcb_ungrab_pointer(conn, XCB_CURRENT_TIME); @@ -6802,6 +6947,7 @@ enum { SWM_S_BAR_FORMAT, SWM_S_BAR_JUSTIFY, SWM_S_BORDER_WIDTH, + SWM_S_BOUNDARY_WIDTH, SWM_S_CLOCK_ENABLED, SWM_S_CLOCK_FORMAT, SWM_S_CYCLE_EMPTY, @@ -6911,6 +7057,11 @@ setconfvalue(char *selector, char *value, int flags) if (border_width < 0) border_width = 0; break; + case SWM_S_BOUNDARY_WIDTH: + boundary_width = atoi(value); + if (boundary_width < 0) + boundary_width = 0; + break; case SWM_S_CLOCK_ENABLED: clock_enabled = atoi(value); break; @@ -7261,6 +7412,7 @@ struct config_option configopt[] = { { "bar_justify", setconfvalue, SWM_S_BAR_JUSTIFY }, { "bind", setconfbinding, 0 }, { "border_width", setconfvalue, SWM_S_BORDER_WIDTH }, + { "boundary_width", setconfvalue, SWM_S_BOUNDARY_WIDTH }, { "clock_enabled", setconfvalue, SWM_S_CLOCK_ENABLED }, { "clock_format", setconfvalue, SWM_S_CLOCK_FORMAT }, { "color_focus", setconfcolor, SWM_S_COLOR_FOCUS }, @@ -7635,8 +7787,6 @@ manage_window(xcb_window_t id, uint16_t mapped) win->ewmh_flags = 0; win->s = r->s; /* this never changes */ - store_float_geom(win, r); - /* Get WM_SIZE_HINTS. */ xcb_icccm_get_wm_normal_hints_reply(conn, xcb_icccm_get_wm_normal_hints(conn, win->id), @@ -7698,6 +7848,9 @@ manage_window(xcb_window_t id, uint16_t mapped) ws_idx_str); } + /* WS must already be set for this to work. */ + store_float_geom(win); + /* Handle EWMH */ ewmh_autoquirk(win); @@ -7741,7 +7894,8 @@ manage_window(xcb_window_t id, uint16_t mapped) /* Make sure window is positioned inside its region, if its active. */ if (win->ws->r) { - constrain_window(win, win->ws->r, 0); + region_containment(win, r, SWM_CW_ALLSIDES | + SWM_CW_HARDBOUNDARY); update_window(win); }