Move magnifier code to separate module

This commit is contained in:
Simon Long 2024-05-15 11:33:20 +01:00
parent 24e86f095c
commit 18819b1147
6 changed files with 328 additions and 304 deletions

View file

@ -20,12 +20,4 @@ struct wlr_scene_node *lab_wlr_scene_get_prev_node(struct wlr_scene_node *node);
/* A variant of wlr_scene_output_commit() that respects wlr_output->pending */
bool lab_wlr_scene_output_commit(struct wlr_scene_output *scene_output);
enum magnify_dir {
MAGNIFY_INCREASE,
MAGNIFY_DECREASE
};
void magnify_toggle(void);
void magnify_set_scale(enum magnify_dir dir);
#endif /* LABWC_SCENE_HELPERS_H */

23
include/magnifier.h Normal file
View file

@ -0,0 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef LABWC_MAGNIFIER_H
#define LABWC_MAGNIFIER_H
#include <stdbool.h>
#include <wayland-server-core.h>
struct server;
struct output;
enum magnify_dir {
MAGNIFY_INCREASE,
MAGNIFY_DECREASE
};
void magnify_toggle(struct server *server);
void magnify_set_scale(struct server *server, enum magnify_dir dir);
bool output_wants_magnification(struct output *output);
void magnify(struct output *output, struct wlr_buffer *output_buffer,
struct wlr_box *damage);
bool is_magnify_on(void);
#endif /* LABWC_MAGNIFIER_H */

View file

@ -13,9 +13,9 @@
#include "common/parse-bool.h"
#include "common/spawn.h"
#include "common/string-helpers.h"
#include "common/scene-helpers.h"
#include "debug.h"
#include "labwc.h"
#include "magnifier.h"
#include "menu/menu.h"
#include "osd.h"
#include "output-virtual.h"
@ -1054,13 +1054,13 @@ actions_run(struct view *activator, struct server *server,
}
break;
case ACTION_TYPE_TOGGLE_MAGNIFY:
magnify_toggle();
magnify_toggle(server);
break;
case ACTION_TYPE_ZOOM_IN:
magnify_set_scale(MAGNIFY_INCREASE);
magnify_set_scale(server, MAGNIFY_INCREASE);
break;
case ACTION_TYPE_ZOOM_OUT:
magnify_set_scale(MAGNIFY_DECREASE);
magnify_set_scale(server, MAGNIFY_DECREASE);
break;
case ACTION_TYPE_INVALID:
wlr_log(WLR_ERROR, "Not executing unknown action");

View file

