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