git.lucas.co / cce-compositor
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