aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/fullscreen-shell.c248
-rw-r--r--examples/meson.build6
-rw-r--r--include/wlr/types/meson.build3
-rw-r--r--include/wlr/types/wlr_fullscreen_shell_v1.h41
-rw-r--r--protocol/meson.build13
-rw-r--r--types/meson.build3
-rw-r--r--types/wlr_fullscreen_shell_v1.c129
7 files changed, 434 insertions, 9 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;
+}
diff --git a/examples/meson.build b/examples/meson.build
index 0811d1a2..8d38b888 100644
--- a/examples/meson.build
+++ b/examples/meson.build
@@ -112,11 +112,15 @@ examples = {
'text-input': {
'src': 'text-input.c',
'dep': [wayland_cursor, wayland_client, wlr_protos, wlroots],
- },
+ },
'foreign-toplevel': {
'src': 'foreign-toplevel.c',
'dep': [wayland_client, wlr_protos, wlroots],
},
+ 'fullscreen-shell': {
+ 'src': 'fullscreen-shell.c',
+ 'dep': [wlr_protos, wlroots],
+ },
}
foreach name, info : examples
diff --git a/include/wlr/types/meson.build b/include/wlr/types/meson.build
index dfb55ec9..f9075bea 100644
--- a/include/wlr/types/meson.build
+++ b/include/wlr/types/meson.build
@@ -6,6 +6,7 @@ install_headers(
'wlr_data_device.h',
'wlr_export_dmabuf_v1.h',
'wlr_foreign_toplevel_management_v1.h',
+ 'wlr_fullscreen_shell_v1.h',
'wlr_gamma_control_v1.h',
'wlr_gamma_control.h',
'wlr_gtk_primary_selection.h',
@@ -22,8 +23,8 @@ install_headers(
'wlr_output_damage.h',
'wlr_output_layout.h',
'wlr_output.h',
- 'wlr_pointer.h',
'wlr_pointer_constraints_v1.h',
+ 'wlr_pointer.h',
'wlr_presentation_time.h',
'wlr_primary_selection.h',
'wlr_region.h',
diff --git a/include/wlr/types/wlr_fullscreen_shell_v1.h b/include/wlr/types/wlr_fullscreen_shell_v1.h
new file mode 100644
index 00000000..a904d966
--- /dev/null
+++ b/include/wlr/types/wlr_fullscreen_shell_v1.h
@@ -0,0 +1,41 @@
+/*
+ * This an unstable interface of wlroots. No guarantees are made regarding the
+ * future consistency of this API.
+ */
+#ifndef WLR_USE_UNSTABLE
+#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
+#endif
+
+#ifndef WLR_TYPES_WLR_FULLSCREEN_SHELL_V1_H
+#define WLR_TYPES_WLR_FULLSCREEN_SHELL_V1_H
+
+#include <wayland-server.h>
+#include "fullscreen-shell-unstable-v1-protocol.h"
+
+struct wlr_fullscreen_shell_v1 {
+ struct wl_global *global;
+ struct wl_list resources;
+
+ struct {
+ struct wl_signal destroy;
+ // wlr_fullscreen_shell_v1_present_surface_event
+ struct wl_signal present_surface;
+ } events;
+
+ struct wl_listener display_destroy;
+
+ void *data;
+};
+
+struct wlr_fullscreen_shell_v1_present_surface_event {
+ struct wl_client *client;
+ struct wlr_surface *surface; // can be NULL
+ enum zwp_fullscreen_shell_v1_present_method method;
+ struct wlr_output *output; // can be NULL
+};
+
+struct wlr_fullscreen_shell_v1 *wlr_fullscreen_shell_v1_create(
+ struct wl_display *display);
+void wlr_fullscreen_shell_v1_destroy(struct wlr_fullscreen_shell_v1 *shell);
+
+#endif
diff --git a/protocol/meson.build b/protocol/meson.build
index 6d5acf37..93820d57 100644
--- a/protocol/meson.build
+++ b/protocol/meson.build
@@ -13,14 +13,15 @@ endif
protocols = [
[wl_protocol_dir, 'stable/presentation-time/presentation-time.xml'],
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
+ [wl_protocol_dir, 'unstable/fullscreen-shell/fullscreen-shell-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'],
+ [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'],
+ [wl_protocol_dir, 'unstable/relative-pointer/relative-pointer-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/tablet/tablet-unstable-v2.xml'],
[wl_protocol_dir, 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'],
- [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'],
- [wl_protocol_dir, 'unstable/relative-pointer/relative-pointer-unstable-v1.xml'],
'gamma-control.xml',
'gtk-primary-selection.xml',
'idle.xml',
@@ -30,8 +31,8 @@ protocols = [
'text-input-unstable-v3.xml',
'virtual-keyboard-unstable-v1.xml',
'wlr-export-dmabuf-unstable-v1.xml',
- 'wlr-gamma-control-unstable-v1.xml',
'wlr-foreign-toplevel-management-unstable-v1.xml',
+ 'wlr-gamma-control-unstable-v1.xml',
'wlr-input-inhibitor-unstable-v1.xml',
'wlr-layer-shell-unstable-v1.xml',
'wlr-screencopy-unstable-v1.xml',
@@ -40,17 +41,17 @@ protocols = [
client_protocols = [
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
[wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'],
- [wl_protocol_dir, 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml'],
- [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'],
[wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/relative-pointer/relative-pointer-unstable-v1.xml'],
+ [wl_protocol_dir, 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml'],
+ [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'],
'idle.xml',
'input-method-unstable-v2.xml',
'screenshooter.xml',
'text-input-unstable-v3.xml',
'wlr-export-dmabuf-unstable-v1.xml',
- 'wlr-gamma-control-unstable-v1.xml',
'wlr-foreign-toplevel-management-unstable-v1.xml',
+ 'wlr-gamma-control-unstable-v1.xml',
'wlr-input-inhibitor-unstable-v1.xml',
'wlr-layer-shell-unstable-v1.xml',
'wlr-screencopy-unstable-v1.xml',
diff --git a/types/meson.build b/types/meson.build
index 982f607d..a1dc213f 100644
--- a/types/meson.build
+++ b/types/meson.build
@@ -29,6 +29,7 @@ lib_wlr_types = static_library(
'wlr_cursor.c',
'wlr_export_dmabuf_v1.c',
'wlr_foreign_toplevel_management_v1.c',
+ 'wlr_fullscreen_shell_v1.c',
'wlr_gamma_control_v1.c',
'wlr_gamma_control.c',
'wlr_gtk_primary_selection.c',
@@ -50,8 +51,8 @@ lib_wlr_types = static_library(
'wlr_presentation_time.c',
'wlr_primary_selection.c',
'wlr_region.c',
- 'wlr_screencopy_v1.c',
'wlr_relative_pointer_v1.c',
+ 'wlr_screencopy_v1.c',
'wlr_screenshooter.c',
'wlr_server_decoration.c',
'wlr_surface.c',
diff --git a/types/wlr_fullscreen_shell_v1.c b/types/wlr_fullscreen_shell_v1.c
new file mode 100644
index 00000000..5bff72ff
--- /dev/null
+++ b/types/wlr_fullscreen_shell_v1.c
@@ -0,0 +1,129 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <wlr/types/wlr_fullscreen_shell_v1.h>
+#include <wlr/types/wlr_output.h>
+#include <wlr/types/wlr_surface.h>
+#include <wlr/util/log.h>
+#include "util/signal.h"
+
+#define FULLSCREEN_SHELL_VERSION 1
+
+static const struct zwp_fullscreen_shell_v1_interface shell_impl;
+
+static struct wlr_fullscreen_shell_v1 *shell_from_resource(
+ struct wl_resource *resource) {
+ assert(wl_resource_instance_of(resource,
+ &zwp_fullscreen_shell_v1_interface, &shell_impl));
+ return wl_resource_get_user_data(resource);
+}
+
+static void shell_handle_present_surface(struct wl_client *client,
+ struct wl_resource *shell_resource,
+ struct wl_resource *surface_resource, uint32_t method,
+ struct wl_resource *output_resource) {
+ struct wlr_fullscreen_shell_v1 *shell = shell_from_resource(shell_resource);
+ struct wlr_surface *surface = NULL;
+ if (surface_resource != NULL) {
+ surface = wlr_surface_from_resource(surface_resource);
+ }
+ struct wlr_output *output = NULL;
+ if (output_resource != NULL) {
+ output = wlr_output_from_resource(output_resource);
+ }
+
+ struct wlr_fullscreen_shell_v1_present_surface_event event = {
+ .client = client,
+ .surface = surface,
+ .method = method,
+ .output = output,
+ };
+ wlr_signal_emit_safe(&shell->events.present_surface, &event);
+}
+
+static void shell_handle_present_surface_for_mode(struct wl_client *client,
+ struct wl_resource *shell_resource,
+ struct wl_resource *surface_resource,
+ struct wl_resource *output_resource, int32_t framerate,
+ uint32_t feedback_id) {
+ uint32_t version = wl_resource_get_version(shell_resource);
+ struct wl_resource *feedback_resource =
+ wl_resource_create(client, NULL, version, feedback_id);
+ if (feedback_resource == NULL) {
+ wl_resource_post_no_memory(shell_resource);
+ return;
+ }
+
+ // TODO: add support for mode switch
+ zwp_fullscreen_shell_mode_feedback_v1_send_mode_failed(feedback_resource);
+ wl_resource_destroy(feedback_resource);
+}
+
+static const struct zwp_fullscreen_shell_v1_interface shell_impl = {
+ .present_surface = shell_handle_present_surface,
+ .present_surface_for_mode = shell_handle_present_surface_for_mode,
+};
+
+static void shell_handle_resource_destroy(struct wl_resource *resource) {
+ wl_list_remove(wl_resource_get_link(resource));
+}
+
+static void shell_bind(struct wl_client *client, void *data, uint32_t version,
+ uint32_t id) {
+ struct wlr_fullscreen_shell_v1 *shell = data;
+
+ struct wl_resource *resource = wl_resource_create(client,
+ &zwp_fullscreen_shell_v1_interface, version, id);
+ if (resource == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+ wl_resource_set_implementation(resource, &shell_impl, shell,
+ shell_handle_resource_destroy);
+
+ wl_list_insert(&shell->resources, wl_resource_get_link(resource));
+}
+
+static void handle_display_destroy(struct wl_listener *listener, void *data) {
+ struct wlr_fullscreen_shell_v1 *shell =
+ wl_container_of(listener, shell, display_destroy);
+ wlr_fullscreen_shell_v1_destroy(shell);
+}
+
+struct wlr_fullscreen_shell_v1 *wlr_fullscreen_shell_v1_create(
+ struct wl_display *display) {
+ struct wlr_fullscreen_shell_v1 *shell =
+ calloc(1, sizeof(struct wlr_fullscreen_shell_v1));
+ if (shell == NULL) {
+ return NULL;
+ }
+ wl_list_init(&shell->resources);
+ wl_signal_init(&shell->events.destroy);
+ wl_signal_init(&shell->events.present_surface);
+
+ shell->global = wl_global_create(display,
+ &zwp_fullscreen_shell_v1_interface, FULLSCREEN_SHELL_VERSION,
+ shell, shell_bind);
+ if (shell->global == NULL) {
+ free(shell);
+ return NULL;
+ }
+
+ shell->display_destroy.notify = handle_display_destroy;
+ wl_display_add_destroy_listener(display, &shell->display_destroy);
+
+ return shell;
+}
+
+void wlr_fullscreen_shell_v1_destroy(struct wlr_fullscreen_shell_v1 *shell) {
+ if (shell == NULL) {
+ return;
+ }
+ wlr_signal_emit_safe(&shell->events.destroy, shell);
+ wl_list_remove(&shell->display_destroy.link);
+ wl_global_destroy(shell->global);
+ struct wl_resource *resource, *resource_tmp;
+ wl_resource_for_each_safe(resource, resource_tmp, &shell->resources) {
+ wl_resource_destroy(resource);
+ }
+ free(shell);
+}