@ -5,16 +5,7 @@
#include <wlr/types/wlr_scene.h>
#include <wlr/util/log.h>
#include "common/scene-helpers.h"
#include "labwc.h"
#include "theme.h"
#include <wlr/render/pass.h>
#include <wlr/types/wlr_buffer.h>
#include <wlr/render/drm_format_set.h>
#include "common/macros.h"
static bool magnify_on;
static double mag_scale = 0.0, mag_increment = 0.0;
#include "magnifier.h"
struct wlr_surface *
lab_wlr_surface_from_node(struct wlr_scene_node *node)
@ -44,285 +35,6 @@ lab_wlr_scene_get_prev_node(struct wlr_scene_node *node)
return prev;
}
static double constrain(double lower, double in, double upper)
{
if (in < lower) {
return lower;
}
if (in > upper) {
return upper;
}
return in;
}
static void
magnify(struct output *output, struct wlr_buffer *output_buffer, struct wlr_box *damage)
{
int width, height;
double x, y;
struct wlr_box border_box, dst_box;
struct wlr_fbox src_box;
bool fullscreen = false;
/* Reuse a single scratch buffer */
static struct wlr_buffer *tmp_buffer = NULL;
static struct wlr_texture *tmp_texture = NULL;
/* TODO: This looks way too complicated to just get the used format */
struct wlr_drm_format wlr_drm_format = {0};
struct wlr_shm_attributes shm_attribs = {0};
struct wlr_dmabuf_attributes dma_attribs = {0};
if (wlr_buffer_get_dmabuf(output_buffer, &dma_attribs)) {
wlr_drm_format.format = dma_attribs.format;
wlr_drm_format.len = 1;
wlr_drm_format.modifiers = &dma_attribs.modifier;
} else if (wlr_buffer_get_shm(output_buffer, &shm_attribs)) {
wlr_drm_format.format = shm_attribs.format;
} else {
wlr_log(WLR_ERROR, "Failed to read buffer format");
return;
}
/* Fetch scale-adjusted cursor coordinates */
struct server *server = output->server;
struct theme *theme = server->theme;
struct wlr_cursor *cursor = server->seat.cursor;
double ox = cursor->x;
double oy = cursor->y;
wlr_output_layout_output_coords(server->output_layout, output->wlr_output, &ox, &oy);
ox *= output->wlr_output->scale;
oy *= output->wlr_output->scale;
if (theme->mag_width == -1 || theme->mag_height == -1) {
fullscreen = true;
}
if ((ox < 0 || oy < 0 || ox >= output_buffer->width || oy >= output_buffer->height)
&& fullscreen) {
return;
}
if (mag_scale == 0.0) {
mag_scale = theme->mag_scale;
}
if (mag_increment == 0.0) {
mag_increment = theme->mag_increment;
}
if (fullscreen) {
// The lines below were the first attempt at enabling fullscreen (with no
// other changes required). They appeared to work with a 4K monitor set to
// 1080p, but when the monitor was set to native 4K, they resulted in a
// corrupt magnifier. Keeping here for now in case they are useful later...
//int width = output_buffer->width * 2 + 1;
//int height = output_buffer->height * 2 + 1;
//double x = ox - ((width - 1) / 2.0);
//double y = oy - ((height - 1) / 2.0);
width = output_buffer->width;
height = output_buffer->height;
x = 0;
y = 0;
} else {
width = theme->mag_width + 1;
height = theme->mag_height + 1;
x = ox - (theme->mag_width / 2.0);
y = oy - (theme->mag_height / 2.0);
}
double cropped_width = width;
double cropped_height = height;
double dst_x = 0;
double dst_y = 0;
/* Ensure everything is kept within output boundaries */
if (x < 0) {
cropped_width += x;
dst_x = x * -1;
x = 0;
}
if (y < 0) {
cropped_height += y;
dst_y = y * -1;
y = 0;
}
cropped_width = MIN(cropped_width, (double)output_buffer->width - x);
cropped_height = MIN(cropped_height, (double)output_buffer->height - y);
/* (Re)create the temporary buffer if required */
if (tmp_buffer && (tmp_buffer->width != width || tmp_buffer->height != height)) {
wlr_log(WLR_ERROR, "tmp buffer size changed, dropping");
assert(tmp_texture);
wlr_texture_destroy(tmp_texture);
wlr_buffer_drop(tmp_buffer);
tmp_buffer = NULL;
tmp_texture = NULL;
}
if (!tmp_buffer) {
tmp_buffer = wlr_allocator_create_buffer(
server->allocator, width, height, &wlr_drm_format);
}
if (!tmp_buffer) {
wlr_log(WLR_ERROR, "Failed to allocate temporary magnifier buffer");
return;
}
/* Extract source region into temporary buffer */
struct wlr_render_pass *tmp_render_pass = wlr_renderer_begin_buffer_pass(
server->renderer, tmp_buffer, NULL);
/* FIXME, try to re-use the existing output texture instead */
wlr_buffer_lock(output_buffer);
struct wlr_texture *output_texture = wlr_texture_from_buffer(
server->renderer, output_buffer);
assert(output_texture);
struct wlr_render_texture_options opts = {
.texture = output_texture,
.src_box = (struct wlr_fbox) {
x, y, cropped_width, cropped_height },
.dst_box = (struct wlr_box) {
dst_x, dst_y, cropped_width, cropped_height },
.alpha = NULL,
};
wlr_render_pass_add_texture(tmp_render_pass, &opts);
if (!wlr_render_pass_submit(tmp_render_pass)) {
wlr_log(WLR_ERROR, "Failed to extract magnifier source region");
wlr_texture_destroy(output_texture);
goto cleanup;
}
wlr_texture_destroy(output_texture);
/* Render to the output buffer itself */
tmp_render_pass = wlr_renderer_begin_buffer_pass(
server->renderer, output_buffer, NULL);
/* Borders */
if (fullscreen) {
border_box.x = 0;
border_box.y = 0;
border_box.width = width;
border_box.height = height;
} else {
border_box.x = ox - (width / 2 + theme->mag_border_width);
border_box.y = oy - (height / 2 + theme->mag_border_width);
border_box.width = (width + theme->mag_border_width * 2);
border_box.height = (height + theme->mag_border_width * 2);
struct wlr_render_rect_options bg_opts = {
.box = border_box,
.color = (struct wlr_render_color) {
.r = theme->mag_border_color[0],
.g = theme->mag_border_color[1],
.b = theme->mag_border_color[2],
.a = theme->mag_border_color[3]
},
.clip = NULL,
};
wlr_render_pass_add_rect(tmp_render_pass, &bg_opts);
}
/* Paste the magnified result back into the output buffer */
if (!tmp_texture) {
tmp_texture = wlr_texture_from_buffer(server->renderer, tmp_buffer);
assert(tmp_texture);
}
src_box.width = width / mag_scale;
src_box.height = height / mag_scale;
dst_box.width = width;
dst_box.height = height;
if (fullscreen) {
src_box.x = constrain(0.0, ox - (ox / mag_scale),
width * (mag_scale - 1.0) / mag_scale);
src_box.y = constrain(0.0, oy - (oy / mag_scale),
height * (mag_scale - 1.0) / mag_scale);
dst_box.x = 0;
dst_box.y = 0;
} else {
src_box.x = width * (mag_scale - 1.0) / (2.0 * mag_scale);
src_box.y = height * (mag_scale - 1.0) / (2.0 * mag_scale);
dst_box.x = ox - (width / 2);
dst_box.y = oy - (height / 2);
}
opts = (struct wlr_render_texture_options) {
.texture = tmp_texture,
.src_box = src_box,
.dst_box = dst_box,
.alpha = NULL,
.clip = NULL,
.filter_mode = theme->mag_filter ? WLR_SCALE_FILTER_BILINEAR
: WLR_SCALE_FILTER_NEAREST,
};
wlr_render_pass_add_texture(tmp_render_pass, &opts);
if (!wlr_render_pass_submit(tmp_render_pass)) {
wlr_log(WLR_ERROR, "Failed to submit render pass");
goto cleanup;
}
/* And finally mark the extra damage */
*damage = border_box;
damage->width += 1;
damage->height += 1;
cleanup:
wlr_buffer_unlock(output_buffer);
}
static bool
output_wants_magnification(struct output *output)
{
static double x = -1;
static double y = -1;
struct wlr_cursor *cursor = output->server->seat.cursor;
if (!magnify_on) {
x = -1;
y = -1;
return false;
}
if (cursor->x == x && cursor->y == y) {
return false;
}
x = cursor->x;
y = cursor->y;
return output_nearest_to_cursor(output->server) == output;
}
/*
* Toggles magnification on and off
*/
void
magnify_toggle(void)
{
if (magnify_on) {
magnify_on = false;
} else {
magnify_on = true;
}
}
/*
* Increases and decreases magnification scale
*/
void
magnify_set_scale(enum magnify_dir dir)
{
if (dir == MAGNIFY_INCREASE) {
if (magnify_on) {
mag_scale += mag_increment;
} else {
magnify_on = true;
mag_scale = 1.0 + mag_increment;
}
} else {
if (magnify_on && mag_scale > 1.0 + mag_increment) {
mag_scale -= mag_increment;
} else {
magnify_on = false;
}
}
}
/*
* This is a copy of wlr_scene_output_commit()
* as it doesn't use the pending state at all.
@ -339,11 +51,11 @@ lab_wlr_scene_output_commit(struct wlr_scene_output *scene_output)
if (!wlr_output->needs_frame && !pixman_region32_not_empty(
&scene_output->damage_ring.current) && !wants_magnification
&& last_mag != magnify_on) {
&& last_mag != is_magnify_on()) {
return false;
}
last_mag = magnify_on;
last_mag = is_magnify_on();
if (!wlr_scene_output_build_state(scene_output, state, NULL)) {
wlr_log(WLR_ERROR, "Failed to build output state for %s",
@ -352,7 +64,7 @@ lab_wlr_scene_output_commit(struct wlr_scene_output *scene_output)
}
struct wlr_box additional_damage = {0};
if (state->buffer && magnify_on) {
if (state->buffer && is_magnify_on()) {
magnify(output, state->buffer, &additional_damage);
}

296
src/magnifier.c Normal file
View file

@ -0,0 +1,296 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <assert.h>
#include <wlr/types/wlr_output.h>
#include "magnifier.h"
#include "labwc.h"
#include "theme.h"
#include "common/macros.h"
bool magnify_on;
double mag_scale = 0.0, mag_increment = 0.0;
#define CLAMP(in, lower, upper) MAX(MIN(in, upper), lower)
void
magnify(struct output *output, struct wlr_buffer *output_buffer, struct wlr_box *damage)
{
int width, height;
double x, y;
struct wlr_box border_box, dst_box;
struct wlr_fbox src_box;
bool fullscreen = false;
/* Reuse a single scratch buffer */
static struct wlr_buffer *tmp_buffer = NULL;
static struct wlr_texture *tmp_texture = NULL;
/* TODO: This looks way too complicated to just get the used format */
struct wlr_drm_format wlr_drm_format = {0};
struct wlr_shm_attributes shm_attribs = {0};
struct wlr_dmabuf_attributes dma_attribs = {0};
if (wlr_buffer_get_dmabuf(output_buffer, &dma_attribs)) {
wlr_drm_format.format = dma_attribs.format;
wlr_drm_format.len = 1;
wlr_drm_format.modifiers = &dma_attribs.modifier;
} else if (wlr_buffer_get_shm(output_buffer, &shm_attribs)) {
wlr_drm_format.format = shm_attribs.format;
} else {
wlr_log(WLR_ERROR, "Failed to read buffer format");
return;
}
/* Fetch scale-adjusted cursor coordinates */
struct server *server = output->server;
struct theme *theme = server->theme;
struct wlr_cursor *cursor = server->seat.cursor;
double ox = cursor->x;
double oy = cursor->y;
wlr_output_layout_output_coords(server->output_layout, output->wlr_output, &ox, &oy);
ox *= output->wlr_output->scale;
oy *= output->wlr_output->scale;
if (theme->mag_width == -1 || theme->mag_height == -1) {
fullscreen = true;
}
if ((ox < 0 || oy < 0 || ox >= output_buffer->width || oy >= output_buffer->height)
&& fullscreen) {
return;
}
if (mag_scale == 0.0) {
mag_scale = theme->mag_scale;
}
if (mag_increment == 0.0) {
mag_increment = theme->mag_increment;
}
if (fullscreen) {
width = output_buffer->width;
height = output_buffer->height;
x = 0;
y = 0;
} else {
width = theme->mag_width + 1;
height = theme->mag_height + 1;
x = ox - (theme->mag_width / 2.0);
y = oy - (theme->mag_height / 2.0);
}
double cropped_width = width;
double cropped_height = height;
double dst_x = 0;
double dst_y = 0;
/* Ensure everything is kept within output boundaries */
if (x < 0) {
cropped_width += x;
dst_x = x * -1;
x = 0;
}
if (y < 0) {
cropped_height += y;
dst_y = y * -1;
y = 0;
}
cropped_width = MIN(cropped_width, (double)output_buffer->width - x);
cropped_height = MIN(cropped_height, (double)output_buffer->height - y);
/* (Re)create the temporary buffer if required */
if (tmp_buffer && (tmp_buffer->width != width || tmp_buffer->height != height)) {
wlr_log(WLR_ERROR, "tmp buffer size changed, dropping");
assert(tmp_texture);
wlr_texture_destroy(tmp_texture);
wlr_buffer_drop(tmp_buffer);
tmp_buffer = NULL;
tmp_texture = NULL;
}
if (!tmp_buffer) {
tmp_buffer = wlr_allocator_create_buffer(
server->allocator, width, height, &wlr_drm_format);
}
if (!tmp_buffer) {
wlr_log(WLR_ERROR, "Failed to allocate temporary magnifier buffer");
return;
}
/* Extract source region into temporary buffer */
struct wlr_render_pass *tmp_render_pass = wlr_renderer_begin_buffer_pass(
server->renderer, tmp_buffer, NULL);
/* FIXME, try to re-use the existing output texture instead */
wlr_buffer_lock(output_buffer);
struct wlr_texture *output_texture = wlr_texture_from_buffer(
server->renderer, output_buffer);
assert(output_texture);
struct wlr_render_texture_options opts = {
.texture = output_texture,
.src_box = (struct wlr_fbox) {
x, y, cropped_width, cropped_height },
.dst_box = (struct wlr_box) {
dst_x, dst_y, cropped_width, cropped_height },
.alpha = NULL,
};
wlr_render_pass_add_texture(tmp_render_pass, &opts);
if (!wlr_render_pass_submit(tmp_render_pass)) {
wlr_log(WLR_ERROR, "Failed to extract magnifier source region");
wlr_texture_destroy(output_texture);
goto cleanup;
}
wlr_texture_destroy(output_texture);
/* Render to the output buffer itself */
tmp_render_pass = wlr_renderer_begin_buffer_pass(
server->renderer, output_buffer, NULL);
/* Borders */
if (fullscreen) {
border_box.x = 0;
border_box.y = 0;
border_box.width = width;
border_box.height = height;
} else {
border_box.x = ox - (width / 2 + theme->mag_border_width);
border_box.y = oy - (height / 2 + theme->mag_border_width);
border_box.width = (width + theme->mag_border_width * 2);
border_box.height = (height + theme->mag_border_width * 2);
struct wlr_render_rect_options bg_opts = {
.box = border_box,
.color = (struct wlr_render_color) {
.r = theme->mag_border_color[0],
.g = theme->mag_border_color[1],
.b = theme->mag_border_color[2],
.a = theme->mag_border_color[3]
},
.clip = NULL,
};
wlr_render_pass_add_rect(tmp_render_pass, &bg_opts);
}
/* Paste the magnified result back into the output buffer */
if (!tmp_texture) {
tmp_texture = wlr_texture_from_buffer(server->renderer, tmp_buffer);
assert(tmp_texture);
}
src_box.width = width / mag_scale;
src_box.height = height / mag_scale;
dst_box.width = width;
dst_box.height = height;
if (fullscreen) {
src_box.x = CLAMP(ox - (ox / mag_scale), 0.0,
width * (mag_scale - 1.0) / mag_scale);
src_box.y = CLAMP(oy - (oy / mag_scale), 0.0,
height * (mag_scale - 1.0) / mag_scale);
dst_box.x = 0;
dst_box.y = 0;
} else {
src_box.x = width * (mag_scale - 1.0) / (2.0 * mag_scale);
src_box.y = height * (mag_scale - 1.0) / (2.0 * mag_scale);
dst_box.x = ox - (width / 2);
dst_box.y = oy - (height / 2);
}
opts = (struct wlr_render_texture_options) {
.texture = tmp_texture,
.src_box = src_box,
.dst_box = dst_box,
.alpha = NULL,
.clip = NULL,
.filter_mode = theme->mag_filter ? WLR_SCALE_FILTER_BILINEAR
: WLR_SCALE_FILTER_NEAREST,
};
wlr_render_pass_add_texture(tmp_render_pass, &opts);
if (!wlr_render_pass_submit(tmp_render_pass)) {
wlr_log(WLR_ERROR, "Failed to submit render pass");
goto cleanup;
}
/* And finally mark the extra damage */
*damage = border_box;
damage->width += 1;
damage->height += 1;
cleanup:
wlr_buffer_unlock(output_buffer);
}
bool
output_wants_magnification(struct output *output)
{
static double x = -1;
static double y = -1;
struct wlr_cursor *cursor = output->server->seat.cursor;
if (!magnify_on) {
x = -1;
y = -1;
return false;
}
if (cursor->x == x && cursor->y == y) {
return false;
}
x = cursor->x;
y = cursor->y;
return output_nearest_to_cursor(output->server) == output;
}
/*
* Toggles magnification on and off
*/
void
magnify_toggle(struct server *server)
{
struct output *output = output_nearest_to_cursor(server);
if (magnify_on) {
magnify_on = false;
} else {
magnify_on = true;
}
if (output) {
wlr_output_schedule_frame(output->wlr_output);
}
}
/*
* Increases and decreases magnification scale
*/
void
magnify_set_scale(struct server *server, enum magnify_dir dir)
{
struct output *output = output_nearest_to_cursor(server);
if (dir == MAGNIFY_INCREASE) {
if (magnify_on) {
mag_scale += mag_increment;
} else {
magnify_on = true;
mag_scale = 1.0 + mag_increment;
}
} else {
if (magnify_on && mag_scale > 1.0 + mag_increment) {
mag_scale -= mag_increment;
} else {
magnify_on = false;
}
}
if (output) {
wlr_output_schedule_frame(output->wlr_output);
}
}
/*
* Report whether magnification is enabled
*/
bool
is_magnify_on(void)
{
return magnify_on;
}

View file

@ -9,6 +9,7 @@ labwc_sources = files(
'idle.c',
'interactive.c',
'layers.c',
'magnifier.c',
'main.c',
'node.c',
'osd.c',