wlroots/examples/fullscreen-shell.c
Simon Ser 4b03bdc3ab Remove wlr_create_renderer_func_t
This callback allowed compositors to customize the EGL config used by
the renderer. However with renderer v6 EGL configs aren't used anymore.
Instead, buffers are allocated via GBM and GL FBOs are rendered to. So
customizing the EGL config is a no-op.
2020-12-30 17:09:40 +01:00

249 lines
6.7 KiB
C

#define _POSIX_C_SOURCE 200112L
#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <wayland-server-core.h>
#include <wlr/backend.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_fullscreen_shell_v1.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/util/log.h>
/**
* A minimal fullscreen-shell server. It only supports rendering.
*/
struct fullscreen_server {
struct wl_display *wl_display;
struct wlr_backend *backend;
struct wlr_renderer *renderer;
struct wlr_fullscreen_shell_v1 *fullscreen_shell;
struct wl_listener present_surface;
struct wlr_output_layout *output_layout;
struct wl_list outputs;
struct wl_listener new_output;
};
struct fullscreen_output {
struct wl_list link;
struct fullscreen_server *server;
struct wlr_output *wlr_output;
struct wlr_surface *surface;
struct wl_listener surface_destroy;
struct wl_listener frame;
};
struct render_data {
struct wlr_output *output;
struct wlr_renderer *renderer;
struct tinywl_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 wlr_output *output = rdata->output;
struct wlr_texture *texture = wlr_surface_get_texture(surface);
if (texture == NULL) {
return;
}
struct wlr_box box = {
.x = sx * output->scale,
.y = sy * 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(rdata->renderer, texture, matrix, 1);
wlr_surface_send_frame_done(surface, rdata->when);
}
static void output_handle_frame(struct wl_listener *listener, void *data) {
struct fullscreen_output *output =
wl_container_of(listener, output, frame);
struct wlr_renderer *renderer = output->server->renderer;
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
int width, height;
wlr_output_effective_resolution(output->wlr_output, &width, &height);
if (!wlr_output_attach_render(output->wlr_output, NULL)) {
return;
}
wlr_renderer_begin(renderer, width, height);
float color[4] = {0.3, 0.3, 0.3, 1.0};
wlr_renderer_clear(renderer, color);
if (output->surface != NULL) {
struct render_data rdata = {
.output = output->wlr_output,
.renderer = renderer,
.when = &now,
};
wlr_surface_for_each_surface(output->surface, render_surface, &rdata);
}
wlr_renderer_end(renderer);
wlr_output_commit(output->wlr_output);
}
static void output_set_surface(struct fullscreen_output *output,
struct wlr_surface *surface);
static void output_handle_surface_destroy(struct wl_listener *listener,
void *data) {
struct fullscreen_output *output =
wl_container_of(listener, output, surface_destroy);
output_set_surface(output, NULL);
}
static void output_set_surface(struct fullscreen_output *output,
struct wlr_surface *surface) {
if (output->surface == surface) {
return;
}
if (output->surface != NULL) {
wl_list_remove(&output->surface_destroy.link);
output->surface = NULL;
}
if (surface != NULL) {
output->surface_destroy.notify = output_handle_surface_destroy;
wl_signal_add(&surface->events.destroy, &output->surface_destroy);
output->surface = surface;
}
wlr_log(WLR_DEBUG, "Presenting surface %p on output %s",
surface, output->wlr_output->name);
}
static void server_handle_new_output(struct wl_listener *listener, void *data) {
struct fullscreen_server *server =
wl_container_of(listener, server, new_output);
struct wlr_output *wlr_output = data;
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);
}
struct fullscreen_output *output =
calloc(1, sizeof(struct fullscreen_output));
output->wlr_output = wlr_output;
output->server = server;
output->frame.notify = output_handle_frame;
wl_signal_add(&wlr_output->events.frame, &output->frame);
wl_list_insert(&server->outputs, &output->link);
wlr_output_layout_add_auto(server->output_layout, wlr_output);
wlr_output_create_global(wlr_output);
wlr_output_commit(wlr_output);
}
static void server_handle_present_surface(struct wl_listener *listener,
void *data) {
struct fullscreen_server *server =
wl_container_of(listener, server, present_surface);
struct wlr_fullscreen_shell_v1_present_surface_event *event = data;
struct fullscreen_output *output;
wl_list_for_each(output, &server->outputs, link) {
if (event->output == NULL || event->output == output->wlr_output) {
output_set_surface(output, event->surface);
}
}
}
int main(int argc, char *argv[]) {
wlr_log_init(WLR_DEBUG, NULL);
char *startup_cmd = NULL;
int c;
while ((c = getopt(argc, argv, "s:")) != -1) {
switch (c) {
case 's':
startup_cmd = optarg;
break;
default:
printf("usage: %s [-s startup-command]\n", argv[0]);
return EXIT_FAILURE;
}
}
if (optind < argc) {
printf("usage: %s [-s startup-command]\n", argv[0]);
return EXIT_FAILURE;
}
struct fullscreen_server server = {0};
server.wl_display = wl_display_create();
server.backend = wlr_backend_autocreate(server.wl_display);
server.renderer = wlr_backend_get_renderer(server.backend);
wlr_renderer_init_wl_display(server.renderer, server.wl_display);
wlr_compositor_create(server.wl_display, server.renderer);
server.output_layout = wlr_output_layout_create();
wl_list_init(&server.outputs);
server.new_output.notify = server_handle_new_output;
wl_signal_add(&server.backend->events.new_output, &server.new_output);
server.fullscreen_shell = wlr_fullscreen_shell_v1_create(server.wl_display);
server.present_surface.notify = server_handle_present_surface;
wl_signal_add(&server.fullscreen_shell->events.present_surface,
&server.present_surface);
const char *socket = wl_display_add_socket_auto(server.wl_display);
if (!socket) {
wl_display_destroy(server.wl_display);
return EXIT_FAILURE;
}
if (!wlr_backend_start(server.backend)) {
wl_display_destroy(server.wl_display);
return EXIT_FAILURE;
}
setenv("WAYLAND_DISPLAY", socket, true);
if (startup_cmd != NULL) {
if (fork() == 0) {
execl("/bin/sh", "/bin/sh", "-c", startup_cmd, (void *)NULL);
}
}
wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s",
socket);
wl_display_run(server.wl_display);
wl_display_destroy_clients(server.wl_display);
wl_display_destroy(server.wl_display);
return 0;
}