void seat_reset_pressed(struct seat *seat);
void seat_output_layout_changed(struct seat *seat);
+/**
+ * interactive_anchor_to_cursor() - repositions the view to remain
+ * underneath the cursor when its size changes during interactive move.
+ *
+ * geometry->{width,height} are provided by the caller.
+ * geometry->{x,y} are computed by this function.
+ */
+void interactive_anchor_to_cursor(struct view *view, struct wlr_box *geometry);
+
void interactive_begin(struct view *view, enum input_mode mode, uint32_t edges);
void interactive_finish(struct view *view);
void interactive_cancel(struct view *view);
void view_minimize(struct view *view, bool minimized);
bool view_compute_centered_position(struct view *view,
const struct wlr_box *ref, int w, int h, int *x, int *y);
+void view_set_fallback_natural_geometry(struct view *view);
void view_store_natural_geometry(struct view *view);
/**
return pos_new;
}
+void
+interactive_anchor_to_cursor(struct view *view, struct wlr_box *geometry)
+{
+ struct wlr_cursor *cursor = view->server->seat.cursor;
+ geometry->x = max_move_scale(cursor->x, view->current.x,
+ view->current.width, geometry->width);
+ geometry->y = max_move_scale(cursor->y, view->current.y,
+ view->current.height, geometry->height);
+}
+
void
interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
{
* width/height.
* Don't reset tiled state yet since we may want
* to keep it (in the snap-to-maximize case).
+ *
+ * If the natural geometry is unknown (possible
+ * with xdg-shell views), then we set a size of
+ * 0x0 here and determine the correct geometry
+ * later. See do_late_positioning() in xdg.c.
*/
- geometry = view->natural_geometry;
- geometry.x = max_move_scale(seat->cursor->x,
- view->current.x, view->current.width,
- geometry.width);
- geometry.y = max_move_scale(seat->cursor->y,
- view->current.y, view->current.height,
- geometry.height);
+ geometry.width = view->natural_geometry.width;
+ geometry.height = view->natural_geometry.height;
+ if (!wlr_box_empty(&geometry)) {
+ interactive_anchor_to_cursor(view, &geometry);
+ }
view_set_shade(view, false);
view_set_untiled(view);
&geometry->x, &geometry->y);
}
-static void
-set_fallback_geometry(struct view *view)
+void
+view_set_fallback_natural_geometry(struct view *view)
{
view->natural_geometry.width = LAB_FALLBACK_WIDTH;
view->natural_geometry.height = LAB_FALLBACK_HEIGHT;
return;
}
- /**
- * If an application was started maximized or fullscreened, its
- * natural_geometry width/height may still be zero (or very small
- * values) in which case we set some fallback values. This is the case
- * with foot and some Qt/Tk applications.
+ /*
+ * Note that for xdg-shell views that start fullscreen or maximized,
+ * we end up storing a natural geometry of 0x0. This is intentional.
+ * When leaving fullscreen or unmaximizing, we pass 0x0 to the
+ * xdg-toplevel configure event, which means the application should
+ * choose its own size.
*/
- if (view->pending.width < LAB_MIN_VIEW_WIDTH
- || view->pending.height < LAB_MIN_VIEW_HEIGHT) {
- set_fallback_geometry(view);
- } else {
- view->natural_geometry = view->pending;
- }
+ view->natural_geometry = view->pending;
}
int
assert(view_is_floating(view));
struct wlr_box geometry = view->natural_geometry;
- adjust_floating_geometry(view, &geometry,
- /* midpoint_visibility */ false);
+ /* Only adjust natural geometry if known (not 0x0) */
+ if (!wlr_box_empty(&geometry)) {
+ adjust_floating_geometry(view, &geometry,
+ /* midpoint_visibility */ false);
+ }
view_move_resize(view, geometry);
}
view_invalidate_last_layout_geometry(view);
}
}
+
+ /*
+ * When natural geometry is unknown (0x0) for an xdg-shell view,
+ * we normally send a configure event of 0x0 to get the client's
+ * preferred size, but this doesn't work if unmaximizing only
+ * one axis. So in that corner case, set a fallback geometry.
+ */
+ if ((axis == VIEW_AXIS_HORIZONTAL || axis == VIEW_AXIS_VERTICAL)
+ && wlr_box_empty(&view->natural_geometry)) {
+ view_set_fallback_natural_geometry(view);
+ }
+
set_maximized(view, axis);
if (view_is_floating(view)) {
view_apply_natural_geometry(view);
xdg_popup_create(view, wlr_popup);
}
+static void
+do_late_positioning(struct view *view)
+{
+ struct server *server = view->server;
+ if (server->input_mode == LAB_INPUT_STATE_MOVE
+ && view == server->grabbed_view) {
+ /* Keep view underneath cursor */
+ interactive_anchor_to_cursor(view, &view->pending);
+ /* Update grab offsets */
+ server->grab_x = server->seat.cursor->x;
+ server->grab_y = server->seat.cursor->y;
+ server->grab_box = view->pending;
+ } else {
+ /* TODO: smart placement? */
+ view_compute_centered_position(view, NULL,
+ view->pending.width, view->pending.height,
+ &view->pending.x, &view->pending.y);
+ }
+}
+
static void
handle_commit(struct wl_listener *listener, void *data)
{
struct wlr_box size;
wlr_xdg_surface_get_geometry(xdg_surface, &size);
+ bool update_required = false;
+
+ /*
+ * If we didn't know the natural size when leaving fullscreen or
+ * unmaximizing, then the pending size will be 0x0. In this case,
+ * the pending x/y is also unset and we still need to position
+ * the window.
+ */
+ if (wlr_box_empty(&view->pending)) {
+ view->pending.width = size.width;
+ view->pending.height = size.height;
+ do_late_positioning(view);
+ update_required = true;
+ }
/*
* Qt applications occasionally fail to call set_window_geometry
}
struct wlr_box *current = &view->current;
- bool update_required = current->width != size.width
- || current->height != size.height;
+ if (current->width != size.width || current->height != size.height) {
+ update_required = true;
+ }
uint32_t serial = view->pending_configure_serial;
if (serial > 0 && serial == xdg_surface->current.configure_serial) {
xdg_toplevel_view_configure(struct view *view, struct wlr_box geo)
{
uint32_t serial = 0;
- view_adjust_size(view, &geo.width, &geo.height);
+
+ /*
+ * Leave a size of 0x0 unchanged; this has special meaning in
+ * an xdg-toplevel configure event and requests the application
+ * to choose its own preferred size.
+ */
+ if (!wlr_box_empty(&geo)) {
+ view_adjust_size(view, &geo.width, &geo.height);
+ }
/*
* We do not need to send a configure request unless the size
struct wlr_xdg_surface *xdg_surface = xdg_surface_from_view(view);
wlr_scene_node_set_enabled(&view->scene_tree->node, true);
if (!view->been_mapped) {
- struct wlr_xdg_toplevel_requested *requested =
- &xdg_toplevel_from_view(view)->requested;
-
init_foreign_toplevel(view);
+ /*
+ * FIXME: is this needed or is the earlier logic in
+ * xdg_surface_new() enough?
+ */
if (view_wants_decorations(view)) {
view_set_ssd_mode(view, LAB_SSD_MODE_FULL);
} else {
}
/*
- * Set initial "pending" dimensions (may be modified by
- * view_set_fullscreen/view_maximize() below). "Current"
+ * Set initial "pending" dimensions. "Current"
* dimensions remain zero until handle_commit().
*/
if (wlr_box_empty(&view->pending)) {
/*
* Set initial "pending" position for floating views.
- * Do this before view_set_fullscreen/view_maximize() so
- * that the position is saved with the natural geometry.
- *
- * FIXME: the natural geometry is not saved if either
- * handle_request_fullscreen/handle_request_maximize()
- * is called before map (try "foot --maximized").
*/
if (view_is_floating(view)) {
set_initial_position(view);
}
- set_fullscreen_from_request(view, requested);
- view_maximize(view, requested->maximized ?
- VIEW_AXIS_BOTH : VIEW_AXIS_NONE,
- /*store_natural_geometry*/ true);
-
/*
* Set initial "current" position directly before
* calling view_moved() to reduce flicker
CONNECT_SIGNAL(xdg_surface, xdg_toplevel_view, new_popup);
wl_list_insert(&server->views, &view->link);
+
+ /* FIXME: is view_wants_decorations() reliable this early? */
+ if (view_wants_decorations(view)) {
+ view_set_ssd_mode(view, LAB_SSD_MODE_FULL);
+ } else {
+ view_set_ssd_mode(view, LAB_SSD_MODE_NONE);
+ }
+
+ /*
+ * Handle initial fullscreen/maximize requests. This needs to be
+ * done early (before map) in order to send the correct size to
+ * the initial configure event and avoid flicker.
+ *
+ * Note that at this point, wlroots has already scheduled (but
+ * not yet sent) the initial configure event with a size of 0x0.
+ * In normal (non-fullscreen/maximized) cases, the zero size
+ * requests the application to choose its own size.
+ */
+ if (toplevel->requested.fullscreen) {
+ set_fullscreen_from_request(view, &toplevel->requested);
+ }
+ if (toplevel->requested.maximized) {
+ view_maximize(view, VIEW_AXIS_BOTH,
+ /*store_natural_geometry*/ true);
+ }
}
void
*/
}
+static void
+check_natural_geometry(struct view *view)
+{
+ /*
+ * Some applications (example: Thonny) don't set a reasonable
+ * un-maximized size when started maximized. Try to detect this
+ * and set a fallback size.
+ */
+ if (!view_is_floating(view)
+ && (view->natural_geometry.width < LAB_MIN_VIEW_WIDTH
+ || view->natural_geometry.height < LAB_MIN_VIEW_HEIGHT)) {
+ view_set_fallback_natural_geometry(view);
+ }
+}
+
static void
set_initial_position(struct view *view,
struct wlr_xwayland_surface *xwayland_surface)
}
if (!view->been_mapped) {
+ check_natural_geometry(view);
set_initial_position(view, xwayland_surface);
/*
* When mapping the view for the first time, visual