Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions waybox/cursor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,11 @@ static void process_cursor_motion(struct wb_server *server, uint32_t time) {
wlr_cursor_set_xcursor(server->cursor->cursor,
server->cursor->xcursor_manager, "default");
}
/* The pointer is over our decoration, not the client surface: drop
* the client's pointer focus so that moving back inside the window
* re-sends a pointer enter, prompting the client to reassert its own
* cursor (otherwise it would keep the resize cursor we just set). */
wlr_seat_pointer_clear_focus(server->seat->seat);
wlr_idle_notifier_v1_notify_activity(server->idle_notifier,
server->seat->seat);
return;
Expand Down
39 changes: 36 additions & 3 deletions waybox/xdg_shell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,34 @@ void constrain_toplevel_to_usable_area(struct wb_toplevel *toplevel) {
toplevel->geometry.y = constrained.y + in.top;
}

/* Clamp a freshly-placed window so its *entire frame* (client plus the
* decoration insets) lies within `area` (a layout-coordinate rectangle, e.g.
* the usable area minus margins). Unlike constrain_toplevel_to_usable_area
* (which only reserves panel edges and lets a window be dragged partly
* off-screen), this keeps every border on-screen at map time, so a server-side
* frame's left border and titlebar are never clipped or pushed under a panel.
* Top-left takes priority when the frame is larger than the area. */
static void clamp_frame_into_area(struct wb_toplevel *toplevel,
const wb::Rect &area) {
wb::FrameInsets in = toplevel_insets(toplevel);
int fw = toplevel->geometry.width + in.left + in.right;
int fh = toplevel->geometry.height + in.top + in.bottom;
int fx = toplevel->geometry.x - in.left;
int fy = toplevel->geometry.y - in.top;

if (fx + fw > area.x + area.width)
fx = area.x + area.width - fw;
if (fy + fh > area.y + area.height)
fy = area.y + area.height - fh;
if (fx < area.x)
fx = area.x;
if (fy < area.y)
fy = area.y;

toplevel->geometry.x = fx + in.left;
toplevel->geometry.y = fy + in.top;
}

static void xdg_toplevel_map(struct wb_toplevel *toplevel, void *data) {
/* Called when the surface is mapped, or ready to display on-screen. */
if (toplevel->xdg_toplevel->base->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL)
Expand Down Expand Up @@ -521,12 +549,17 @@ static void xdg_toplevel_map(struct wb_toplevel *toplevel, void *data) {
if (do_focus)
focus_toplevel(toplevel);

position_toplevel(toplevel);
log_geometry(toplevel, "mapped");

/* Sync the server-side frame (no-op unless SSD is negotiated for it). */
update_toplevel_decoration(toplevel);

/* Now that the frame (and thus the insets) exist, pull the whole frame
* inside the placement area so a server-side titlebar/border is never
* clipped off-screen or placed under a panel's reserved zone. Log the
* final, on-screen position as "mapped" so restore geometry matches it. */
clamp_frame_into_area(toplevel, area);
position_toplevel(toplevel);
log_geometry(toplevel, "mapped");

/* Apply requested initial states after the base mapping. */
if (rule != nullptr) {
if (rule->decor.has_value()) {
Expand Down