aboutsummaryrefslogtreecommitdiff
path: root/examples/fullscreen-shell.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/fullscreen-shell.c')
-rw-r--r--examples/fullscreen-shell.c248
1 files changed, 248 insertions, 0 deletions
diff --git a/examples/fullscreen-shell.c b/examples/fullscreen-shell.c
new file mode 100644
index 00000000..7f2c9c2b
--- /dev/null
+++ b/examples/fullscreen-shell.c
@@ -0,0 +1,248 @@
+#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.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_linux_dmabuf_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_make_current(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_swap_buffers(output->wlr_output, NULL, NULL);
+}
+
+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);
+}
+
+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, NULL);
+ 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);
+ wlr_linux_dmabuf_v1_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;
+}