Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
scenefx/examples/scene-graph.c (6.7K)
1 #include <assert.h>
2 #include <getopt.h>
3 #include <scenefx/render/fx_renderer/fx_renderer.h>
4 #include <scenefx/types/wlr_scene.h>
5 #include <stdbool.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <time.h>
9 #include <unistd.h>
10 #include <wayland-server-core.h>
11 #include <wlr/backend.h>
12 #include <wlr/render/allocator.h>
13 #include <wlr/render/wlr_renderer.h>
14 #include <wlr/types/wlr_compositor.h>
15 #include <wlr/types/wlr_output.h>
16 #include <wlr/types/wlr_xdg_shell.h>
17 #include <wlr/util/log.h>
18
19 /* Simple compositor making use of the scene-graph API. Input is unimplemented.
20 *
21 * New surfaces are stacked on top of the existing ones as they appear. */
22
23 static const int border_width = 3;
24 static const int corner_radius = 0; // TODO
25
26 struct server {
27 struct wl_display *display;
28 struct wlr_backend *backend;
29 struct wlr_renderer *renderer;
30 struct wlr_allocator *allocator;
31 struct wlr_scene *scene;
32
33 uint32_t surface_offset;
34
35 struct wl_listener new_output;
36 struct wl_listener new_surface;
37 };
38
39 struct surface {
40 struct wlr_surface *wlr;
41 struct wlr_scene_surface *scene_surface;
42 struct wlr_scene_rect *border;
43 struct wlr_scene_shadow *shadow;
44 struct wl_list link;
45
46 struct wl_listener commit;
47 struct wl_listener destroy;
48 };
49
50 struct output {
51 struct wl_list link;
52 struct server *server;
53 struct wlr_output *wlr;
54 struct wlr_scene_output *scene_output;
55
56 struct wl_listener frame;
57 };
58
59 static void output_handle_frame(struct wl_listener *listener, void *data) {
60 struct output *output = wl_container_of(listener, output, frame);
61
62 if (!wlr_scene_output_commit(output->scene_output, NULL)) {
63 return;
64 }
65
66 struct timespec now;
67 clock_gettime(CLOCK_MONOTONIC, &now);
68 wlr_scene_output_send_frame_done(output->scene_output, &now);
69 }
70
71 static void server_handle_new_output(struct wl_listener *listener, void *data) {
72 struct server *server = wl_container_of(listener, server, new_output);
73 struct wlr_output *wlr_output = data;
74
75 wlr_output_init_render(wlr_output, server->allocator, server->renderer);
76
77 struct output *output = calloc(1, sizeof(*output));
78 output->wlr = wlr_output;
79 output->server = server;
80 output->frame.notify = output_handle_frame;
81 wl_signal_add(&wlr_output->events.frame, &output->frame);
82
83 output->scene_output = wlr_scene_output_create(server->scene, wlr_output);
84
85 struct wlr_output_state state;
86 wlr_output_state_init(&state);
87 wlr_output_state_set_enabled(&state, true);
88 struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
89 if (mode != NULL) {
90 wlr_output_state_set_mode(&state, mode);
91 }
92 wlr_output_commit_state(wlr_output, &state);
93 wlr_output_state_finish(&state);
94
95 wlr_output_create_global(wlr_output, server->display);
96 }
97
98 static void surface_handle_commit(struct wl_listener *listener, void *data) {
99 struct surface *surface = wl_container_of(listener, surface, commit);
100 wlr_scene_rect_set_size(surface->border,
101 surface->wlr->current.width + 2 * border_width,
102 surface->wlr->current.height + 2 * border_width);
103 wlr_scene_shadow_set_size(surface->shadow,
104 surface->wlr->current.width + 2 * (surface->shadow->blur_sigma + border_width),
105 surface->wlr->current.height + 2 * (surface->shadow->blur_sigma + border_width));
106 }
107
108 static void surface_handle_destroy(struct wl_listener *listener, void *data) {
109 struct surface *surface = wl_container_of(listener, surface, destroy);
110 wlr_scene_node_destroy(&surface->scene_surface->buffer->node);
111 wlr_scene_node_destroy(&surface->border->node);
112 wlr_scene_node_destroy(&surface->shadow->node);
113 wl_list_remove(&surface->destroy.link);
114 wl_list_remove(&surface->link);
115 free(surface);
116 }
117
118 static void server_handle_new_surface(struct wl_listener *listener,
119 void *data) {
120 struct server *server = wl_container_of(listener, server, new_surface);
121 struct wlr_surface *wlr_surface = data;
122
123 int pos = server->surface_offset;
124 server->surface_offset += 50;
125
126 struct surface *surface = calloc(1, sizeof(*surface));
127 surface->wlr = wlr_surface;
128 surface->commit.notify = surface_handle_commit;
129 wl_signal_add(&wlr_surface->events.commit, &surface->commit);
130 surface->destroy.notify = surface_handle_destroy;
131 wl_signal_add(&wlr_surface->events.destroy, &surface->destroy);
132
133 /* Border dimensions will be set in surface.commit handler */
134 surface->border = wlr_scene_rect_create(&server->scene->tree,
135 0, 0, (float[4]){ 0.5f, 0.5f, 0.5f, 1 });
136 wlr_scene_node_set_position(&surface->border->node, pos, pos);
137
138 /* Shadow dimensions will be set in surface.commit handler */
139 float blur_sigma = 20.0f;
140 surface->shadow = wlr_scene_shadow_create(&server->scene->tree,
141 0, 0, corner_radius, blur_sigma, (float[4]){ 1.0f, 0.f, 0.f, 1.0f });
142 wlr_scene_node_set_position(&surface->shadow->node,
143 pos - blur_sigma, pos - blur_sigma);
144 surface->scene_surface =
145 wlr_scene_surface_create(&server->scene->tree, wlr_surface);
146 wlr_scene_buffer_set_corner_radius(
147 surface->scene_surface->buffer, corner_radius);
148
149 wlr_scene_node_set_position(&surface->scene_surface->buffer->node,
150 pos + border_width, pos + border_width);
151 }
152
153 int main(int argc, char *argv[]) {
154 wlr_log_init(WLR_DEBUG, NULL);
155
156 char *startup_cmd = NULL;
157
158 int c;
159 while ((c = getopt(argc, argv, "s:")) != -1) {
160 switch (c) {
161 case 's':
162 startup_cmd = optarg;
163 break;
164 default:
165 printf("usage: %s [-s startup-command]\n", argv[0]);
166 return EXIT_FAILURE;
167 }
168 }
169 if (optind < argc) {
170 printf("usage: %s [-s startup-command]\n", argv[0]);
171 return EXIT_FAILURE;
172 }
173
174 struct server server = {0};
175 server.surface_offset = 0;
176 server.display = wl_display_create();
177 server.backend = wlr_backend_autocreate(wl_display_get_event_loop(server.display), NULL);
178 server.scene = wlr_scene_create();
179
180 server.renderer = fx_renderer_create(server.backend);
181 wlr_renderer_init_wl_display(server.renderer, server.display);
182
183 server.allocator = wlr_allocator_autocreate(server.backend,
184 server.renderer);
185
186 struct wlr_compositor *compositor =
187 wlr_compositor_create(server.display, 5, server.renderer);
188
189 wlr_xdg_shell_create(server.display, 2);
190
191 server.new_output.notify = server_handle_new_output;
192 wl_signal_add(&server.backend->events.new_output, &server.new_output);
193
194 server.new_surface.notify = server_handle_new_surface;
195 wl_signal_add(&compositor->events.new_surface, &server.new_surface);
196
197 const char *socket = wl_display_add_socket_auto(server.display);
198 if (!socket) {
199 wl_display_destroy(server.display);
200 return EXIT_FAILURE;
201 }
202
203 if (!wlr_backend_start(server.backend)) {
204 wl_display_destroy(server.display);
205 return EXIT_FAILURE;
206 }
207
208 setenv("WAYLAND_DISPLAY", socket, true);
209 if (startup_cmd != NULL) {
210 if (fork() == 0) {
211 execl("/bin/sh", "/bin/sh", "-c", startup_cmd, (void *)NULL);
212 }
213 }
214
215 wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s",
216 socket);
217 wl_display_run(server.display);
218
219 wl_display_destroy_clients(server.display);
220 wl_display_destroy(server.display);
221 return EXIT_SUCCESS;
222 }
223