wlroots-hyprland/backend/wayland/output.c
emersion 225d182765
output: always use hardware cursors if available
This changes the `wlr_output_impl.set_cursor` function to take a
`wlr_texture` instead of a byte buffer. This simplifies the
DRM and Wayland backends since they were creating textures from
the byte buffer anyway.

With this commit, performance should be improved when moving the
cursor since outputs don't need to be re-rendered anymore.
2018-05-01 21:38:04 +01:00

317 lines
9.5 KiB
C

#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <wayland-client.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/util/log.h>
#include "backend/wayland.h"
#include "util/signal.h"
#include "xdg-shell-unstable-v6-client-protocol.h"
int os_create_anonymous_file(off_t size);
static struct wl_callback_listener frame_listener;
static void surface_frame_callback(void *data, struct wl_callback *cb,
uint32_t time) {
struct wlr_wl_output *output = data;
assert(output);
wl_callback_destroy(cb);
output->frame_callback = NULL;
wlr_output_send_frame(&output->wlr_output);
}
static struct wl_callback_listener frame_listener = {
.done = surface_frame_callback
};
static bool output_set_custom_mode(struct wlr_output *_output,
int32_t width, int32_t height, int32_t refresh) {
struct wlr_wl_output *output = (struct wlr_wl_output *)_output;
wl_egl_window_resize(output->egl_window, width, height, 0, 0);
wlr_output_update_custom_mode(&output->wlr_output, width, height, 0);
return true;
}
static bool output_make_current(struct wlr_output *wlr_output,
int *buffer_age) {
struct wlr_wl_output *output =
(struct wlr_wl_output *)wlr_output;
return wlr_egl_make_current(&output->backend->egl, output->egl_surface,
buffer_age);
}
static bool output_swap_buffers(struct wlr_output *wlr_output,
pixman_region32_t *damage) {
struct wlr_wl_output *output =
(struct wlr_wl_output *)wlr_output;
if (output->frame_callback != NULL) {
wlr_log(L_ERROR, "Skipping buffer swap");
return false;
}
output->frame_callback = wl_surface_frame(output->surface);
wl_callback_add_listener(output->frame_callback, &frame_listener, output);
return wlr_egl_swap_buffers(&output->backend->egl, output->egl_surface,
damage);
}
static void output_transform(struct wlr_output *_output,
enum wl_output_transform transform) {
struct wlr_wl_output *output = (struct wlr_wl_output *)_output;
output->wlr_output.transform = transform;
}
static bool output_set_cursor(struct wlr_output *_output,
struct wlr_texture *texture, int32_t hotspot_x, int32_t hotspot_y,
bool update_texture) {
struct wlr_wl_output *output =
(struct wlr_wl_output *)_output;
struct wlr_wl_backend *backend = output->backend;
// TODO: use output->wlr_output.transform to transform pixels and hotpot
output->cursor.hotspot_x = hotspot_x;
output->cursor.hotspot_y = hotspot_y;
if (!update_texture) {
// Update hotspot without changing cursor image
update_wl_output_cursor(output);
return true;
}
if (output->cursor.surface == NULL) {
output->cursor.surface =
wl_compositor_create_surface(backend->compositor);
}
struct wl_surface *surface = output->cursor.surface;
if (texture != NULL) {
int width, height;
wlr_texture_get_size(texture, &width, &height);
if (output->cursor.egl_window == NULL) {
output->cursor.egl_window =
wl_egl_window_create(surface, width, height);
}
wl_egl_window_resize(output->cursor.egl_window, width, height, 0, 0);
EGLSurface egl_surface =
wlr_egl_create_surface(&backend->egl, output->cursor.egl_window);
wlr_egl_make_current(&backend->egl, egl_surface, NULL);
float matrix[9];
wlr_matrix_projection(matrix, width, height, WL_OUTPUT_TRANSFORM_NORMAL);
wlr_renderer_begin(backend->renderer, width, height);
wlr_renderer_clear(backend->renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 });
wlr_render_texture(backend->renderer, texture, matrix, 0, 0, 1.0);
wlr_renderer_end(backend->renderer);
wlr_egl_swap_buffers(&backend->egl, egl_surface, NULL);
wlr_egl_destroy_surface(&backend->egl, egl_surface);
} else {
wl_surface_attach(surface, NULL, 0, 0);
wl_surface_commit(surface);
}
update_wl_output_cursor(output);
return true;
}
static void output_destroy(struct wlr_output *wlr_output) {
struct wlr_wl_output *output =
(struct wlr_wl_output *)wlr_output;
if (output == NULL) {
return;
}
wl_list_remove(&output->link);
if (output->cursor.egl_window != NULL) {
wl_egl_window_destroy(output->cursor.egl_window);
}
if (output->cursor.surface) {
wl_surface_destroy(output->cursor.surface);
}
if (output->frame_callback) {
wl_callback_destroy(output->frame_callback);
}
wlr_egl_destroy_surface(&output->backend->egl, output->egl_surface);
wl_egl_window_destroy(output->egl_window);
zxdg_toplevel_v6_destroy(output->xdg_toplevel);
zxdg_surface_v6_destroy(output->xdg_surface);
wl_surface_destroy(output->surface);
free(output);
}
void update_wl_output_cursor(struct wlr_wl_output *output) {
if (output->backend->pointer && output->enter_serial) {
wl_pointer_set_cursor(output->backend->pointer, output->enter_serial,
output->cursor.surface, output->cursor.hotspot_x,
output->cursor.hotspot_y);
}
}
bool output_move_cursor(struct wlr_output *_output, int x, int y) {
// TODO: only return true if x == current x and y == current y
return true;
}
static const struct wlr_output_impl output_impl = {
.set_custom_mode = output_set_custom_mode,
.transform = output_transform,
.destroy = output_destroy,
.make_current = output_make_current,
.swap_buffers = output_swap_buffers,
.set_cursor = output_set_cursor,
.move_cursor = output_move_cursor,
};
bool wlr_output_is_wl(struct wlr_output *wlr_output) {
return wlr_output->impl == &output_impl;
}
static void xdg_surface_handle_configure(void *data, struct zxdg_surface_v6 *xdg_surface,
uint32_t serial) {
struct wlr_wl_output *output = data;
assert(output && output->xdg_surface == xdg_surface);
zxdg_surface_v6_ack_configure(xdg_surface, serial);
// nothing else?
}
static struct zxdg_surface_v6_listener xdg_surface_listener = {
.configure = xdg_surface_handle_configure,
};
static void xdg_toplevel_handle_configure(void *data, struct zxdg_toplevel_v6 *xdg_toplevel,
int32_t width, int32_t height, struct wl_array *states) {
struct wlr_wl_output *output = data;
assert(output && output->xdg_toplevel == xdg_toplevel);
if (width == 0 && height == 0) {
return;
}
// loop over states for maximized etc?
wl_egl_window_resize(output->egl_window, width, height, 0, 0);
wlr_output_update_custom_mode(&output->wlr_output, width, height, 0);
}
static void xdg_toplevel_handle_close(void *data, struct zxdg_toplevel_v6 *xdg_toplevel) {
struct wlr_wl_output *output = data;
assert(output && output->xdg_toplevel == xdg_toplevel);
wlr_output_destroy((struct wlr_output *)output);
}
static struct zxdg_toplevel_v6_listener xdg_toplevel_listener = {
.configure = xdg_toplevel_handle_configure,
.close = xdg_toplevel_handle_close,
};
struct wlr_output *wlr_wl_output_create(struct wlr_backend *_backend) {
assert(wlr_backend_is_wl(_backend));
struct wlr_wl_backend *backend = (struct wlr_wl_backend *)_backend;
if (!backend->started) {
++backend->requested_outputs;
return NULL;
}
struct wlr_wl_output *output;
if (!(output = calloc(sizeof(struct wlr_wl_output), 1))) {
wlr_log(L_ERROR, "Failed to allocate wlr_wl_output");
return NULL;
}
wlr_output_init(&output->wlr_output, &backend->backend, &output_impl,
backend->local_display);
struct wlr_output *wlr_output = &output->wlr_output;
wlr_output_update_custom_mode(wlr_output, 1280, 720, 0);
strncpy(wlr_output->make, "wayland", sizeof(wlr_output->make));
strncpy(wlr_output->model, "wayland", sizeof(wlr_output->model));
snprintf(wlr_output->name, sizeof(wlr_output->name), "WL-%d",
wl_list_length(&backend->outputs) + 1);
output->backend = backend;
output->surface = wl_compositor_create_surface(backend->compositor);
if (!output->surface) {
wlr_log_errno(L_ERROR, "Could not create output surface");
goto error;
}
wl_surface_set_user_data(output->surface, output);
output->xdg_surface =
zxdg_shell_v6_get_xdg_surface(backend->shell, output->surface);
if (!output->xdg_surface) {
wlr_log_errno(L_ERROR, "Could not get xdg surface");
goto error;
}
output->xdg_toplevel =
zxdg_surface_v6_get_toplevel(output->xdg_surface);
if (!output->xdg_toplevel) {
wlr_log_errno(L_ERROR, "Could not get xdg toplevel");
goto error;
}
zxdg_toplevel_v6_set_app_id(output->xdg_toplevel, "wlroots");
zxdg_toplevel_v6_set_title(output->xdg_toplevel, "wlroots");
zxdg_surface_v6_add_listener(output->xdg_surface,
&xdg_surface_listener, output);
zxdg_toplevel_v6_add_listener(output->xdg_toplevel,
&xdg_toplevel_listener, output);
wl_surface_commit(output->surface);
output->egl_window = wl_egl_window_create(output->surface,
wlr_output->width, wlr_output->height);
output->egl_surface = wlr_egl_create_surface(&backend->egl,
output->egl_window);
wl_display_roundtrip(output->backend->remote_display);
// start rendering loop per callbacks by rendering first frame
if (!wlr_egl_make_current(&output->backend->egl, output->egl_surface,
NULL)) {
goto error;
}
wlr_renderer_begin(backend->renderer, wlr_output->width, wlr_output->height);
wlr_renderer_clear(backend->renderer, (float[]){ 1.0, 1.0, 1.0, 1.0 });
wlr_renderer_end(backend->renderer);
output->frame_callback = wl_surface_frame(output->surface);
wl_callback_add_listener(output->frame_callback, &frame_listener, output);
if (!wlr_egl_swap_buffers(&output->backend->egl, output->egl_surface,
NULL)) {
goto error;
}
wl_list_insert(&backend->outputs, &output->link);
wlr_output_update_enabled(wlr_output, true);
wlr_signal_emit_safe(&backend->backend.events.new_output, wlr_output);
if (backend->pointer != NULL) {
create_wl_pointer(backend->pointer, output);
}
return wlr_output;
error:
wlr_output_destroy(&output->wlr_output);
return NULL;
}