From 063d2ad2d58d0989e79a5f6c79b96fdce0b54297 Mon Sep 17 00:00:00 2001 From: Mars-Wave <57585293+Mars-Wave@users.noreply.github.com> Date: Thu, 23 Oct 2025 20:39:29 +0200 Subject: [PATCH 1/2] Snap outputs to the compositor's logical size --- src/main.c | 61 ++++++++++++++++++++++++++----------------------- src/outputs.c | 12 +++++++++- src/wdisplays.h | 1 + 3 files changed, 45 insertions(+), 29 deletions(-) diff --git a/src/main.c b/src/main.c index 13fbb7a..8b3f198 100644 --- a/src/main.c +++ b/src/main.c @@ -251,6 +251,28 @@ static inline void color_to_float_array(GtkStyleContext *ctx, #define SWAP(_type, _a, _b) { _type _tmp = (_a); (_a) = (_b); (_b) = _tmp; } +static void get_logical_size(WdHeadForm *form, const WdHeadDimensions *dim, + double *w, double *h) { + const struct wd_head *head = g_object_get_data(G_OBJECT(form), "head"); + int32_t mode_w = head->mode != NULL ? head->mode->width : head->custom_mode.width; + int32_t mode_h = head->mode != NULL ? head->mode->height : head->custom_mode.height; + if (head->logical_width > 0 && dim->w == mode_w && dim->h == mode_h + && round(dim->scale * 100.) == round(head->scale * 100.) + && (dim->rotation_id & 1) == (head->transform & 1)) { + *w = head->logical_width; + *h = head->logical_height; + return; + } + double scale = dim->scale > 0. ? dim->scale : 1.; + /* same as wlroots: wl_fixed scale snapped to 1/120, float math, truncated */ + float fixed_scale = round(wl_fixed_to_double(wl_fixed_from_double(scale)) * 120.) / 120.; + *w = (int) ((float) dim->w / fixed_scale); + *h = (int) ((float) dim->h / fixed_scale); + if (dim->rotation_id & 1) { + SWAP(double, *w, *h); + } +} + static void queue_canvas_draw(struct wd_state *state) { GtkStyleContext *style_ctx = gtk_widget_get_style_context(state->canvas); color_to_float_array(style_ctx, @@ -270,11 +292,9 @@ static void queue_canvas_draw(struct wd_state *state) { if (wd_head_form_get_enabled(form)) { WdHeadDimensions dim; wd_head_form_get_dimensions(form, &dim); - double w = dim.w; - double h = dim.h; - double scale = dim.scale; - if (scale <= 0.) - scale = 1.; + double w; + double h; + get_logical_size(form, &dim, &w, &h); struct wd_head *head = g_object_get_data(G_OBJECT(form_iter->data), "head"); if (head->render == NULL) { @@ -283,14 +303,11 @@ static void queue_canvas_draw(struct wd_state *state) { } struct wd_render_head_data *render = head->render; render->queued.rotation = dim.rotation_id; - if (render->queued.rotation & 1) { - SWAP(int, w, h); - } render->queued.x_invert = dim.flipped; render->x1 = floor(dim.x * state->zoom - state->render.scroll_x - state->render.x_origin); render->y1 = floor(dim.y * state->zoom - state->render.scroll_y - state->render.y_origin); - render->x2 = floor(render->x1 + w * state->zoom / scale); - render->y2 = floor(render->y1 + h * state->zoom / scale); + render->x2 = floor(render->x1 + w * state->zoom); + render->y2 = floor(render->y1 + h * state->zoom); } } gtk_gl_area_queue_render(GTK_GL_AREA(state->canvas)); @@ -666,14 +683,8 @@ static void canvas_drag1_update(GtkGestureDrag *drag, return; WdHeadDimensions dim; wd_head_form_get_dimensions(form, &dim); - struct wd_point size = { .x = dim.w, .y = dim.h }; - if (dim.scale > 0.) { - size.x /= dim.scale; - size.y /= dim.scale; - } - if (dim.rotation_id & 1) { - SWAP(int, size.x, size.y); - } + struct wd_point size; + get_logical_size(form, &dim, &size.x, &size.y); struct wd_point tl = { /* top left */ .x = (state->drag_start.x + delta_x - state->head_drag_start.x * size.x * state->zoom) / state->zoom, .y = (state->drag_start.y + delta_y - state->head_drag_start.y * size.y * state->zoom) / state->zoom @@ -698,15 +709,9 @@ static void canvas_drag1_update(GtkGestureDrag *drag, wd_head_form_get_dimensions(other_form, &other_dim); double x1 = other_dim.x; double y1 = other_dim.y; - double w = other_dim.w; - double h = other_dim.h; - if (other_dim.scale > 0.) { - w /= other_dim.scale; - h /= other_dim.scale; - } - if (other_dim.rotation_id & 1) { - SWAP(int, w, h); - } + double w; + double h; + get_logical_size(other_form, &other_dim, &w, &h); double x2 = x1 + w; double y2 = y1 + h; if (fabs(br.x) <= snap) @@ -736,7 +741,7 @@ static void canvas_drag1_update(GtkGestureDrag *drag, new_pos.y = y2; } } - wd_head_form_set_position(form, new_pos.x, new_pos.y); + wd_head_form_set_position(form, round(new_pos.x), round(new_pos.y)); } static void canvas_drag1_end(GtkGestureDrag *drag, diff --git a/src/outputs.c b/src/outputs.c index 024a686..7a646a3 100644 --- a/src/outputs.c +++ b/src/outputs.c @@ -588,6 +588,16 @@ static void output_logical_position(void *data, struct zxdg_output_v1 *zxdg_outp } } +static void output_logical_size(void *data, struct zxdg_output_v1 *zxdg_output_v1, + int32_t width, int32_t height) { + struct wd_output *output = data; + struct wd_head *head = wd_find_head(output->state, output); + if (head != NULL) { + head->logical_width = width; + head->logical_height = height; + } +} + static void output_name(void *data, struct zxdg_output_v1 *zxdg_output_v1, const char *name) { struct wd_output *output = data; @@ -603,7 +613,7 @@ static void output_name(void *data, struct zxdg_output_v1 *zxdg_output_v1, static const struct zxdg_output_v1_listener output_listener = { .logical_position = output_logical_position, - .logical_size = (void (*)(void *, struct zxdg_output_v1 *, int32_t, int32_t))noop, + .logical_size = output_logical_size, .done = (void (*)(void *, struct zxdg_output_v1 *))noop, .name = output_name, .description = (void (*)(void *, struct zxdg_output_v1 *, const char *))noop diff --git a/src/wdisplays.h b/src/wdisplays.h index 4824017..92d8691 100644 --- a/src/wdisplays.h +++ b/src/wdisplays.h @@ -117,6 +117,7 @@ struct wd_head { int32_t x, y; enum wl_output_transform transform; double scale; + int32_t logical_width, logical_height; }; struct wd_gl_data; From ca0559dd613b23883752012e36c8ec923ce043bf Mon Sep 17 00:00:00 2001 From: Mars-Wave <57585293+Mars-Wave@users.noreply.github.com> Date: Sat, 25 Oct 2025 18:05:11 +0200 Subject: [PATCH 2/2] Size the canvas by logical size, redraw on change --- src/main.c | 59 +++++++++++++++++++++++++-------------------------- src/outputs.c | 1 + 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/main.c b/src/main.c index 8b3f198..04b253a 100644 --- a/src/main.c +++ b/src/main.c @@ -105,6 +105,32 @@ static void update_scroll_size(struct wd_state *state) { gtk_adjustment_set_value(scroll_y_adj, MIN(y, scroll_y_upper)); } +#define SWAP(_type, _a, _b) { _type _tmp = (_a); (_a) = (_b); (_b) = _tmp; } + +static void get_logical_size(WdHeadForm *form, const WdHeadDimensions *dim, + double *w, double *h) { + const struct wd_head *head = g_object_get_data(G_OBJECT(form), "head"); + const struct wd_mode *mode = head->mode; + int32_t mode_w = mode != NULL ? mode->width : head->custom_mode.width; + int32_t mode_h = mode != NULL ? mode->height : head->custom_mode.height; + if (head->logical_width > 0 && dim->w == mode_w && dim->h == mode_h + && round(dim->scale * 100.) == round(head->scale * 100.) + && (dim->rotation_id & 1) == (head->transform & 1)) { + *w = head->logical_width; + *h = head->logical_height; + return; + } + double scale = dim->scale > 0. ? dim->scale : 1.; + scale = wl_fixed_to_double(wl_fixed_from_double(scale)); + /* as sway does: snap to 1/120, divide in float, truncate */ + float wlr_scale = round(scale * 120.) / 120.; + *w = (int) ((float) dim->w / wlr_scale); + *h = (int) ((float) dim->h / wlr_scale); + if (dim->rotation_id & 1) { + SWAP(double, *w, *h); + } +} + /* * Recalculates the desired canvas size, accounting for zoom + margins. */ @@ -120,12 +146,9 @@ static void update_canvas_size(struct wd_state *state) { if (wd_head_form_get_enabled(form)) { WdHeadDimensions dim; wd_head_form_get_dimensions(form, &dim); - int h = dim.h; - int w = dim.w; - if (dim.scale > 0.) { - w /= dim.scale; - h /= dim.scale; - } + double w; + double h; + get_logical_size(form, &dim, &w, &h); int x2 = dim.x + w; int y2 = dim.y + h; xmin = MIN(xmin, dim.x); @@ -249,30 +272,6 @@ static inline void color_to_float_array(GtkStyleContext *ctx, out[3] = color.alpha; } -#define SWAP(_type, _a, _b) { _type _tmp = (_a); (_a) = (_b); (_b) = _tmp; } - -static void get_logical_size(WdHeadForm *form, const WdHeadDimensions *dim, - double *w, double *h) { - const struct wd_head *head = g_object_get_data(G_OBJECT(form), "head"); - int32_t mode_w = head->mode != NULL ? head->mode->width : head->custom_mode.width; - int32_t mode_h = head->mode != NULL ? head->mode->height : head->custom_mode.height; - if (head->logical_width > 0 && dim->w == mode_w && dim->h == mode_h - && round(dim->scale * 100.) == round(head->scale * 100.) - && (dim->rotation_id & 1) == (head->transform & 1)) { - *w = head->logical_width; - *h = head->logical_height; - return; - } - double scale = dim->scale > 0. ? dim->scale : 1.; - /* same as wlroots: wl_fixed scale snapped to 1/120, float math, truncated */ - float fixed_scale = round(wl_fixed_to_double(wl_fixed_from_double(scale)) * 120.) / 120.; - *w = (int) ((float) dim->w / fixed_scale); - *h = (int) ((float) dim->h / fixed_scale); - if (dim->rotation_id & 1) { - SWAP(double, *w, *h); - } -} - static void queue_canvas_draw(struct wd_state *state) { GtkStyleContext *style_ctx = gtk_widget_get_style_context(state->canvas); color_to_float_array(style_ctx, diff --git a/src/outputs.c b/src/outputs.c index 7a646a3..9a8b403 100644 --- a/src/outputs.c +++ b/src/outputs.c @@ -595,6 +595,7 @@ static void output_logical_size(void *data, struct zxdg_output_v1 *zxdg_output_v if (head != NULL) { head->logical_width = width; head->logical_height = height; + wd_ui_reset_head(head, 0); } }