From 02bbb4a94a4a780ce4feee1743e0442e7c242029 Mon Sep 17 00:00:00 2001 From: nerdopolis Date: Sat, 10 Jan 2026 16:15:10 -0500 Subject: [PATCH] Add cage clone mode The main window will be restricted to the smallest dimensions of all possible screens --- cage.1.scd | 3 +++ cage.c | 6 +++++- output.c | 19 +++++++++++++++++-- seat.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++--- seat.h | 3 +++ server.h | 1 + view.c | 24 ++++++++++++++++++++---- xdg_shell.c | 7 ++++++- 8 files changed, 101 insertions(+), 11 deletions(-) diff --git a/cage.1.scd b/cage.1.scd index ed1f518..4d36806 100644 --- a/cage.1.scd +++ b/cage.1.scd @@ -16,6 +16,9 @@ activities outside the scope of the running application are prevented. # OPTIONS +*-c* + Enable clone mode for all attached screens + *-d* Don't draw client side decorations when possible. diff --git a/cage.c b/cage.c index 7c3d89d..2b4dd93 100644 --- a/cage.c +++ b/cage.c @@ -241,6 +241,7 @@ usage(FILE *file, const char *cage) fprintf(file, "Usage: %s [OPTIONS] [--] [APPLICATION...]\n" "\n" + " -c\t Enable clone mode for all attached screens\n" " -d\t Don't draw client side decorations, when possible\n" " -D\t Enable debug logging\n" " -h\t Display this help message\n" @@ -257,8 +258,11 @@ static bool parse_args(struct cg_server *server, int argc, char *argv[]) { int c; - while ((c = getopt(argc, argv, "dDhm:sv")) != -1) { + while ((c = getopt(argc, argv, "cdDhm:sv")) != -1) { switch (c) { + case 'c': + server->clone_mode = true; + break; case 'd': server->xdg_decoration = true; break; diff --git a/output.c b/output.c index 9f2a708..89aa0be 100644 --- a/output.c +++ b/output.c @@ -66,6 +66,13 @@ update_output_manager_config(struct cg_server *server) } wlr_output_manager_v1_set_configuration(server->output_manager_v1, config); + + if (server->clone_mode) { + struct wlr_box confine_box; + seat_get_clone_confines(server, &confine_box); + server->seat->clone_confine_box = confine_box; + } + } static inline void @@ -110,7 +117,11 @@ output_enable(struct cg_output *output) wlr_output_state_set_enabled(&state, true); if (wlr_output_commit_state(wlr_output, &state)) { - output_layout_add_auto(output); + if (output->server->clone_mode) { + output_layout_add(output, 0, 0); + } else { + output_layout_add_auto(output); + } } update_output_manager_config(output->server); @@ -313,7 +324,11 @@ handle_new_output(struct wl_listener *listener, void *data) wlr_log(WLR_DEBUG, "Enabling new output %s", wlr_output->name); if (wlr_output_commit_state(wlr_output, &state)) { - output_layout_add_auto(output); + if (output->server->clone_mode) { + output_layout_add(output, 0, 0); + } else { + output_layout_add_auto(output); + } } view_position_all(output->server); diff --git a/seat.c b/seat.c index 5f659a4..c245c32 100644 --- a/seat.c +++ b/seat.c @@ -626,6 +626,7 @@ process_cursor_motion(struct cg_seat *seat, uint32_t time_msec, double dx, doubl struct wlr_surface *surface = NULL; struct cg_view *view = desktop_view_at(seat->server, seat->cursor->x, seat->cursor->y, &surface, &sx, &sy); + if (!view) { wlr_seat_pointer_clear_focus(wlr_seat); } else { @@ -669,9 +670,23 @@ handle_cursor_motion_relative(struct wl_listener *listener, void *data) { struct cg_seat *seat = wl_container_of(listener, seat, cursor_motion_relative); struct wlr_pointer_motion_event *event = data; + double delta_x = event->delta_x; + double delta_y = event->delta_y; - wlr_cursor_move(seat->cursor, &event->pointer->base, event->delta_x, event->delta_y); - process_cursor_motion(seat, event->time_msec, event->delta_x, event->delta_y, event->unaccel_dx, + if (seat->server->clone_mode) { + struct wlr_box confine_box = seat->clone_confine_box; + + if (seat->cursor->x + delta_x >= confine_box.width) { + delta_x = confine_box.width - seat->cursor->x; + } + + if (seat->cursor->y + delta_y >= confine_box.height) { + delta_y = confine_box.height - seat->cursor->y; + } + } + + wlr_cursor_move(seat->cursor, &event->pointer->base, delta_x, delta_y); + process_cursor_motion(seat, event->time_msec, delta_x, delta_y, event->unaccel_dx, event->unaccel_dy); wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat); } @@ -978,6 +993,34 @@ seat_center_cursor(struct cg_seat *seat) { /* Place the cursor in the center of the output layout. */ struct wlr_box layout_box; - wlr_output_layout_get_box(seat->server->output_layout, NULL, &layout_box); + if (seat->server->clone_mode) { + layout_box = seat->clone_confine_box; + } else { + wlr_output_layout_get_box(seat->server->output_layout, NULL, &layout_box); + } wlr_cursor_warp(seat->cursor, NULL, layout_box.width / 2, layout_box.height / 2); } + +void +seat_get_clone_confines(struct cg_server *server, struct wlr_box *confine_box) +{ + int width = 0; + int height = 0; + struct cg_output *output; + + /* Get the smallest dimensions out of all the screens */ + wl_list_for_each (output, &server->outputs, link) { + if (width == 0 || width > output->wlr_output->width) { + width = output->wlr_output->width; + } + + if (height == 0 || height > output->wlr_output->height) { + height = output->wlr_output->height; + } + } + + confine_box->x = 0; + confine_box->y = 0; + confine_box->width = width; + confine_box->height = height; +} diff --git a/seat.h b/seat.h index 4b7bfda..58b50c6 100644 --- a/seat.h +++ b/seat.h @@ -48,6 +48,8 @@ struct cg_seat { struct wl_listener request_set_cursor; struct wl_listener request_set_selection; struct wl_listener request_set_primary_selection; + + struct wlr_box clone_confine_box; }; struct cg_keyboard_group { @@ -92,5 +94,6 @@ void seat_destroy(struct cg_seat *seat); struct cg_view *seat_get_focus(struct cg_seat *seat); void seat_set_focus(struct cg_seat *seat, struct cg_view *view); void seat_center_cursor(struct cg_seat *seat); +void seat_get_clone_confines(struct cg_server *server, struct wlr_box *confine_box); #endif diff --git a/server.h b/server.h index 27f212a..5ef77df 100644 --- a/server.h +++ b/server.h @@ -70,6 +70,7 @@ struct cg_server { struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager; + bool clone_mode; bool xdg_decoration; bool allow_vt_switch; bool return_app_code; diff --git a/view.c b/view.c index 94fd75e..0a42355 100644 --- a/view.c +++ b/view.c @@ -60,7 +60,12 @@ view_extends_output_layout(struct cg_view *view, struct wlr_box *layout_box) int width, height; view->impl->get_geometry(view, &width, &height); - return (layout_box->height < height || layout_box->width < width); + if (view->server->clone_mode) { + struct wlr_box confine_box = view->server->seat->clone_confine_box; + return (confine_box.height < height || confine_box.width < width); + } else { + return (layout_box->height < height || layout_box->width < width); + } } static void @@ -73,7 +78,12 @@ view_maximize(struct cg_view *view, struct wlr_box *layout_box) wlr_scene_node_set_position(&view->scene_tree->node, view->lx, view->ly); } - view->impl->maximize(view, layout_box->width, layout_box->height); + if (view->server->clone_mode) { + struct wlr_box confine_box = view->server->seat->clone_confine_box; + view->impl->maximize(view, confine_box.width, confine_box.height); + } else { + view->impl->maximize(view, layout_box->width, layout_box->height); + } } static void @@ -82,8 +92,14 @@ view_center(struct cg_view *view, struct wlr_box *layout_box) int width, height; view->impl->get_geometry(view, &width, &height); - view->lx = (layout_box->width - width) / 2; - view->ly = (layout_box->height - height) / 2; + if (view->server->clone_mode) { + struct wlr_box confine_box = view->server->seat->clone_confine_box; + view->lx = (confine_box.width - width) / 2; + view->ly = (confine_box.height - height) / 2; + } else { + view->lx = (layout_box->width - width) / 2; + view->ly = (layout_box->height - height) / 2; + } if (view->scene_tree) { wlr_scene_node_set_position(&view->scene_tree->node, view->lx, view->ly); diff --git a/xdg_shell.c b/xdg_shell.c index 4da19a0..da6b551 100644 --- a/xdg_shell.c +++ b/xdg_shell.c @@ -15,6 +15,7 @@ #include #include +#include "seat.h" #include "server.h" #include "view.h" #include "xdg_shell.h" @@ -100,7 +101,11 @@ popup_unconstrain(struct wlr_xdg_popup *popup) struct wlr_output *wlr_output = wlr_output_layout_output_at(output_layout, view->lx + popup_box->x, view->ly + popup_box->y); struct wlr_box output_box; - wlr_output_layout_get_box(output_layout, wlr_output, &output_box); + if (server->clone_mode) { + output_box = server->seat->clone_confine_box; + } else { + wlr_output_layout_get_box(output_layout, wlr_output, &output_box); + } struct wlr_box output_toplevel_box = { .x = output_box.x - view->lx,