2018-12-31 00:12:33 +01:00
|
|
|
/*
|
|
|
|
|
* Cage: A Wayland kiosk.
|
|
|
|
|
*
|
2019-01-02 20:58:39 +01:00
|
|
|
* Copyright (C) 2018-2019 Jente Hidskes
|
2018-12-31 00:12:33 +01:00
|
|
|
*
|
|
|
|
|
* See the LICENSE file accompanying this file.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#define _POSIX_C_SOURCE 200112L
|
|
|
|
|
|
2018-12-31 17:24:21 +01:00
|
|
|
#include "config.h"
|
|
|
|
|
|
2018-12-31 00:12:33 +01:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <wayland-server.h>
|
|
|
|
|
#include <wlr/backend.h>
|
|
|
|
|
#include <wlr/render/wlr_renderer.h>
|
|
|
|
|
#include <wlr/types/wlr_matrix.h>
|
|
|
|
|
#include <wlr/types/wlr_output.h>
|
|
|
|
|
#include <wlr/types/wlr_output_layout.h>
|
|
|
|
|
#include <wlr/types/wlr_surface.h>
|
|
|
|
|
#include <wlr/types/wlr_xdg_shell.h>
|
|
|
|
|
#include <wlr/util/log.h>
|
|
|
|
|
|
|
|
|
|
#include "output.h"
|
|
|
|
|
#include "server.h"
|
|
|
|
|
#include "view.h"
|
|
|
|
|
|
2019-01-04 18:33:59 +01:00
|
|
|
static void
|
|
|
|
|
render_overlay(struct wlr_renderer *renderer, struct wlr_output *output, int width, int height)
|
|
|
|
|
{
|
|
|
|
|
struct wlr_box box = { .width = width, .height = height };
|
|
|
|
|
float color[4] = { 0.0, 0.0, 0.0, 0.3 };
|
|
|
|
|
|
|
|
|
|
wlr_render_rect(renderer, &box, color, output->transform_matrix);
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-31 00:12:33 +01:00
|
|
|
/* Used to move all of the data necessary to render a surface from the
|
|
|
|
|
* top-level frame handler to the per-surface render function. */
|
|
|
|
|
struct render_data {
|
|
|
|
|
struct wlr_output_layout *output_layout;
|
|
|
|
|
struct wlr_output *output;
|
|
|
|
|
struct cg_view *view;
|
|
|
|
|
struct timespec *when;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
render_surface(struct wlr_surface *surface, int sx, int sy, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct render_data *rdata = data;
|
|
|
|
|
struct cg_view *view = rdata->view;
|
|
|
|
|
struct wlr_output *output = rdata->output;
|
|
|
|
|
|
|
|
|
|
if (!wlr_surface_has_buffer(surface)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct wlr_texture *texture = wlr_surface_get_texture(surface);
|
|
|
|
|
if (!texture) {
|
|
|
|
|
wlr_log(WLR_DEBUG, "Cannot obtain surface texture");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double ox = 0, oy = 0;
|
|
|
|
|
wlr_output_layout_output_coords(rdata->output_layout, output, &ox, &oy);
|
|
|
|
|
ox += view->x + sx, oy += view->y + sy;
|
|
|
|
|
|
|
|
|
|
struct wlr_box box = {
|
|
|
|
|
.x = ox * output->scale,
|
|
|
|
|
.y = oy * output->scale,
|
|
|
|
|
.width = surface->current.width * output->scale,
|
|
|
|
|
.height = surface->current.height * output->scale,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
float matrix[9];
|
|
|
|
|
enum wl_output_transform transform = wlr_output_transform_invert(surface->current.transform);
|
|
|
|
|
wlr_matrix_project_box(matrix, &box, transform, 0, output->transform_matrix);
|
|
|
|
|
wlr_render_texture_with_matrix(surface->renderer, texture, matrix, 1);
|
|
|
|
|
wlr_surface_send_frame_done(surface, rdata->when);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
handle_output_frame(struct wl_listener *listener, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct cg_output *output = wl_container_of(listener, output, frame);
|
|
|
|
|
struct wlr_renderer *renderer = wlr_backend_get_renderer(output->server->backend);
|
|
|
|
|
|
|
|
|
|
if (!wlr_output_make_current(output->wlr_output, NULL)) {
|
|
|
|
|
wlr_log(WLR_DEBUG, "Cannot make damage output current");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct timespec now;
|
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
|
|
|
|
|
|
|
|
int width, height;
|
|
|
|
|
wlr_output_effective_resolution(output->wlr_output, &width, &height);
|
|
|
|
|
|
|
|
|
|
wlr_renderer_begin(renderer, width, height);
|
|
|
|
|
|
|
|
|
|
float color[4] = {0.3, 0.3, 0.3, 1.0};
|
|
|
|
|
wlr_renderer_clear(renderer, color);
|
|
|
|
|
|
|
|
|
|
struct render_data rdata = {
|
|
|
|
|
.output_layout = output->server->output_layout,
|
|
|
|
|
.output = output->wlr_output,
|
|
|
|
|
.when = &now,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct cg_view *view;
|
|
|
|
|
wl_list_for_each_reverse(view, &output->server->views, link) {
|
|
|
|
|
rdata.view = view;
|
2019-01-02 21:23:16 +01:00
|
|
|
view_for_each_surface(view, render_surface, &rdata);
|
2019-01-04 18:33:59 +01:00
|
|
|
/* If we have dialogs open and this view is not the
|
|
|
|
|
top of the stack, draw an overlay. */
|
|
|
|
|
if (have_dialogs_open(output->server) &&
|
|
|
|
|
view->link.prev != &output->server->views) {
|
|
|
|
|
render_overlay(renderer, output->wlr_output, width, height);
|
|
|
|
|
}
|
2018-12-31 00:12:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wlr_renderer_end(renderer);
|
|
|
|
|
wlr_output_swap_buffers(output->wlr_output, NULL, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-10 15:50:31 +01:00
|
|
|
static void
|
|
|
|
|
handle_output_mode(struct wl_listener *listener, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct cg_output *output = wl_container_of(listener, output, mode);
|
|
|
|
|
|
|
|
|
|
struct cg_view *view;
|
|
|
|
|
wl_list_for_each(view, &output->server->views, link) {
|
|
|
|
|
view_position(view);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-31 00:12:33 +01:00
|
|
|
static void
|
|
|
|
|
handle_output_destroy(struct wl_listener *listener, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct cg_output *output = wl_container_of(listener, output, destroy);
|
|
|
|
|
struct cg_server *server = output->server;
|
|
|
|
|
|
|
|
|
|
wl_list_remove(&output->destroy.link);
|
|
|
|
|
wl_list_remove(&output->frame.link);
|
|
|
|
|
free(output);
|
|
|
|
|
server->output = NULL;
|
|
|
|
|
|
|
|
|
|
/* Since there is no use in continuing without our (single)
|
|
|
|
|
* output, terminate. */
|
|
|
|
|
wl_display_terminate(server->wl_display);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
handle_new_output(struct wl_listener *listener, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct cg_server *server = wl_container_of(listener, server, new_output);
|
|
|
|
|
struct wlr_output *wlr_output = data;
|
|
|
|
|
|
|
|
|
|
/* On outputs that have modes, we need to set one before we
|
|
|
|
|
* can use it. Each monitor supports only a specific set of
|
|
|
|
|
* modes. We just pick the last, in the future we could pick
|
|
|
|
|
* the mode the display advertises as preferred. */
|
|
|
|
|
if (!wl_list_empty(&wlr_output->modes)) {
|
|
|
|
|
struct wlr_output_mode *mode = wl_container_of(wlr_output->modes.prev, mode, link);
|
|
|
|
|
wlr_output_set_mode(wlr_output, mode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
server->output = calloc(1, sizeof(struct cg_output));
|
|
|
|
|
server->output->wlr_output = wlr_output;
|
|
|
|
|
server->output->server = server;
|
|
|
|
|
|
|
|
|
|
server->output->frame.notify = handle_output_frame;
|
|
|
|
|
wl_signal_add(&wlr_output->events.frame, &server->output->frame);
|
2019-01-10 15:50:31 +01:00
|
|
|
server->output->mode.notify = handle_output_mode;
|
|
|
|
|
wl_signal_add(&wlr_output->events.mode, &server->output->mode);
|
2018-12-31 00:12:33 +01:00
|
|
|
server->output->destroy.notify = handle_output_destroy;
|
|
|
|
|
wl_signal_add(&wlr_output->events.destroy, &server->output->destroy);
|
|
|
|
|
|
|
|
|
|
wlr_output_layout_add_auto(server->output_layout, wlr_output);
|
|
|
|
|
|
|
|
|
|
/* Disconnect the signal now, because we only use one static output. */
|
|
|
|
|
wl_list_remove(&server->new_output.link);
|
2018-12-31 00:18:37 +01:00
|
|
|
|
|
|
|
|
if (wlr_xcursor_manager_load(server->seat->xcursor_manager, wlr_output->scale)) {
|
|
|
|
|
wlr_log(WLR_ERROR, "Cannot load XCursor theme for output '%s' with scale %f",
|
|
|
|
|
wlr_output->name,
|
|
|
|
|
wlr_output->scale);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Place the cursor in the center of the screen. */
|
|
|
|
|
wlr_cursor_warp(server->seat->cursor, NULL, wlr_output->width / 2, wlr_output->height / 2);
|
2018-12-31 00:12:33 +01:00
|
|
|
}
|