mirror of
https://github.com/labwc/labwc.git
synced 2025-10-29 05:40:24 -04:00
304 lines
8 KiB
C
304 lines
8 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
#include "magnifier.h"
|
|
#include <assert.h>
|
|
#include <wlr/render/allocator.h>
|
|
#include <wlr/render/swapchain.h>
|
|
#include <wlr/types/wlr_cursor.h>
|
|
#include <wlr/types/wlr_output.h>
|
|
#include <wlr/types/wlr_scene.h>
|
|
#include <wlr/util/transform.h>
|
|
#include "common/box.h"
|
|
#include "config/rcxml.h"
|
|
#include "labwc.h"
|
|
#include "output.h"
|
|
#include "theme.h"
|
|
|
|
static bool magnify_on;
|
|
static double mag_scale = 0.0;
|
|
|
|
/* Reuse a single scratch buffer */
|
|
static struct wlr_buffer *tmp_buffer = NULL;
|
|
static struct wlr_texture *tmp_texture = NULL;
|
|
|
|
static void
|
|
box_logical_to_physical(struct wlr_box *box, struct wlr_output *output)
|
|
{
|
|
box->x *= output->scale;
|
|
box->y *= output->scale;
|
|
box->width *= output->scale;
|
|
box->height *= output->scale;
|
|
|
|
int output_w = output->width;
|
|
int output_h = output->height;
|
|
wlr_output_transform_coords(output->transform,
|
|
&output_w, &output_h);
|
|
wlr_box_transform(box, box,
|
|
wlr_output_transform_invert(output->transform),
|
|
output_w, output_h);
|
|
}
|
|
|
|
void
|
|
magnifier_draw(struct output *output, struct wlr_buffer *output_buffer, struct wlr_box *damage)
|
|
{
|
|
struct server *server = output->server;
|
|
struct theme *theme = server->theme;
|
|
bool fullscreen = (rc.mag_width == -1 || rc.mag_height == -1);
|
|
|
|
struct wlr_box output_box = {
|
|
.width = output_buffer->width,
|
|
.height = output_buffer->height,
|
|
};
|
|
|
|
/* Cursor position in per-output logical coordinate */
|
|
double cursor_logical_x = server->seat.cursor->x;
|
|
double cursor_logical_y = server->seat.cursor->y;
|
|
wlr_output_layout_output_coords(server->output_layout,
|
|
output->wlr_output, &cursor_logical_x, &cursor_logical_y);
|
|
/* Cursor position in per-output physical coordinate */
|
|
struct wlr_box cursor_pos = {
|
|
.x = cursor_logical_x,
|
|
.y = cursor_logical_y,
|
|
};
|
|
box_logical_to_physical(&cursor_pos, output->wlr_output);
|
|
|
|
if (!wlr_box_contains_point(&output_box,
|
|
cursor_pos.x, cursor_pos.y)) {
|
|
return;
|
|
}
|
|
|
|
if (mag_scale == 0.0) {
|
|
mag_scale = rc.mag_scale;
|
|
}
|
|
assert(mag_scale >= 1.0);
|
|
|
|
/* Magnifier geometry in physical output coordinate */
|
|
struct wlr_box mag_box;
|
|
if (fullscreen) {
|
|
mag_box = output_box;
|
|
} else {
|
|
mag_box.x = cursor_logical_x - (rc.mag_width / 2.0);
|
|
mag_box.y = cursor_logical_y - (rc.mag_height / 2.0);
|
|
mag_box.width = rc.mag_width;
|
|
mag_box.height = rc.mag_height;
|
|
box_logical_to_physical(&mag_box, output->wlr_output);
|
|
}
|
|
|
|
/* (Re)create the temporary buffer if required */
|
|
if (tmp_buffer && (tmp_buffer->width != mag_box.width
|
|
|| tmp_buffer->height != mag_box.height)) {
|
|
wlr_log(WLR_DEBUG, "tmp magnifier 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, mag_box.width, mag_box.height,
|
|
&output->wlr_output->swapchain->format);
|
|
}
|
|
if (!tmp_buffer) {
|
|
wlr_log(WLR_ERROR, "Failed to allocate temporary magnifier buffer");
|
|
return;
|
|
}
|
|
|
|
if (!tmp_texture) {
|
|
tmp_texture = wlr_texture_from_buffer(server->renderer, tmp_buffer);
|
|
}
|
|
if (!tmp_texture) {
|
|
wlr_log(WLR_ERROR, "Failed to allocate temporary magnifier texture");
|
|
wlr_buffer_drop(tmp_buffer);
|
|
tmp_buffer = NULL;
|
|
return;
|
|
}
|
|
|
|
/* Extract source region into temporary buffer */
|
|
struct wlr_render_pass *tmp_render_pass = wlr_renderer_begin_buffer_pass(
|
|
server->renderer, tmp_buffer, NULL);
|
|
if (!tmp_render_pass) {
|
|
wlr_log(WLR_ERROR, "Failed to begin magnifier render pass");
|
|
return;
|
|
}
|
|
|
|
wlr_buffer_lock(output_buffer);
|
|
struct wlr_texture *output_texture = wlr_texture_from_buffer(
|
|
server->renderer, output_buffer);
|
|
if (!output_texture) {
|
|
goto cleanup;
|
|
}
|
|
|
|
struct wlr_box src_box_for_copy;
|
|
wlr_box_intersection(&src_box_for_copy, &mag_box, &output_box);
|
|
|
|
struct wlr_box dst_box_for_copy = src_box_for_copy;
|
|
dst_box_for_copy.x -= mag_box.x;
|
|
dst_box_for_copy.y -= mag_box.y;
|
|
|
|
struct wlr_render_texture_options opts = {
|
|
.texture = output_texture,
|
|
.src_box = box_to_fbox(&src_box_for_copy),
|
|
.dst_box = dst_box_for_copy,
|
|
};
|
|
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);
|
|
if (!tmp_render_pass) {
|
|
wlr_log(WLR_ERROR, "Failed to begin second magnifier render pass");
|
|
goto cleanup;
|
|
}
|
|
|
|
struct wlr_box damage_box;
|
|
if (fullscreen) {
|
|
damage_box = output_box;
|
|
} else {
|
|
/* Draw borders */
|
|
int border_width =
|
|
theme->mag_border_width * output->wlr_output->scale;
|
|
struct wlr_box border_box = {
|
|
.x = mag_box.x - border_width,
|
|
.y = mag_box.y - border_width,
|
|
.width = mag_box.width + border_width * 2,
|
|
.height = mag_box.height + 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);
|
|
wlr_box_intersection(&damage_box, &border_box, &output_box);
|
|
}
|
|
|
|
struct wlr_fbox src_box_for_paste = {
|
|
.width = mag_box.width / mag_scale,
|
|
.height = mag_box.height / mag_scale,
|
|
};
|
|
|
|
if (fullscreen) {
|
|
src_box_for_paste.x = cursor_pos.x - (cursor_pos.x / mag_scale);
|
|
src_box_for_paste.y = cursor_pos.y - (cursor_pos.y / mag_scale);
|
|
} else {
|
|
src_box_for_paste.x =
|
|
mag_box.width * (mag_scale - 1.0) / (2.0 * mag_scale);
|
|
src_box_for_paste.y =
|
|
mag_box.height * (mag_scale - 1.0) / (2.0 * mag_scale);
|
|
}
|
|
|
|
/* Paste the magnified result back into the output buffer */
|
|
opts = (struct wlr_render_texture_options) {
|
|
.texture = tmp_texture,
|
|
.src_box = src_box_for_paste,
|
|
.dst_box = mag_box,
|
|
.filter_mode = rc.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 magnifier render pass");
|
|
goto cleanup;
|
|
}
|
|
|
|
/* And finally mark the extra damage */
|
|
*damage = damage_box;
|
|
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;
|
|
}
|
|
|
|
static void
|
|
enable_magnifier(struct server *server, bool enable)
|
|
{
|
|
magnify_on = enable;
|
|
server->scene->WLR_PRIVATE.direct_scanout = enable ? false
|
|
: server->direct_scanout_enabled;
|
|
}
|
|
|
|
/* Toggles magnification on and off */
|
|
void
|
|
magnifier_toggle(struct server *server)
|
|
{
|
|
enable_magnifier(server, !magnify_on);
|
|
|
|
struct output *output = output_nearest_to_cursor(server);
|
|
if (output) {
|
|
wlr_output_schedule_frame(output->wlr_output);
|
|
}
|
|
}
|
|
|
|
/* Increases and decreases magnification scale */
|
|
void
|
|
magnifier_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 += rc.mag_increment;
|
|
} else {
|
|
enable_magnifier(server, true);
|
|
mag_scale = 1.0 + rc.mag_increment;
|
|
}
|
|
} else {
|
|
if (magnify_on && mag_scale > 1.0 + rc.mag_increment) {
|
|
mag_scale -= rc.mag_increment;
|
|
} else {
|
|
enable_magnifier(server, false);
|
|
}
|
|
}
|
|
|
|
if (output) {
|
|
wlr_output_schedule_frame(output->wlr_output);
|
|
}
|
|
}
|
|
|
|
/* Reset any buffers held by the magnifier */
|
|
void
|
|
magnifier_reset(void)
|
|
{
|
|
if (tmp_texture && tmp_buffer) {
|
|
wlr_texture_destroy(tmp_texture);
|
|
wlr_buffer_drop(tmp_buffer);
|
|
tmp_buffer = NULL;
|
|
tmp_texture = NULL;
|
|
}
|
|
}
|
|
|
|
/* Report whether magnification is enabled */
|
|
bool
|
|
magnifier_is_enabled(void)
|
|
{
|
|
return magnify_on;
|
|
}
|