1 #include <hikari/output.h>
2
3 #include <wlr/backend.h>
4
5 #include <hikari/memory.h>
6 #include <hikari/renderer.h>
7 #include <hikari/server.h>
8 #ifdef HAVE_XWAYLAND
9 #include <hikari/view.h>
10 #endif
11
12 static inline void
render_image_to_surface(cairo_surface_t * output,cairo_surface_t * image,enum hikari_background_fit fit)13 render_image_to_surface(cairo_surface_t *output,
14 cairo_surface_t *image,
15 enum hikari_background_fit fit)
16 {
17 cairo_t *cairo = cairo_create(output);
18 if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) {
19 return;
20 }
21
22 double output_width = cairo_image_surface_get_width(output);
23 double output_height = cairo_image_surface_get_height(output);
24 double width = cairo_image_surface_get_width(image);
25 double height = cairo_image_surface_get_height(image);
26
27 cairo_rectangle(cairo, 0, 0, output_width, output_height);
28 cairo_fill(cairo);
29
30 if (fit == HIKARI_BACKGROUND_STRETCH) {
31 cairo_scale(cairo, output_width / width, output_height / height);
32 cairo_set_source_surface(cairo, image, 0, 0);
33 } else if (fit == HIKARI_BACKGROUND_CENTER) {
34 cairo_set_source_surface(cairo,
35 image,
36 output_width / 2 - width / 2,
37 output_height / 2 - height / 2);
38 } else if (fit == HIKARI_BACKGROUND_TILE) {
39 cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image);
40 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
41 cairo_set_source(cairo, pattern);
42 cairo_pattern_destroy(pattern);
43 }
44
45 cairo_paint(cairo);
46 cairo_destroy(cairo);
47 }
48
49 void
hikari_output_load_background(struct hikari_output * output,const char * path,enum hikari_background_fit background_fit)50 hikari_output_load_background(struct hikari_output *output,
51 const char *path,
52 enum hikari_background_fit background_fit)
53 {
54 if (output->background != NULL) {
55 wlr_texture_destroy(output->background);
56 output->background = NULL;
57 }
58
59 assert(output->background == NULL);
60
61 if (path == NULL) {
62 goto done;
63 }
64
65 cairo_surface_t *image = cairo_image_surface_create_from_png(path);
66 if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) {
67 goto done;
68 }
69
70 int output_width = output->geometry.width;
71 int output_height = output->geometry.height;
72
73 cairo_surface_t *output_surface = cairo_image_surface_create(
74 CAIRO_FORMAT_ARGB32, output_width, output_height);
75 if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) {
76 cairo_surface_destroy(image);
77 goto done;
78 }
79
80 render_image_to_surface(output_surface, image, background_fit);
81
82 unsigned char *data = cairo_image_surface_get_data(output_surface);
83 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, output_width);
84
85 struct wlr_renderer *renderer =
86 wlr_backend_get_renderer(output->wlr_output->backend);
87
88 output->background = wlr_texture_from_pixels(renderer,
89 WL_SHM_FORMAT_ARGB8888,
90 stride,
91 output_width,
92 output_height,
93 data);
94
95 cairo_surface_destroy(image);
96 cairo_surface_destroy(output_surface);
97
98 done:
99 if (output->enabled) {
100 hikari_output_damage_whole(output);
101 }
102 }
103
104 void
hikari_output_damage_whole(struct hikari_output * output)105 hikari_output_damage_whole(struct hikari_output *output)
106 {
107 assert(output != NULL);
108
109 wlr_output_damage_add_whole(output->damage);
110 }
111
112 void
hikari_output_disable(struct hikari_output * output)113 hikari_output_disable(struct hikari_output *output)
114 {
115 assert(output != NULL);
116
117 if (!output->enabled) {
118 return;
119 }
120
121 struct wlr_output *wlr_output = output->wlr_output;
122
123 wl_list_remove(&output->damage_frame.link);
124 wl_list_init(&output->damage_frame.link);
125
126 wlr_output_rollback(wlr_output);
127 wlr_output_enable(wlr_output, false);
128 wlr_output_commit(wlr_output);
129
130 output->enabled = false;
131 }
132
133 void
hikari_output_enable(struct hikari_output * output)134 hikari_output_enable(struct hikari_output *output)
135 {
136 assert(output != NULL);
137
138 if (output->enabled) {
139 return;
140 }
141
142 struct wlr_output *wlr_output = output->wlr_output;
143
144 wl_list_remove(&output->damage_frame.link);
145 output->damage_frame.notify = hikari_renderer_damage_frame_handler;
146 wl_signal_add(&output->damage->events.frame, &output->damage_frame);
147
148 wlr_output_enable(wlr_output, true);
149 wlr_output_commit(wlr_output);
150 hikari_output_damage_whole(output);
151
152 output->enabled = true;
153 }
154
155 static void
output_geometry(struct hikari_output * output)156 output_geometry(struct hikari_output *output)
157 {
158 struct wlr_box *output_box = wlr_output_layout_get_box(
159 hikari_server.output_layout, output->wlr_output);
160
161 output->geometry.x = output_box->x;
162 output->geometry.y = output_box->y;
163 output->geometry.width = output_box->width;
164 output->geometry.height = output_box->height;
165
166 output->usable_area = (struct wlr_box){
167 .x = 0, .y = 0, .width = output_box->width, .height = output_box->height
168 };
169 }
170
171 /* static void */
172 /* mode_handler(struct wl_listener *listener, void *data) */
173 /* { */
174 /* #if !defined(NDEBUG) */
175 /* printf("MODE\n"); */
176 /* #endif */
177
178 /* struct hikari_output *output = wl_container_of( */
179 /* listener, */
180 /* output, */
181 /* mode); */
182
183 /* output_geometry(output); */
184 /* } */
185
186 static void
damage_destroy_handler(struct wl_listener * listener,void * data)187 damage_destroy_handler(struct wl_listener *listener, void *data)
188 {
189 struct hikari_output *output =
190 wl_container_of(listener, output, damage_destroy);
191
192 hikari_output_disable(output);
193 }
194
195 #ifdef HAVE_LAYERSHELL
196 static void
close_layers(struct wl_list * layers)197 close_layers(struct wl_list *layers)
198 {
199 struct hikari_layer *layer, *layer_temp;
200 wl_list_for_each_safe (layer, layer_temp, layers, layer_surfaces) {
201 wlr_layer_surface_v1_close(layer->surface);
202 layer->output = NULL;
203 }
204 }
205 #endif
206
207 static void
destroy_handler(struct wl_listener * listener,void * data)208 destroy_handler(struct wl_listener *listener, void *data)
209 {
210 struct hikari_output *output = wl_container_of(listener, output, destroy);
211
212 #ifndef NDEBUG
213 printf("DESTORY OUTPUT %p\n", output);
214 #endif
215
216 hikari_output_fini(output);
217 hikari_free(output);
218 }
219
220 void
hikari_output_init(struct hikari_output * output,struct wlr_output * wlr_output)221 hikari_output_init(struct hikari_output *output, struct wlr_output *wlr_output)
222 {
223 assert(!output->enabled);
224
225 bool noop = wlr_output->backend == hikari_server.noop_backend;
226
227 output->wlr_output = wlr_output;
228 output->damage = wlr_output_damage_create(wlr_output);
229 output->background = NULL;
230 output->enabled = false;
231 output->workspace = hikari_malloc(sizeof(struct hikari_workspace));
232
233 #ifdef HAVE_XWAYLAND
234 wl_list_init(&output->unmanaged_xwayland_views);
235 #endif
236 wl_list_init(&output->views);
237
238 #ifdef HAVE_LAYERSHELL
239 wl_list_init(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
240 wl_list_init(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
241 wl_list_init(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
242 wl_list_init(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
243 #endif
244
245 hikari_workspace_init(output->workspace, output);
246
247 wlr_output->data = output;
248
249 output->destroy.notify = destroy_handler;
250 wl_signal_add(&wlr_output->events.destroy, &output->destroy);
251
252 if (!noop) {
253 bool first = wl_list_empty(&hikari_server.outputs);
254
255 wl_list_insert(&hikari_server.outputs, &output->server_outputs);
256
257 output->damage_destroy.notify = damage_destroy_handler;
258 wl_signal_add(&output->damage->events.destroy, &output->damage_destroy);
259
260 if (!wl_list_empty(&wlr_output->modes)) {
261 struct wlr_output_mode *mode =
262 wl_container_of(wlr_output->modes.prev, mode, link);
263 wlr_output_set_mode(wlr_output, mode);
264 }
265
266 wl_list_init(&output->damage_frame.link);
267
268 if (!hikari_server_in_lock_mode()) {
269 hikari_output_enable(output);
270 } else if (hikari_lock_mode_are_outputs_disabled(
271 &hikari_server.lock_mode)) {
272 hikari_output_disable(output);
273 }
274
275 struct hikari_output_config *output_config =
276 hikari_configuration_resolve_output_config(
277 hikari_configuration, wlr_output->name);
278
279 if (output_config != NULL && output_config->position.value.type ==
280 HIKARI_POSITION_CONFIG_TYPE_ABSOLUTE) {
281 int x = output_config->position.value.config.absolute.x;
282 int y = output_config->position.value.config.absolute.y;
283
284 wlr_output_layout_add(hikari_server.output_layout, wlr_output, x, y);
285 } else {
286 wlr_output_layout_add_auto(hikari_server.output_layout, wlr_output);
287 }
288
289 output_geometry(output);
290
291 if (first) {
292 hikari_workspace_merge(
293 hikari_server.noop_output->workspace, output->workspace);
294 }
295 }
296 }
297
298 void
hikari_output_fini(struct hikari_output * output)299 hikari_output_fini(struct hikari_output *output)
300 {
301 bool noop = output->wlr_output->backend == hikari_server.noop_backend;
302
303 #ifdef HAVE_LAYERSHELL
304 close_layers(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
305 close_layers(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
306 close_layers(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
307 close_layers(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
308 #endif
309
310 hikari_output_disable(output);
311
312 wl_list_remove(&output->destroy.link);
313
314 struct hikari_workspace *workspace = output->workspace;
315
316 if (!noop) {
317 struct hikari_workspace *merge_workspace;
318 struct hikari_workspace *next_workspace = hikari_workspace_next(workspace);
319
320 wlr_texture_destroy(output->background);
321
322 if (workspace != next_workspace) {
323 merge_workspace = next_workspace;
324 } else {
325 merge_workspace = hikari_server.noop_output->workspace;
326 }
327
328 hikari_workspace_merge(workspace, merge_workspace);
329
330 if (!hikari_server_in_lock_mode()) {
331 if (!hikari_server_in_normal_mode()) {
332 hikari_server_enter_normal_mode(NULL);
333 }
334
335 hikari_workspace_focus_view(merge_workspace, NULL);
336 } else {
337 merge_workspace->focus_view = NULL;
338 hikari_server.workspace = merge_workspace;
339 }
340
341 wl_list_remove(&output->server_outputs);
342 wl_list_remove(&output->damage_destroy.link);
343 } else {
344 hikari_server.workspace = NULL;
345 }
346
347 hikari_workspace_fini(workspace);
348 hikari_free(workspace);
349 }
350
351 void
hikari_output_move(struct hikari_output * output,double lx,double ly)352 hikari_output_move(struct hikari_output *output, double lx, double ly)
353 {
354 wlr_output_layout_move(
355 hikari_server.output_layout, output->wlr_output, lx, ly);
356 }
357
358 #define CYCLE_OUTPUT(name) \
359 struct hikari_output *hikari_output_##name(struct hikari_output *output) \
360 { \
361 if (wl_list_empty(&hikari_server.outputs)) { \
362 return NULL; \
363 } \
364 \
365 struct wl_list *name = output->server_outputs.name; \
366 \
367 if (name == &hikari_server.outputs) { \
368 name = hikari_server.outputs.name; \
369 } \
370 \
371 struct hikari_output *name##_output = \
372 wl_container_of(name, name##_output, server_outputs); \
373 \
374 return name##_output; \
375 }
376
377 CYCLE_OUTPUT(next)
CYCLE_OUTPUT(prev)378 CYCLE_OUTPUT(prev)
379 #undef CYCLE_OUTPUT
380
381 #ifdef HAVE_XWAYLAND
382 void
383 hikari_output_rearrange_xwayland_views(struct hikari_output *output)
384 {
385 struct hikari_view *view;
386 wl_list_for_each (view, &output->views, output_views) {
387 if (view->move != NULL) {
388 struct wlr_box *geometry = hikari_view_geometry(view);
389 view->move(view, geometry->x, geometry->y);
390 }
391 }
392 }
393 #endif
394