1 /*******************************************************************
2  *
3  * @file lv_wayland.c - The Wayland client for LVGL applications
4  * Based on the original file from the repository.
5  *
6  * Porting to LVGL 9.1
7  * EDGEMTech Ltd. by Erik Tagirov (erik.tagirov@edgemtech.ch)
8  *
9  * See LICENCE.txt for details
10  *
11  ******************************************************************/
12 
13 typedef int dummy_t;    /* Make GCC on windows happy, avoid empty translation unit */
14 
15 #ifndef _WIN32
16 
17 /*********************
18  *      INCLUDES
19  *********************/
20 #include "lv_wayland.h"
21 #include "lv_wayland_smm.h"
22 
23 #if LV_USE_WAYLAND
24 
25 #include <stdio.h>
26 #include <stddef.h>
27 #include <stdbool.h>
28 #include <stdint.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <time.h>
35 #include <poll.h>
36 #include <sys/mman.h>
37 #include <linux/input.h>
38 #include <linux/input-event-codes.h>
39 #include <wayland-client.h>
40 #include <wayland-cursor.h>
41 #include <xkbcommon/xkbcommon.h>
42 
43 #include "lvgl.h"
44 
45 #if !LV_WAYLAND_WL_SHELL
46     #include "wayland_xdg_shell.h"
47     #define LV_WAYLAND_XDG_SHELL 1
48 #else
49     #define LV_WAYLAND_XDG_SHELL 0
50 #endif
51 
52 
53 /*********************
54  *      DEFINES
55  *********************/
56 
57 #define LVGL_DRAW_BUFFER_DIV (8)
58 #define DMG_CACHE_CAPACITY (32)
59 #define TAG_LOCAL         (0)
60 #define TAG_BUFFER_DAMAGE (1)
61 
62 #if LV_WAYLAND_WINDOW_DECORATIONS
63     #define TITLE_BAR_HEIGHT 24
64     #define BORDER_SIZE 2
65     #define BUTTON_MARGIN LV_MAX((TITLE_BAR_HEIGHT / 6), BORDER_SIZE)
66     #define BUTTON_PADDING LV_MAX((TITLE_BAR_HEIGHT / 8), BORDER_SIZE)
67     #define BUTTON_SIZE (TITLE_BAR_HEIGHT - (2 * BUTTON_MARGIN))
68 #endif
69 
70 #ifndef LV_WAYLAND_CYCLE_PERIOD
71     #define LV_WAYLAND_CYCLE_PERIOD LV_MIN(LV_DEF_REFR_PERIOD,1)
72 #endif
73 
74 #define SHM_FORMAT_UNKNOWN 0xFFFFFF
75 
76 #if (LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1)
77     #error [wayland] Unsupported LV_COLOR_DEPTH
78 #endif
79 
80 /**********************
81  *      TYPEDEFS
82  **********************/
83 
84 enum object_type {
85     OBJECT_TITLEBAR = 0,
86     OBJECT_BUTTON_CLOSE,
87 #if LV_WAYLAND_XDG_SHELL
88     OBJECT_BUTTON_MAXIMIZE,
89     OBJECT_BUTTON_MINIMIZE,
90 #endif
91     OBJECT_BORDER_TOP,
92     OBJECT_BORDER_BOTTOM,
93     OBJECT_BORDER_LEFT,
94     OBJECT_BORDER_RIGHT,
95     OBJECT_WINDOW,
96 };
97 
98 #define FIRST_DECORATION (OBJECT_TITLEBAR)
99 #define LAST_DECORATION (OBJECT_BORDER_RIGHT)
100 #define NUM_DECORATIONS (LAST_DECORATION-FIRST_DECORATION+1)
101 
102 
103 struct window;
104 struct input {
105     struct {
106         uint32_t x;
107         uint32_t y;
108         lv_indev_state_t left_button;
109         lv_indev_state_t right_button;
110         lv_indev_state_t wheel_button;
111         int16_t wheel_diff;
112     } pointer;
113 
114     struct {
115         lv_key_t key;
116         lv_indev_state_t state;
117     } keyboard;
118 
119 #if LV_USE_GESTURE_RECOGNITION
120     lv_indev_touch_data_t touches[10];
121     uint8_t touch_event_cnt;
122     uint8_t primary_id;
123     lv_indev_gesture_recognizer_t recognizer;
124 #endif
125 };
126 
127 struct seat {
128     struct wl_touch * wl_touch;
129     struct wl_pointer * wl_pointer;
130     struct wl_keyboard * wl_keyboard;
131 
132     struct {
133         struct xkb_keymap * keymap;
134         struct xkb_state * state;
135     } xkb;
136 };
137 
138 struct graphic_object {
139     struct window * window;
140 
141     struct wl_surface * surface;
142     bool surface_configured;
143     smm_buffer_t * pending_buffer;
144     smm_group_t * buffer_group;
145     struct wl_subsurface * subsurface;
146 
147     enum object_type type;
148     int width;
149     int height;
150 
151     struct input input;
152 };
153 
154 struct application {
155     struct wl_display * display;
156     struct wl_registry * registry;
157     struct wl_compositor * compositor;
158     struct wl_subcompositor * subcompositor;
159     struct wl_shm * shm;
160     struct wl_seat * wl_seat;
161 
162     struct wl_cursor_theme * cursor_theme;
163     struct wl_surface * cursor_surface;
164 
165 #if LV_WAYLAND_WL_SHELL
166     struct wl_shell * wl_shell;
167 #endif
168 
169 #if LV_WAYLAND_XDG_SHELL
170     struct xdg_wm_base * xdg_wm;
171 #endif
172 
173     const char * xdg_runtime_dir;
174 
175 #ifdef LV_WAYLAND_WINDOW_DECORATIONS
176     bool opt_disable_decorations;
177 #endif
178 
179     uint32_t shm_format;
180 
181     struct xkb_context * xkb_context;
182 
183     struct seat seat;
184 
185     struct graphic_object * touch_obj;
186     struct graphic_object * pointer_obj;
187     struct graphic_object * keyboard_obj;
188 
189     lv_ll_t window_ll;
190     lv_timer_t * cycle_timer;
191 
192     bool cursor_flush_pending;
193     struct pollfd wayland_pfd;
194 };
195 
196 struct window {
197     lv_display_t * lv_disp;
198     lv_draw_buf_t * lv_disp_draw_buf;
199 
200     lv_indev_t * lv_indev_pointer;
201     lv_indev_t * lv_indev_pointeraxis;
202     lv_indev_t * lv_indev_touch;
203     lv_indev_t * lv_indev_keyboard;
204 
205     lv_wayland_display_close_f_t close_cb;
206 
207     struct application * application;
208 
209 #if LV_WAYLAND_WL_SHELL
210     struct wl_shell_surface * wl_shell_surface;
211 #endif
212 
213 #if LV_WAYLAND_XDG_SHELL
214     struct xdg_surface * xdg_surface;
215     struct xdg_toplevel * xdg_toplevel;
216     uint32_t wm_capabilities;
217 #endif
218 
219     struct graphic_object * body;
220     struct {
221         lv_area_t cache[DMG_CACHE_CAPACITY];
222         unsigned char start;
223         unsigned char end;
224         unsigned size;
225     } dmg_cache;
226 
227 #if LV_WAYLAND_WINDOW_DECORATIONS
228     struct graphic_object * decoration[NUM_DECORATIONS];
229 #endif
230 
231     int width;
232     int height;
233 
234     bool resize_pending;
235     int resize_width;
236     int resize_height;
237 
238     bool flush_pending;
239     bool shall_close;
240     bool closed;
241     bool maximized;
242     bool fullscreen;
243     uint32_t frame_counter;
244     bool frame_done;
245 };
246 
247 /*********************************
248  *   STATIC VARIABLES and FUNCTIONS
249  *********************************/
250 
251 static struct application application;
252 
253 static void color_fill(void * pixels, lv_color_t color, uint32_t width, uint32_t height);
254 static void color_fill_XRGB8888(void * pixels, lv_color_t color, uint32_t width, uint32_t height);
255 static void color_fill_RGB565(void * pixels, lv_color_t color, uint32_t width, uint32_t height);
256 
257 static const struct wl_callback_listener wl_surface_frame_listener;
258 static bool resize_window(struct window * window, int width, int height);
259 static struct graphic_object * create_graphic_obj(struct application * app, struct window * window,
260                                                   enum object_type type,
261                                                   struct graphic_object * parent);
262 
263 static uint32_t tick_get_cb(void);
264 
265 static void wayland_init(void);
266 static void wayland_deinit(void);
267 
268 /**
269  * The frame callback called when the compositor has finished rendering
270  * a frame.It increments the frame counter and sets up the callback
271  * for the next frame the frame counter is used to avoid needlessly
272  * committing frames too fast on a slow system
273  *
274  * NOTE: this function is invoked by the wayland-server library within the compositor
275  * the event is added to the queue, and then upon the next timer call it's
276  * called indirectly from _lv_wayland_handle_input (via wl_display_dispatch_queue)
277  * @param void data the user object defined that was tied to this event during
278  * the configuration of the callback
279  * @param struct wl_callback The callback that needs to be destroyed and re-created
280  * @param time Timestamp of the event (unused)
281  */
graphic_obj_frame_done(void * data,struct wl_callback * cb,uint32_t time)282 static void graphic_obj_frame_done(void * data, struct wl_callback * cb, uint32_t time)
283 {
284     struct graphic_object * obj;
285     struct window * window;
286 
287     LV_UNUSED(time);
288 
289     wl_callback_destroy(cb);
290 
291     obj = (struct graphic_object *)data;
292     window = obj->window;
293     window->frame_counter++;
294 
295     LV_LOG_TRACE("frame: %d done, new frame: %d",
296                  window->frame_counter - 1, window->frame_counter);
297 
298     window->frame_done = true;
299 
300 }
301 
302 static const struct wl_callback_listener wl_surface_frame_listener = {
303     .done = graphic_obj_frame_done,
304 };
305 
_is_digit(char ch)306 static inline bool _is_digit(char ch)
307 {
308     return (ch >= '0') && (ch <= '9');
309 }
310 
311 /*
312  *  shm_format
313  *  @description called by the compositor to advertise the supported
314  *  color formats for SHM buffers, there is a call per supported format
315  */
shm_format(void * data,struct wl_shm * wl_shm,uint32_t format)316 static void shm_format(void * data, struct wl_shm * wl_shm, uint32_t format)
317 {
318     struct application * app = data;
319 
320     LV_UNUSED(wl_shm);
321 
322     LV_LOG_TRACE("Supported color space fourcc.h code: %08X", format);
323 
324     if(LV_COLOR_DEPTH == 32 && format == WL_SHM_FORMAT_ARGB8888) {
325 
326         /* Wayland compositors MUST support ARGB8888 */
327         app->shm_format = format;
328 
329     }
330     else if(LV_COLOR_DEPTH == 32 &&
331             format == WL_SHM_FORMAT_XRGB8888 &&
332             app->shm_format != WL_SHM_FORMAT_ARGB8888) {
333 
334         /* Select XRGB only if the compositor doesn't support transprancy */
335         app->shm_format = format;
336 
337     }
338     else if(LV_COLOR_DEPTH == 16 && format == WL_SHM_FORMAT_RGB565) {
339 
340         app->shm_format = format;
341 
342     }
343 }
344 
345 static const struct wl_shm_listener shm_listener = {
346     shm_format
347 };
348 
pointer_handle_enter(void * data,struct wl_pointer * pointer,uint32_t serial,struct wl_surface * surface,wl_fixed_t sx,wl_fixed_t sy)349 static void pointer_handle_enter(void * data, struct wl_pointer * pointer,
350                                  uint32_t serial, struct wl_surface * surface,
351                                  wl_fixed_t sx, wl_fixed_t sy)
352 {
353     struct application * app = data;
354     const char * cursor = "left_ptr";
355     int pos_x = wl_fixed_to_int(sx);
356     int pos_y = wl_fixed_to_int(sy);
357 
358     if(!surface) {
359         app->pointer_obj = NULL;
360         return;
361     }
362 
363     app->pointer_obj = wl_surface_get_user_data(surface);
364 
365     app->pointer_obj->input.pointer.x = pos_x;
366     app->pointer_obj->input.pointer.y = pos_y;
367 
368 #if (LV_WAYLAND_WINDOW_DECORATIONS && LV_WAYLAND_XDG_SHELL)
369     if(!app->pointer_obj->window->xdg_toplevel || app->opt_disable_decorations) {
370         return;
371     }
372 
373     struct window * window = app->pointer_obj->window;
374 
375     switch(app->pointer_obj->type) {
376         case OBJECT_BORDER_TOP:
377             if(window->maximized) {
378                 // do nothing
379             }
380             else if(pos_x < (BORDER_SIZE * 5)) {
381                 cursor = "top_left_corner";
382             }
383             else if(pos_x >= (window->width + BORDER_SIZE - (BORDER_SIZE * 5))) {
384                 cursor = "top_right_corner";
385             }
386             else {
387                 cursor = "top_side";
388             }
389             break;
390         case OBJECT_BORDER_BOTTOM:
391             if(window->maximized) {
392                 // do nothing
393             }
394             else if(pos_x < (BORDER_SIZE * 5)) {
395                 cursor = "bottom_left_corner";
396             }
397             else if(pos_x >= (window->width + BORDER_SIZE - (BORDER_SIZE * 5))) {
398                 cursor = "bottom_right_corner";
399             }
400             else {
401                 cursor = "bottom_side";
402             }
403             break;
404         case OBJECT_BORDER_LEFT:
405             if(window->maximized) {
406                 // do nothing
407             }
408             else if(pos_y < (BORDER_SIZE * 5)) {
409                 cursor = "top_left_corner";
410             }
411             else if(pos_y >= (window->height + BORDER_SIZE - (BORDER_SIZE * 5))) {
412                 cursor = "bottom_left_corner";
413             }
414             else {
415                 cursor = "left_side";
416             }
417             break;
418         case OBJECT_BORDER_RIGHT:
419             if(window->maximized) {
420                 // do nothing
421             }
422             else if(pos_y < (BORDER_SIZE * 5)) {
423                 cursor = "top_right_corner";
424             }
425             else if(pos_y >= (window->height + BORDER_SIZE - (BORDER_SIZE * 5))) {
426                 cursor = "bottom_right_corner";
427             }
428             else {
429                 cursor = "right_side";
430             }
431             break;
432         default:
433             break;
434     }
435 #endif
436 
437     if(app->cursor_surface) {
438         struct wl_cursor_image * cursor_image = wl_cursor_theme_get_cursor(app->cursor_theme, cursor)->images[0];
439         wl_pointer_set_cursor(pointer, serial, app->cursor_surface, cursor_image->hotspot_x, cursor_image->hotspot_y);
440         wl_surface_attach(app->cursor_surface, wl_cursor_image_get_buffer(cursor_image), 0, 0);
441         wl_surface_damage(app->cursor_surface, 0, 0, cursor_image->width, cursor_image->height);
442         wl_surface_commit(app->cursor_surface);
443         app->cursor_flush_pending = true;
444     }
445 }
446 
pointer_handle_leave(void * data,struct wl_pointer * pointer,uint32_t serial,struct wl_surface * surface)447 static void pointer_handle_leave(void * data, struct wl_pointer * pointer,
448                                  uint32_t serial, struct wl_surface * surface)
449 {
450     struct application * app = data;
451 
452     LV_UNUSED(pointer);
453     LV_UNUSED(serial);
454 
455     if(!surface || (app->pointer_obj == wl_surface_get_user_data(surface))) {
456         app->pointer_obj = NULL;
457     }
458 }
459 
pointer_handle_motion(void * data,struct wl_pointer * pointer,uint32_t time,wl_fixed_t sx,wl_fixed_t sy)460 static void pointer_handle_motion(void * data, struct wl_pointer * pointer,
461                                   uint32_t time, wl_fixed_t sx, wl_fixed_t sy)
462 {
463     struct application * app = data;
464 
465     LV_UNUSED(pointer);
466     LV_UNUSED(time);
467 
468     if(!app->pointer_obj) {
469         return;
470     }
471 
472     app->pointer_obj->input.pointer.x = LV_MAX(0, LV_MIN(wl_fixed_to_int(sx), app->pointer_obj->width - 1));
473     app->pointer_obj->input.pointer.y = LV_MAX(0, LV_MIN(wl_fixed_to_int(sy), app->pointer_obj->height - 1));
474 }
475 
pointer_handle_button(void * data,struct wl_pointer * wl_pointer,uint32_t serial,uint32_t time,uint32_t button,uint32_t state)476 static void pointer_handle_button(void * data, struct wl_pointer * wl_pointer,
477                                   uint32_t serial, uint32_t time, uint32_t button,
478                                   uint32_t state)
479 {
480     struct application * app = data;
481 
482     LV_UNUSED(serial);
483     LV_UNUSED(wl_pointer);
484     LV_UNUSED(time);
485 
486     const lv_indev_state_t lv_state =
487         (state == WL_POINTER_BUTTON_STATE_PRESSED) ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
488 
489     if(!app->pointer_obj) {
490         return;
491     }
492 
493 
494 #if LV_WAYLAND_WINDOW_DECORATIONS
495     struct window * window;
496     window = app->pointer_obj->window;
497     int pos_x = app->pointer_obj->input.pointer.x;
498     int pos_y = app->pointer_obj->input.pointer.y;
499 #endif
500 
501     switch(app->pointer_obj->type) {
502         case OBJECT_WINDOW:
503             switch(button) {
504                 case BTN_LEFT:
505                     app->pointer_obj->input.pointer.left_button = lv_state;
506                     break;
507                 case BTN_RIGHT:
508                     app->pointer_obj->input.pointer.right_button = lv_state;
509                     break;
510                 case BTN_MIDDLE:
511                     app->pointer_obj->input.pointer.wheel_button = lv_state;
512                     break;
513                 default:
514                     break;
515             }
516 
517             break;
518 #if LV_WAYLAND_WINDOW_DECORATIONS
519         case OBJECT_TITLEBAR:
520             if((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_PRESSED)) {
521 #if LV_WAYLAND_XDG_SHELL
522                 if(window->xdg_toplevel) {
523                     xdg_toplevel_move(window->xdg_toplevel, app->wl_seat, serial);
524                     window->flush_pending = true;
525                 }
526 #endif
527 #if LV_WAYLAND_WL_SHELL
528                 if(window->wl_shell_surface) {
529                     wl_shell_surface_move(window->wl_shell_surface, app->wl_seat, serial);
530                     window->flush_pending = true;
531                 }
532 #endif
533             }
534             break;
535         case OBJECT_BUTTON_CLOSE:
536             if((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_RELEASED)) {
537                 window->shall_close = true;
538             }
539             break;
540 #if LV_WAYLAND_XDG_SHELL
541         case OBJECT_BUTTON_MAXIMIZE:
542             if((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_RELEASED)) {
543                 if(window->xdg_toplevel) {
544                     if(window->maximized) {
545                         xdg_toplevel_unset_maximized(window->xdg_toplevel);
546                     }
547                     else {
548                         xdg_toplevel_set_maximized(window->xdg_toplevel);
549                     }
550                     window->maximized ^= true;
551                     window->flush_pending = true;
552                 }
553             }
554             break;
555         case OBJECT_BUTTON_MINIMIZE:
556             if((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_RELEASED)) {
557                 if(window->xdg_toplevel) {
558                     xdg_toplevel_set_minimized(window->xdg_toplevel);
559                     window->flush_pending = true;
560                 }
561             }
562             break;
563         case OBJECT_BORDER_TOP:
564             if((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_PRESSED)) {
565                 if(window->xdg_toplevel && !window->maximized) {
566                     uint32_t edge;
567                     if(pos_x < (BORDER_SIZE * 5)) {
568                         edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT;
569                     }
570                     else if(pos_x >= (window->width + BORDER_SIZE - (BORDER_SIZE * 5))) {
571                         edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT;
572                     }
573                     else {
574                         edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP;
575                     }
576                     xdg_toplevel_resize(window->xdg_toplevel,
577                                         window->application->wl_seat, serial, edge);
578                     window->flush_pending = true;
579                 }
580             }
581             break;
582         case OBJECT_BORDER_BOTTOM:
583             if((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_PRESSED)) {
584                 if(window->xdg_toplevel && !window->maximized) {
585                     uint32_t edge;
586                     if(pos_x < (BORDER_SIZE * 5)) {
587                         edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
588                     }
589                     else if(pos_x >= (window->width + BORDER_SIZE - (BORDER_SIZE * 5))) {
590                         edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT;
591                     }
592                     else {
593                         edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM;
594                     }
595                     xdg_toplevel_resize(window->xdg_toplevel,
596                                         window->application->wl_seat, serial, edge);
597                     window->flush_pending = true;
598                 }
599             }
600             break;
601         case OBJECT_BORDER_LEFT:
602             if((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_PRESSED)) {
603                 if(window->xdg_toplevel && !window->maximized) {
604                     uint32_t edge;
605                     if(pos_y < (BORDER_SIZE * 5)) {
606                         edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT;
607                     }
608                     else if(pos_y >= (window->height + BORDER_SIZE - (BORDER_SIZE * 5))) {
609                         edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
610                     }
611                     else {
612                         edge = XDG_TOPLEVEL_RESIZE_EDGE_LEFT;
613                     }
614                     xdg_toplevel_resize(window->xdg_toplevel,
615                                         window->application->wl_seat, serial, edge);
616                     window->flush_pending = true;
617                 }
618             }
619             break;
620         case OBJECT_BORDER_RIGHT:
621             if((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_PRESSED)) {
622                 if(window->xdg_toplevel && !window->maximized) {
623                     uint32_t edge;
624                     if(pos_y < (BORDER_SIZE * 5)) {
625                         edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT;
626                     }
627                     else if(pos_y >= (window->height + BORDER_SIZE - (BORDER_SIZE * 5))) {
628                         edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT;
629                     }
630                     else {
631                         edge = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT;
632                     }
633                     xdg_toplevel_resize(window->xdg_toplevel,
634                                         window->application->wl_seat, serial, edge);
635                     window->flush_pending = true;
636                 }
637             }
638             break;
639 #endif // LV_WAYLAND_XDG_SHELL
640 #endif // LV_WAYLAND_WINDOW_DECORATIONS
641         default:
642             break;
643     }
644 }
645 
pointer_handle_axis(void * data,struct wl_pointer * wl_pointer,uint32_t time,uint32_t axis,wl_fixed_t value)646 static void pointer_handle_axis(void * data, struct wl_pointer * wl_pointer,
647                                 uint32_t time, uint32_t axis, wl_fixed_t value)
648 {
649     struct application * app = data;
650     const int diff = wl_fixed_to_int(value);
651 
652     LV_UNUSED(time);
653     LV_UNUSED(wl_pointer);
654 
655     if(!app->pointer_obj) {
656         return;
657     }
658 
659     if(axis == 0) {
660         if(diff > 0) {
661             app->pointer_obj->input.pointer.wheel_diff++;
662         }
663         else if(diff < 0) {
664             app->pointer_obj->input.pointer.wheel_diff--;
665         }
666     }
667 }
668 
669 static const struct wl_pointer_listener pointer_listener = {
670     .enter  = pointer_handle_enter,
671     .leave  = pointer_handle_leave,
672     .motion = pointer_handle_motion,
673     .button = pointer_handle_button,
674     .axis   = pointer_handle_axis,
675 };
676 
keycode_xkb_to_lv(xkb_keysym_t xkb_key)677 static lv_key_t keycode_xkb_to_lv(xkb_keysym_t xkb_key)
678 {
679     lv_key_t key = 0;
680 
681     if(((xkb_key >= XKB_KEY_space) && (xkb_key <= XKB_KEY_asciitilde))) {
682         key = xkb_key;
683     }
684     else if(((xkb_key >= XKB_KEY_KP_0) && (xkb_key <= XKB_KEY_KP_9))) {
685         key = (xkb_key & 0x003f);
686     }
687     else {
688         switch(xkb_key) {
689             case XKB_KEY_BackSpace:
690                 key = LV_KEY_BACKSPACE;
691                 break;
692             case XKB_KEY_Return:
693             case XKB_KEY_KP_Enter:
694                 key = LV_KEY_ENTER;
695                 break;
696             case XKB_KEY_Escape:
697                 key = LV_KEY_ESC;
698                 break;
699             case XKB_KEY_Delete:
700             case XKB_KEY_KP_Delete:
701                 key = LV_KEY_DEL;
702                 break;
703             case XKB_KEY_Home:
704             case XKB_KEY_KP_Home:
705                 key = LV_KEY_HOME;
706                 break;
707             case XKB_KEY_Left:
708             case XKB_KEY_KP_Left:
709                 key = LV_KEY_LEFT;
710                 break;
711             case XKB_KEY_Up:
712             case XKB_KEY_KP_Up:
713                 key = LV_KEY_UP;
714                 break;
715             case XKB_KEY_Right:
716             case XKB_KEY_KP_Right:
717                 key = LV_KEY_RIGHT;
718                 break;
719             case XKB_KEY_Down:
720             case XKB_KEY_KP_Down:
721                 key = LV_KEY_DOWN;
722                 break;
723             case XKB_KEY_Prior:
724             case XKB_KEY_KP_Prior:
725                 key = LV_KEY_PREV;
726                 break;
727             case XKB_KEY_Next:
728             case XKB_KEY_KP_Next:
729             case XKB_KEY_Tab:
730             case XKB_KEY_KP_Tab:
731                 key = LV_KEY_NEXT;
732                 break;
733             case XKB_KEY_End:
734             case XKB_KEY_KP_End:
735                 key = LV_KEY_END;
736                 break;
737             default:
738                 break;
739         }
740     }
741 
742     return key;
743 }
744 
keyboard_handle_keymap(void * data,struct wl_keyboard * keyboard,uint32_t format,int fd,uint32_t size)745 static void keyboard_handle_keymap(void * data, struct wl_keyboard * keyboard,
746                                    uint32_t format, int fd, uint32_t size)
747 {
748     struct application * app = data;
749 
750     struct xkb_keymap * keymap;
751     struct xkb_state * state;
752     char * map_str;
753 
754     LV_UNUSED(keyboard);
755 
756     if(format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
757         close(fd);
758         return;
759     }
760 
761     map_str = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
762     if(map_str == MAP_FAILED) {
763         close(fd);
764         return;
765     }
766 
767     /* Set up XKB keymap */
768     keymap = xkb_keymap_new_from_string(app->xkb_context, map_str,
769                                         XKB_KEYMAP_FORMAT_TEXT_V1, 0);
770     munmap(map_str, size);
771     close(fd);
772 
773     if(!keymap) {
774         LV_LOG_ERROR("failed to compile keymap");
775         return;
776     }
777 
778     /* Set up XKB state */
779     state = xkb_state_new(keymap);
780     if(!state) {
781         LV_LOG_ERROR("failed to create XKB state");
782         xkb_keymap_unref(keymap);
783         return;
784     }
785 
786     xkb_keymap_unref(app->seat.xkb.keymap);
787     xkb_state_unref(app->seat.xkb.state);
788     app->seat.xkb.keymap = keymap;
789     app->seat.xkb.state = state;
790 }
791 
keyboard_handle_enter(void * data,struct wl_keyboard * keyboard,uint32_t serial,struct wl_surface * surface,struct wl_array * keys)792 static void keyboard_handle_enter(void * data, struct wl_keyboard * keyboard,
793                                   uint32_t serial, struct wl_surface * surface,
794                                   struct wl_array * keys)
795 {
796     struct application * app = data;
797 
798     LV_UNUSED(keyboard);
799     LV_UNUSED(serial);
800     LV_UNUSED(keys);
801 
802     if(!surface) {
803         app->keyboard_obj = NULL;
804     }
805     else {
806         app->keyboard_obj = wl_surface_get_user_data(surface);
807     }
808 }
809 
keyboard_handle_leave(void * data,struct wl_keyboard * keyboard,uint32_t serial,struct wl_surface * surface)810 static void keyboard_handle_leave(void * data, struct wl_keyboard * keyboard,
811                                   uint32_t serial, struct wl_surface * surface)
812 {
813     struct application * app = data;
814 
815     LV_UNUSED(serial);
816     LV_UNUSED(keyboard);
817 
818     if(!surface || (app->keyboard_obj == wl_surface_get_user_data(surface))) {
819         app->keyboard_obj = NULL;
820     }
821 }
822 
keyboard_handle_key(void * data,struct wl_keyboard * keyboard,uint32_t serial,uint32_t time,uint32_t key,uint32_t state)823 static void keyboard_handle_key(void * data, struct wl_keyboard * keyboard,
824                                 uint32_t serial, uint32_t time, uint32_t key,
825                                 uint32_t state)
826 {
827     struct application * app = data;
828     const uint32_t code = (key + 8);
829     const xkb_keysym_t * syms;
830     xkb_keysym_t sym = XKB_KEY_NoSymbol;
831 
832     LV_UNUSED(serial);
833     LV_UNUSED(time);
834     LV_UNUSED(keyboard);
835 
836     if(!app->keyboard_obj || !app->seat.xkb.state) {
837         return;
838     }
839 
840     if(xkb_state_key_get_syms(app->seat.xkb.state, code, &syms) == 1) {
841         sym = syms[0];
842     }
843 
844     const lv_key_t lv_key = keycode_xkb_to_lv(sym);
845     const lv_indev_state_t lv_state =
846         (state == WL_KEYBOARD_KEY_STATE_PRESSED) ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
847 
848     if(lv_key != 0) {
849         app->keyboard_obj->input.keyboard.key = lv_key;
850         app->keyboard_obj->input.keyboard.state = lv_state;
851     }
852 }
853 
keyboard_handle_modifiers(void * data,struct wl_keyboard * keyboard,uint32_t serial,uint32_t mods_depressed,uint32_t mods_latched,uint32_t mods_locked,uint32_t group)854 static void keyboard_handle_modifiers(void * data, struct wl_keyboard * keyboard,
855                                       uint32_t serial, uint32_t mods_depressed,
856                                       uint32_t mods_latched, uint32_t mods_locked,
857                                       uint32_t group)
858 {
859     struct application * app = data;
860 
861     LV_UNUSED(serial);
862     LV_UNUSED(keyboard);
863 
864     /* If we're not using a keymap, then we don't handle PC-style modifiers */
865     if(!app->seat.xkb.keymap) {
866         return;
867     }
868 
869     xkb_state_update_mask(app->seat.xkb.state,
870                           mods_depressed, mods_latched, mods_locked, 0, 0, group);
871 }
872 
873 static const struct wl_keyboard_listener keyboard_listener = {
874     .keymap     = keyboard_handle_keymap,
875     .enter      = keyboard_handle_enter,
876     .leave      = keyboard_handle_leave,
877     .key        = keyboard_handle_key,
878     .modifiers  = keyboard_handle_modifiers,
879 };
880 
881 #if LV_USE_GESTURE_RECOGNITION
882 
touch_handle_down(void * data,struct wl_touch * wl_touch,uint32_t serial,uint32_t time,struct wl_surface * surface,int32_t id,wl_fixed_t x_w,wl_fixed_t y_w)883 static void touch_handle_down(void * data, struct wl_touch * wl_touch,
884                               uint32_t serial, uint32_t time, struct wl_surface * surface,
885                               int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
886 {
887     struct application * app = data;
888     uint8_t i;
889 
890     LV_UNUSED(id);
891     LV_UNUSED(time);
892     LV_UNUSED(serial);
893     LV_UNUSED(wl_touch);
894 
895     if(!surface) {
896         app->touch_obj = NULL;
897         return;
898     }
899 
900     /* Create the touch down event */
901     app->touch_obj = wl_surface_get_user_data(surface);
902     i = app->touch_obj->input.touch_event_cnt;
903 
904     app->touch_obj->input.touches[i].point.x = wl_fixed_to_int(x_w);
905     app->touch_obj->input.touches[i].point.y = wl_fixed_to_int(y_w);
906     app->touch_obj->input.touches[i].id = id;
907     app->touch_obj->input.touches[i].timestamp = time;
908     app->touch_obj->input.touches[i].state = LV_INDEV_STATE_PRESSED;
909     app->touch_obj->input.touch_event_cnt++;
910 
911 #if LV_WAYLAND_WINDOW_DECORATIONS
912     struct window * window = app->touch_obj->window;
913     switch(app->touch_obj->type) {
914         case OBJECT_TITLEBAR:
915 #if LV_WAYLAND_XDG_SHELL
916             if(window->xdg_toplevel) {
917                 xdg_toplevel_move(window->xdg_toplevel, app->wl_seat, serial);
918                 window->flush_pending = true;
919             }
920 #endif
921 #if LV_WAYLAND_WL_SHELL
922             if(window->wl_shell_surface) {
923                 wl_shell_surface_move(window->wl_shell_surface, app->wl_seat, serial);
924                 window->flush_pending = true;
925             }
926 #endif
927             break;
928         default:
929             break;
930     }
931 #endif
932 }
933 
touch_handle_up(void * data,struct wl_touch * wl_touch,uint32_t serial,uint32_t time,int32_t id)934 static void touch_handle_up(void * data, struct wl_touch * wl_touch,
935                             uint32_t serial, uint32_t time, int32_t id)
936 {
937     struct application * app = data;
938     uint8_t i;
939 
940     LV_UNUSED(serial);
941     LV_UNUSED(time);
942     LV_UNUSED(id);
943     LV_UNUSED(wl_touch);
944 
945 #if LV_USE_GESTURE_RECOGNITION
946     /* Create a released event */
947     i = app->touch_obj->input.touch_event_cnt;
948 
949     app->touch_obj->input.touches[i].point.x = 0;
950     app->touch_obj->input.touches[i].point.y = 0;
951     app->touch_obj->input.touches[i].id = id;
952     app->touch_obj->input.touches[i].timestamp = time;
953     app->touch_obj->input.touches[i].state = LV_INDEV_STATE_RELEASED;
954 
955     app->touch_obj->input.touch_event_cnt++;
956 #endif
957 
958 #if LV_WAYLAND_WINDOW_DECORATIONS
959     struct window * window = app->touch_obj->window;
960     switch(app->touch_obj->type) {
961         case OBJECT_BUTTON_CLOSE:
962             window->shall_close = true;
963             break;
964 #if LV_WAYLAND_XDG_SHELL
965         case OBJECT_BUTTON_MAXIMIZE:
966             if(window->xdg_toplevel) {
967                 if(window->maximized) {
968                     xdg_toplevel_unset_maximized(window->xdg_toplevel);
969                 }
970                 else {
971                     xdg_toplevel_set_maximized(window->xdg_toplevel);
972                 }
973                 window->maximized ^= true;
974             }
975             break;
976         case OBJECT_BUTTON_MINIMIZE:
977             if(window->xdg_toplevel) {
978                 xdg_toplevel_set_minimized(window->xdg_toplevel);
979                 window->flush_pending = true;
980             }
981 #endif /* LV_WAYLAND_XDG_SHELL */
982         default:
983             break;
984     }
985 #endif /* LV_WAYLAND_WINDOW_DECORATIONS */
986 
987 }
988 
touch_handle_motion(void * data,struct wl_touch * wl_touch,uint32_t time,int32_t id,wl_fixed_t x_w,wl_fixed_t y_w)989 static void touch_handle_motion(void * data, struct wl_touch * wl_touch,
990                                 uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
991 {
992     struct application * app = data;
993     lv_indev_touch_data_t * touch;
994     lv_indev_touch_data_t * cur;
995     uint8_t i;
996 
997     LV_UNUSED(time);
998     LV_UNUSED(id);
999     LV_UNUSED(wl_touch);
1000 
1001     /* Update the contact point of the corresponding id with the latest coordinate */
1002     touch = &app->touch_obj->input.touches[0];
1003     cur = NULL;
1004 
1005     for(i = 0; i < app->touch_obj->input.touch_event_cnt; i++) {
1006         if(touch->id == id) {
1007             cur = touch;
1008         }
1009         touch++;
1010     }
1011 
1012     if(cur == NULL) {
1013 
1014         i = app->touch_obj->input.touch_event_cnt;
1015         app->touch_obj->input.touches[i].point.x = wl_fixed_to_int(x_w);
1016         app->touch_obj->input.touches[i].point.y = wl_fixed_to_int(y_w);
1017         app->touch_obj->input.touches[i].id = id;
1018         app->touch_obj->input.touches[i].timestamp = time;
1019         app->touch_obj->input.touches[i].state = LV_INDEV_STATE_PRESSED;
1020         app->touch_obj->input.touch_event_cnt++;
1021 
1022     }
1023     else {
1024 
1025         cur->point.x = wl_fixed_to_int(x_w);
1026         cur->point.y = wl_fixed_to_int(y_w);
1027         cur->id = id;
1028         cur->timestamp = time;
1029     }
1030 
1031 }
1032 
touch_handle_frame(void * data,struct wl_touch * wl_touch)1033 static void touch_handle_frame(void * data, struct wl_touch * wl_touch)
1034 {
1035     LV_UNUSED(wl_touch);
1036     LV_UNUSED(data);
1037 
1038 }
1039 
touch_handle_cancel(void * data,struct wl_touch * wl_touch)1040 static void touch_handle_cancel(void * data, struct wl_touch * wl_touch)
1041 {
1042     LV_UNUSED(wl_touch);
1043     LV_UNUSED(data);
1044 }
1045 
1046 static const struct wl_touch_listener touch_listener = {
1047     .down   = touch_handle_down,
1048     .up     = touch_handle_up,
1049     .motion = touch_handle_motion,
1050     .frame  = touch_handle_frame,
1051     .cancel = touch_handle_cancel,
1052 };
1053 
1054 #endif /* END LV_USE_GESTURE_RECOGNITION */
1055 
seat_handle_capabilities(void * data,struct wl_seat * wl_seat,enum wl_seat_capability caps)1056 static void seat_handle_capabilities(void * data, struct wl_seat * wl_seat, enum wl_seat_capability caps)
1057 {
1058     struct application * app = data;
1059     struct seat * seat = &app->seat;
1060 
1061     if((caps & WL_SEAT_CAPABILITY_POINTER) && !seat->wl_pointer) {
1062         seat->wl_pointer = wl_seat_get_pointer(wl_seat);
1063         wl_pointer_add_listener(seat->wl_pointer, &pointer_listener, app);
1064         app->cursor_surface = wl_compositor_create_surface(app->compositor);
1065         if(!app->cursor_surface) {
1066             LV_LOG_WARN("failed to create cursor surface");
1067         }
1068     }
1069     else if(!(caps & WL_SEAT_CAPABILITY_POINTER) && seat->wl_pointer) {
1070         wl_pointer_destroy(seat->wl_pointer);
1071         if(app->cursor_surface) {
1072             wl_surface_destroy(app->cursor_surface);
1073         }
1074         seat->wl_pointer = NULL;
1075     }
1076 
1077     if((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !seat->wl_keyboard) {
1078         seat->wl_keyboard = wl_seat_get_keyboard(wl_seat);
1079         wl_keyboard_add_listener(seat->wl_keyboard, &keyboard_listener, app);
1080     }
1081     else if(!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && seat->wl_keyboard) {
1082         wl_keyboard_destroy(seat->wl_keyboard);
1083         seat->wl_keyboard = NULL;
1084     }
1085 
1086 #if LV_USE_GESTURE_RECOGNITION
1087     if((caps & WL_SEAT_CAPABILITY_TOUCH) && !seat->wl_touch) {
1088         seat->wl_touch = wl_seat_get_touch(wl_seat);
1089         wl_touch_add_listener(seat->wl_touch, &touch_listener, app);
1090     }
1091 #endif
1092     else if(!(caps & WL_SEAT_CAPABILITY_TOUCH) && seat->wl_touch) {
1093         wl_touch_destroy(seat->wl_touch);
1094         seat->wl_touch = NULL;
1095     }
1096 }
1097 
1098 static const struct wl_seat_listener seat_listener = {
1099     .capabilities = seat_handle_capabilities,
1100 };
1101 
draw_window(struct window * window,uint32_t width,uint32_t height)1102 static void draw_window(struct window * window, uint32_t width, uint32_t height)
1103 {
1104 
1105 #if LV_WAYLAND_WINDOW_DECORATIONS
1106     if(application.opt_disable_decorations == false) {
1107         int d;
1108         for(d = 0; d < NUM_DECORATIONS; d++) {
1109             window->decoration[d] = create_graphic_obj(&application, window, (FIRST_DECORATION + d), window->body);
1110             if(!window->decoration[d]) {
1111                 LV_LOG_ERROR("Failed to create decoration %d", d);
1112             }
1113         }
1114     }
1115 #endif
1116 
1117     /* First resize */
1118     if(!resize_window(window, width, height)) {
1119         LV_LOG_ERROR("Failed to resize window");
1120 #if LV_WAYLAND_XDG_SHELL
1121         if(window->xdg_toplevel) {
1122             xdg_toplevel_destroy(window->xdg_toplevel);
1123         }
1124 #endif
1125     }
1126 
1127     lv_refr_now(window->lv_disp);
1128 
1129 }
1130 
1131 #if LV_WAYLAND_WL_SHELL
wl_shell_handle_ping(void * data,struct wl_shell_surface * shell_surface,uint32_t serial)1132 static void wl_shell_handle_ping(void * data, struct wl_shell_surface * shell_surface, uint32_t serial)
1133 {
1134     return wl_shell_surface_pong(shell_surface, serial);
1135 }
1136 
wl_shell_handle_configure(void * data,struct wl_shell_surface * shell_surface,uint32_t edges,int32_t width,int32_t height)1137 static void wl_shell_handle_configure(void * data, struct wl_shell_surface * shell_surface,
1138                                       uint32_t edges, int32_t width, int32_t height)
1139 {
1140     struct window * window = (struct window *)data;
1141 
1142     LV_UNUSED(edges);
1143 
1144     if((width <= 0) || (height <= 0)) {
1145         return;
1146     }
1147     else if((width != window->width) || (height != window->height)) {
1148         window->resize_width = width;
1149         window->resize_height = height;
1150         window->resize_pending = true;
1151     }
1152 }
1153 
1154 static const struct wl_shell_surface_listener shell_surface_listener = {
1155     .ping       = wl_shell_handle_ping,
1156     .configure  =  wl_shell_handle_configure,
1157 };
1158 #endif
1159 
1160 #if LV_WAYLAND_XDG_SHELL
xdg_surface_handle_configure(void * data,struct xdg_surface * xdg_surface,uint32_t serial)1161 static void xdg_surface_handle_configure(void * data, struct xdg_surface * xdg_surface, uint32_t serial)
1162 {
1163     struct window * window = (struct window *)data;
1164 
1165     xdg_surface_ack_configure(xdg_surface, serial);
1166 
1167     if(window->body->surface_configured == false) {
1168         /* This branch is executed at launch */
1169         if(window->resize_pending == false) {
1170             /* Use the size passed to the create_window function */
1171             draw_window(window, window->width, window->height);
1172         }
1173         else {
1174 
1175             /* Handle early maximization or fullscreen, */
1176             /* by using the size communicated by the compositor */
1177             /* when the initial xdg configure event arrives  */
1178             draw_window(window, window->resize_width, window->resize_height);
1179             window->width = window->resize_width;
1180             window->height = window->resize_height;
1181             window->resize_pending = false;
1182         }
1183     }
1184     window->body->surface_configured = true;
1185 }
1186 
1187 static const struct xdg_surface_listener xdg_surface_listener = {
1188     .configure = xdg_surface_handle_configure,
1189 };
1190 
xdg_toplevel_handle_configure(void * data,struct xdg_toplevel * xdg_toplevel,int32_t width,int32_t height,struct wl_array * states)1191 static void xdg_toplevel_handle_configure(void * data, struct xdg_toplevel * xdg_toplevel,
1192                                           int32_t width, int32_t height, struct wl_array * states)
1193 {
1194     struct window * window = (struct window *)data;
1195 
1196     LV_UNUSED(xdg_toplevel);
1197     LV_UNUSED(states);
1198     LV_UNUSED(width);
1199     LV_UNUSED(height);
1200 
1201     LV_LOG_TRACE("w:%d h:%d", width, height);
1202     LV_LOG_TRACE("current body w:%d h:%d", window->body->width, window->body->height);
1203     LV_LOG_TRACE("window w:%d h:%d", window->width, window->height);
1204 
1205 
1206     if((width <= 0) || (height <= 0)) {
1207         LV_LOG_TRACE("will not resize to w:%d h:%d", width, height);
1208         return;
1209     }
1210 
1211     if((width != window->width) || (height != window->height)) {
1212         window->resize_width = width;
1213         window->resize_height = height;
1214         window->resize_pending = true;
1215         LV_LOG_TRACE("resize_pending is set, will resize to w:%d h:%d", width, height);
1216     }
1217     else {
1218         LV_LOG_TRACE("resize_pending not set w:%d h:%d", width, height);
1219     }
1220 }
1221 
xdg_toplevel_handle_close(void * data,struct xdg_toplevel * xdg_toplevel)1222 static void xdg_toplevel_handle_close(void * data, struct xdg_toplevel * xdg_toplevel)
1223 {
1224     struct window * window = (struct window *)data;
1225     window->shall_close = true;
1226 
1227     LV_UNUSED(xdg_toplevel);
1228 }
1229 
xdg_toplevel_handle_configure_bounds(void * data,struct xdg_toplevel * xdg_toplevel,int32_t width,int32_t height)1230 static void xdg_toplevel_handle_configure_bounds(void * data, struct xdg_toplevel * xdg_toplevel,
1231                                                  int32_t width, int32_t height)
1232 {
1233 
1234     LV_UNUSED(width);
1235     LV_UNUSED(height);
1236     LV_UNUSED(data);
1237     LV_UNUSED(xdg_toplevel);
1238 
1239     /* Optional: Could set window width/height upper bounds, however, currently
1240      *           we'll honor the set width/height.
1241      */
1242 }
1243 
1244 static const struct xdg_toplevel_listener xdg_toplevel_listener = {
1245     .configure = xdg_toplevel_handle_configure,
1246     .close = xdg_toplevel_handle_close,
1247     .configure_bounds = xdg_toplevel_handle_configure_bounds
1248 };
1249 
xdg_wm_base_ping(void * data,struct xdg_wm_base * xdg_wm_base,uint32_t serial)1250 static void xdg_wm_base_ping(void * data, struct xdg_wm_base * xdg_wm_base, uint32_t serial)
1251 {
1252     LV_UNUSED(data);
1253 
1254     xdg_wm_base_pong(xdg_wm_base, serial);
1255 
1256     return;
1257 }
1258 
1259 static const struct xdg_wm_base_listener xdg_wm_base_listener = {
1260     .ping = xdg_wm_base_ping
1261 };
1262 #endif
1263 
1264 
handle_global(void * data,struct wl_registry * registry,uint32_t name,const char * interface,uint32_t version)1265 static void handle_global(void * data, struct wl_registry * registry,
1266                           uint32_t name, const char * interface, uint32_t version)
1267 {
1268     struct application * app = data;
1269 
1270     LV_UNUSED(version);
1271     LV_UNUSED(data);
1272 
1273     if(strcmp(interface, wl_compositor_interface.name) == 0) {
1274         app->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1);
1275     }
1276     else if(strcmp(interface, wl_subcompositor_interface.name) == 0) {
1277         app->subcompositor = wl_registry_bind(registry, name, &wl_subcompositor_interface, 1);
1278     }
1279     else if(strcmp(interface, wl_shm_interface.name) == 0) {
1280         app->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
1281         wl_shm_add_listener(app->shm, &shm_listener, app);
1282         app->cursor_theme = wl_cursor_theme_load(NULL, 32, app->shm);
1283     }
1284     else if(strcmp(interface, wl_seat_interface.name) == 0) {
1285         app->wl_seat = wl_registry_bind(app->registry, name, &wl_seat_interface, 1);
1286         wl_seat_add_listener(app->wl_seat, &seat_listener, app);
1287     }
1288 #if LV_WAYLAND_WL_SHELL
1289     else if(strcmp(interface, wl_shell_interface.name) == 0) {
1290         app->wl_shell = wl_registry_bind(registry, name, &wl_shell_interface, 1);
1291     }
1292 #endif
1293 #if LV_WAYLAND_XDG_SHELL
1294     else if(strcmp(interface, xdg_wm_base_interface.name) == 0) {
1295         /* Explicitly support version 4 of the xdg protocol */
1296         app->xdg_wm = wl_registry_bind(app->registry, name, &xdg_wm_base_interface, 4);
1297         xdg_wm_base_add_listener(app->xdg_wm, &xdg_wm_base_listener, app);
1298     }
1299 #endif
1300 }
1301 
handle_global_remove(void * data,struct wl_registry * registry,uint32_t name)1302 static void handle_global_remove(void * data, struct wl_registry * registry, uint32_t name)
1303 {
1304 
1305     LV_UNUSED(data);
1306     LV_UNUSED(registry);
1307     LV_UNUSED(name);
1308 
1309 }
1310 
1311 static const struct wl_registry_listener registry_listener = {
1312     .global         = handle_global,
1313     .global_remove  = handle_global_remove
1314 };
1315 
handle_wl_buffer_release(void * data,struct wl_buffer * wl_buffer)1316 static void handle_wl_buffer_release(void * data, struct wl_buffer * wl_buffer)
1317 {
1318     const struct smm_buffer_properties * props;
1319     struct graphic_object * obj;
1320     struct window * window;
1321     smm_buffer_t * buf;
1322 
1323     buf = (smm_buffer_t *)data;
1324     props = SMM_BUFFER_PROPERTIES(buf);
1325     obj = SMM_GROUP_PROPERTIES(props->group)->tag[TAG_LOCAL];
1326     window = obj->window;
1327 
1328     LV_LOG_TRACE("releasing buffer %p wl_buffer %p w:%d h:%d frame: %d", (smm_buffer_t *)data, (void *)wl_buffer,
1329                  obj->width,
1330                  obj->height, window->frame_counter);
1331     smm_release((smm_buffer_t *)data);
1332 }
1333 
1334 static const struct wl_buffer_listener wl_buffer_listener = {
1335     .release = handle_wl_buffer_release,
1336 };
1337 
cache_clear(struct window * window)1338 static void cache_clear(struct window * window)
1339 {
1340     window->dmg_cache.start = window->dmg_cache.end;
1341     window->dmg_cache.size = 0;
1342 }
1343 
cache_purge(struct window * window,smm_buffer_t * buf)1344 static void cache_purge(struct window * window, smm_buffer_t * buf)
1345 {
1346     lv_area_t * next_dmg;
1347     smm_buffer_t * next_buf = smm_next(buf);
1348 
1349     /* Remove all damage areas up until start of next buffers damage */
1350     if(next_buf == NULL) {
1351         cache_clear(window);
1352     }
1353     else {
1354         next_dmg = SMM_BUFFER_PROPERTIES(next_buf)->tag[TAG_BUFFER_DAMAGE];
1355         while((window->dmg_cache.cache + window->dmg_cache.start) != next_dmg) {
1356             window->dmg_cache.start++;
1357             window->dmg_cache.start %= DMG_CACHE_CAPACITY;
1358             window->dmg_cache.size--;
1359         }
1360     }
1361 }
1362 
cache_add_area(struct window * window,smm_buffer_t * buf,const lv_area_t * area)1363 static void cache_add_area(struct window * window, smm_buffer_t * buf, const lv_area_t * area)
1364 {
1365     if(SMM_BUFFER_PROPERTIES(buf)->tag[TAG_BUFFER_DAMAGE] == NULL) {
1366         /* Buffer damage beyond cache capacity */
1367         goto done;
1368     }
1369 
1370     if((window->dmg_cache.start == window->dmg_cache.end) &&
1371        (window->dmg_cache.size)) {
1372         /* This buffer has more damage then the cache's capacity, so
1373          * clear cache and leave buffer damage unrecorded
1374          */
1375         cache_clear(window);
1376         SMM_TAG(buf, TAG_BUFFER_DAMAGE, NULL);
1377         goto done;
1378     }
1379 
1380     /* Add damage area to cache */
1381     memcpy(window->dmg_cache.cache + window->dmg_cache.end,
1382            area,
1383            sizeof(lv_area_t));
1384     window->dmg_cache.end++;
1385     window->dmg_cache.end %= DMG_CACHE_CAPACITY;
1386     window->dmg_cache.size++;
1387 
1388 done:
1389     return;
1390 }
1391 
cache_apply_areas(struct window * window,void * dest,void * src,smm_buffer_t * src_buf)1392 static void cache_apply_areas(struct window * window, void * dest, void * src, smm_buffer_t * src_buf)
1393 {
1394     unsigned long offset;
1395     unsigned char start;
1396     int32_t y;
1397     lv_area_t * dmg;
1398     lv_area_t * next_dmg;
1399     smm_buffer_t * next_buf = smm_next(src_buf);
1400     const struct smm_buffer_properties * props = SMM_BUFFER_PROPERTIES(src_buf);
1401     struct graphic_object * obj = SMM_GROUP_PROPERTIES(props->group)->tag[TAG_LOCAL];
1402     uint8_t bpp;
1403 
1404     if(next_buf == NULL) {
1405         next_dmg = (window->dmg_cache.cache + window->dmg_cache.end);
1406     }
1407     else {
1408         next_dmg = SMM_BUFFER_PROPERTIES(next_buf)->tag[TAG_BUFFER_DAMAGE];
1409     }
1410 
1411     bpp = lv_color_format_get_size(LV_COLOR_FORMAT_NATIVE);
1412 
1413     /* Apply all buffer damage areas */
1414     start = ((lv_area_t *)SMM_BUFFER_PROPERTIES(src_buf)->tag[TAG_BUFFER_DAMAGE] - window->dmg_cache.cache);
1415     while((window->dmg_cache.cache + start) != next_dmg) {
1416         /* Copy an area from source to destination (line-by-line) */
1417         dmg = (window->dmg_cache.cache + start);
1418         for(y = dmg->y1; y <= dmg->y2; y++) {
1419             offset = (dmg->x1 + (y * obj->width)) * bpp;
1420 
1421             memcpy(((char *)dest) + offset,
1422                    ((char *)src) + offset,
1423                    ((dmg->x2 - dmg->x1 + 1) * bpp));
1424         }
1425 
1426 
1427         start++;
1428         start %= DMG_CACHE_CAPACITY;
1429     }
1430 
1431 }
1432 
sme_new_pool(void * ctx,smm_pool_t * pool)1433 static bool sme_new_pool(void * ctx, smm_pool_t * pool)
1434 {
1435     struct wl_shm_pool * wl_pool;
1436     struct application * app = ctx;
1437     const struct smm_pool_properties * props = SMM_POOL_PROPERTIES(pool);
1438 
1439     LV_UNUSED(ctx);
1440 
1441     wl_pool = wl_shm_create_pool(app->shm,
1442                                  props->fd,
1443                                  props->size);
1444 
1445     SMM_TAG(pool, TAG_LOCAL, wl_pool);
1446     return (wl_pool == NULL);
1447 }
1448 
sme_expand_pool(void * ctx,smm_pool_t * pool)1449 static void sme_expand_pool(void * ctx, smm_pool_t * pool)
1450 {
1451     const struct smm_pool_properties * props = SMM_POOL_PROPERTIES(pool);
1452 
1453     LV_UNUSED(ctx);
1454 
1455     wl_shm_pool_resize(props->tag[TAG_LOCAL], props->size);
1456 }
1457 
sme_free_pool(void * ctx,smm_pool_t * pool)1458 static void sme_free_pool(void * ctx, smm_pool_t * pool)
1459 {
1460     struct wl_shm_pool * wl_pool = SMM_POOL_PROPERTIES(pool)->tag[TAG_LOCAL];
1461 
1462     LV_UNUSED(ctx);
1463 
1464     wl_shm_pool_destroy(wl_pool);
1465 }
1466 
sme_new_buffer(void * ctx,smm_buffer_t * buf)1467 static bool sme_new_buffer(void * ctx, smm_buffer_t * buf)
1468 {
1469     struct wl_buffer * wl_buf;
1470     bool fail_alloc = true;
1471     const struct smm_buffer_properties * props = SMM_BUFFER_PROPERTIES(buf);
1472     struct wl_shm_pool * wl_pool = SMM_POOL_PROPERTIES(props->pool)->tag[TAG_LOCAL];
1473     struct application * app = ctx;
1474     struct graphic_object * obj = SMM_GROUP_PROPERTIES(props->group)->tag[TAG_LOCAL];
1475     uint8_t bpp;
1476 
1477     LV_LOG_TRACE("create new buffer of width %d height %d", obj->width, obj->height);
1478 
1479     bpp = lv_color_format_get_size(LV_COLOR_FORMAT_NATIVE);
1480     wl_buf = wl_shm_pool_create_buffer(wl_pool,
1481                                        props->offset,
1482                                        obj->width,
1483                                        obj->height,
1484                                        obj->width * bpp,
1485                                        app->shm_format);
1486 
1487     if(wl_buf != NULL) {
1488         wl_buffer_add_listener(wl_buf, &wl_buffer_listener, buf);
1489         SMM_TAG(buf, TAG_LOCAL, wl_buf);
1490         SMM_TAG(buf, TAG_BUFFER_DAMAGE, NULL);
1491         fail_alloc = false;
1492     }
1493 
1494     return fail_alloc;
1495 }
1496 
sme_init_buffer(void * ctx,smm_buffer_t * buf)1497 static bool sme_init_buffer(void * ctx, smm_buffer_t * buf)
1498 {
1499     smm_buffer_t * src;
1500     void * src_base;
1501     bool fail_init = true;
1502     bool dmg_missing = false;
1503     void * buf_base = smm_map(buf);
1504     const struct smm_buffer_properties * props = SMM_BUFFER_PROPERTIES(buf);
1505     struct graphic_object * obj = SMM_GROUP_PROPERTIES(props->group)->tag[TAG_LOCAL];
1506     uint8_t bpp;
1507 
1508     LV_UNUSED(ctx);
1509 
1510     if(buf_base == NULL) {
1511         LV_LOG_ERROR("cannot map in buffer to initialize");
1512         goto done;
1513     }
1514 
1515     /* Determine if all subsequent buffers damage is recorded */
1516     for(src = smm_next(buf); src != NULL; src = smm_next(src)) {
1517         if(SMM_BUFFER_PROPERTIES(src)->tag[TAG_BUFFER_DAMAGE] == NULL) {
1518             dmg_missing = true;
1519             break;
1520         }
1521     }
1522 
1523     bpp = lv_color_format_get_size(LV_COLOR_FORMAT_NATIVE);
1524 
1525     if((smm_next(buf) == NULL) || dmg_missing) {
1526         /* Missing subsequent buffer damage, initialize by copying the most
1527          * recently acquired buffers data
1528          */
1529         src = smm_latest(props->group);
1530         if((src != NULL) &&
1531            (src != buf)) {
1532             /* Map and copy latest buffer data */
1533             src_base = smm_map(src);
1534             if(src_base == NULL) {
1535                 LV_LOG_ERROR("cannot map most recent buffer to copy");
1536                 goto done;
1537             }
1538 
1539             memcpy(buf_base,
1540                    src_base,
1541                    (obj->width * bpp) * obj->height);
1542         }
1543     }
1544     else {
1545         /* All subsequent buffers damage is recorded, initialize by applying
1546          * their damage to this buffer
1547          */
1548         for(src = smm_next(buf); src != NULL; src = smm_next(src)) {
1549             src_base = smm_map(src);
1550             if(src_base == NULL) {
1551                 LV_LOG_ERROR("cannot map source buffer to copy from");
1552                 goto done;
1553             }
1554 
1555             cache_apply_areas(obj->window, buf_base, src_base, src);
1556         }
1557 
1558         /* Purge out-of-date cached damage (up to and including next buffer) */
1559         src = smm_next(buf);
1560         if(src == NULL) {
1561             cache_purge(obj->window, src);
1562         }
1563     }
1564 
1565     fail_init = false;
1566 done:
1567     return fail_init;
1568 }
1569 
sme_free_buffer(void * ctx,smm_buffer_t * buf)1570 static void sme_free_buffer(void * ctx, smm_buffer_t * buf)
1571 {
1572     struct wl_buffer * wl_buf = SMM_BUFFER_PROPERTIES(buf)->tag[TAG_LOCAL];
1573 
1574     LV_UNUSED(ctx);
1575 
1576     wl_buffer_destroy(wl_buf);
1577 }
1578 
create_graphic_obj(struct application * app,struct window * window,enum object_type type,struct graphic_object * parent)1579 static struct graphic_object * create_graphic_obj(struct application * app, struct window * window,
1580                                                   enum object_type type,
1581                                                   struct graphic_object * parent)
1582 {
1583     struct graphic_object * obj;
1584 
1585     LV_UNUSED(parent);
1586 
1587     obj = lv_malloc(sizeof(*obj));
1588     LV_ASSERT_MALLOC(obj);
1589     if(!obj) {
1590         goto err_out;
1591     }
1592 
1593     lv_memset(obj, 0x00, sizeof(struct graphic_object));
1594 
1595     obj->surface = wl_compositor_create_surface(app->compositor);
1596     if(!obj->surface) {
1597         LV_LOG_ERROR("cannot create surface for graphic object");
1598         goto err_free;
1599     }
1600 
1601     obj->buffer_group = smm_create();
1602     if(obj->buffer_group == NULL) {
1603         LV_LOG_ERROR("cannot create buffer group for graphic object");
1604         goto err_destroy_surface;
1605     }
1606 
1607     obj->window = window;
1608     obj->type = type;
1609     obj->surface_configured = true;
1610     obj->pending_buffer = NULL;
1611     wl_surface_set_user_data(obj->surface, obj);
1612     SMM_TAG(obj->buffer_group, TAG_LOCAL, obj);
1613 
1614     return obj;
1615 
1616 err_destroy_surface:
1617     wl_surface_destroy(obj->surface);
1618 
1619 err_free:
1620     lv_free(obj);
1621 
1622 err_out:
1623     return NULL;
1624 }
1625 
destroy_graphic_obj(struct graphic_object * obj)1626 static void destroy_graphic_obj(struct graphic_object * obj)
1627 {
1628     if(obj->subsurface) {
1629         wl_subsurface_destroy(obj->subsurface);
1630     }
1631 
1632     wl_surface_destroy(obj->surface);
1633     smm_destroy(obj->buffer_group);
1634     lv_free(obj);
1635 }
1636 
1637 #if LV_WAYLAND_WINDOW_DECORATIONS
attach_decoration(struct window * window,struct graphic_object * decoration,smm_buffer_t * decoration_buffer,struct graphic_object * parent)1638 static bool attach_decoration(struct window * window, struct graphic_object * decoration,
1639                               smm_buffer_t * decoration_buffer, struct graphic_object * parent)
1640 {
1641     struct wl_buffer * wl_buf = SMM_BUFFER_PROPERTIES(decoration_buffer)->tag[TAG_LOCAL];
1642 
1643     int pos_x, pos_y;
1644 
1645     switch(decoration->type) {
1646         case OBJECT_TITLEBAR:
1647             pos_x = 0;
1648             pos_y = -TITLE_BAR_HEIGHT;
1649             break;
1650         case OBJECT_BUTTON_CLOSE:
1651             pos_x = parent->width - 1 * (BUTTON_MARGIN + BUTTON_SIZE);
1652             pos_y = -1 * (BUTTON_MARGIN + BUTTON_SIZE + (BORDER_SIZE / 2));
1653             break;
1654 #if LV_WAYLAND_XDG_SHELL
1655         case OBJECT_BUTTON_MAXIMIZE:
1656             pos_x = parent->width - 2 * (BUTTON_MARGIN + BUTTON_SIZE);
1657             pos_y = -1 * (BUTTON_MARGIN + BUTTON_SIZE + (BORDER_SIZE / 2));
1658             break;
1659         case OBJECT_BUTTON_MINIMIZE:
1660             pos_x = parent->width - 3 * (BUTTON_MARGIN + BUTTON_SIZE);
1661             pos_y = -1 * (BUTTON_MARGIN + BUTTON_SIZE + (BORDER_SIZE / 2));
1662             break;
1663 #endif
1664         case OBJECT_BORDER_TOP:
1665             pos_x = -BORDER_SIZE;
1666             pos_y = -(BORDER_SIZE + TITLE_BAR_HEIGHT);
1667             break;
1668         case OBJECT_BORDER_BOTTOM:
1669             pos_x = -BORDER_SIZE;
1670             pos_y = parent->height;
1671             break;
1672         case OBJECT_BORDER_LEFT:
1673             pos_x = -BORDER_SIZE;
1674             pos_y = -TITLE_BAR_HEIGHT;
1675             break;
1676         case OBJECT_BORDER_RIGHT:
1677             pos_x = parent->width;
1678             pos_y = -TITLE_BAR_HEIGHT;
1679             break;
1680         default:
1681             LV_ASSERT_MSG(0, "Invalid object type");
1682             return false;
1683     }
1684 
1685     /* Enable this, to make it function on weston 10.0.2 */
1686     /* It's not elegant but it forces weston to size the surfaces before */
1687     /* the conversion to a subsurface takes place */
1688 
1689     /* Likely related to this issue, some patches were merged into 10.0.0 */
1690     /* https://gitlab.freedesktop.org/wayland/weston/-/issues/446 */
1691     /* Moreover, it crashes on GNOME */
1692 
1693 #if 0
1694     wl_surface_attach(decoration->surface, wl_buf, 0, 0);
1695     wl_surface_commit(decoration->surface);
1696 #endif
1697 
1698     if(decoration->subsurface == NULL) {
1699         /* Create the subsurface only once */
1700 
1701         decoration->subsurface = wl_subcompositor_get_subsurface(window->application->subcompositor,
1702                                                                  decoration->surface,
1703                                                                  parent->surface);
1704         if(!decoration->subsurface) {
1705             LV_LOG_ERROR("cannot get subsurface for decoration");
1706             goto err_destroy_surface;
1707         }
1708     }
1709 
1710     wl_subsurface_set_position(decoration->subsurface, pos_x, pos_y);
1711     wl_surface_attach(decoration->surface, wl_buf, 0, 0);
1712     wl_surface_commit(decoration->surface);
1713 
1714     return true;
1715 
1716 err_destroy_surface:
1717     wl_surface_destroy(decoration->surface);
1718     decoration->surface = NULL;
1719 
1720     return false;
1721 }
1722 
1723 /*
1724  * Fills a buffer with a color
1725  * @description Used to draw the decorations, by writing directly to the SHM buffer,
1726  * most wayland compositors support the ARGB8888, XRGB8888, RGB565 formats
1727  *
1728  * For color depths usually not natively supported by wayland i.e RGB332, Grayscale
1729  * A conversion is performed to match the format of the SHM buffer read by the compositor.
1730  *
1731  * This function can also be used as a visual debugging aid to see how damage is applied
1732  *
1733  * @param pixels pointer to the buffer to fill
1734  * @param lv_color_t color the color that will be used for the fill
1735  * @param width width of the filled area
1736  * @param height height of the filled area
1737  *
1738  */
color_fill(void * pixels,lv_color_t color,uint32_t width,uint32_t height)1739 static void color_fill(void * pixels, lv_color_t color, uint32_t width, uint32_t height)
1740 {
1741 
1742     switch(application.shm_format) {
1743         case WL_SHM_FORMAT_ARGB8888:
1744             color_fill_XRGB8888(pixels, color, width, height);
1745             break;
1746         case WL_SHM_FORMAT_RGB565:
1747             color_fill_RGB565(pixels, color, width, height);
1748             break;
1749         default:
1750             LV_ASSERT_MSG(0, "Unsupported WL_SHM_FORMAT");
1751             break;
1752     }
1753 }
1754 
color_fill_XRGB8888(void * pixels,lv_color_t color,uint32_t width,uint32_t height)1755 static void color_fill_XRGB8888(void * pixels, lv_color_t color, uint32_t width, uint32_t height)
1756 {
1757     unsigned char * buf = pixels;
1758     unsigned char * buf_end;
1759 
1760     buf_end = (unsigned char *)((uint32_t *)buf + width * height);
1761 
1762     while(buf < buf_end) {
1763         *(buf++) = color.blue;
1764         *(buf++) = color.green;
1765         *(buf++) = color.red;
1766         *(buf++) = 0xFF;
1767     }
1768 
1769 }
1770 
color_fill_RGB565(void * pixels,lv_color_t color,uint32_t width,uint32_t height)1771 static void color_fill_RGB565(void * pixels, lv_color_t color, uint32_t width, uint32_t height)
1772 {
1773     uint16_t * buf = pixels;
1774     uint16_t * buf_end;
1775 
1776     buf_end = (uint16_t *)buf + width * height;
1777 
1778     while(buf < buf_end) {
1779         *(buf++) = lv_color_to_u16(color);
1780     }
1781 }
1782 
create_decoration(struct window * window,struct graphic_object * decoration,int window_width,int window_height)1783 static bool create_decoration(struct window * window,
1784                               struct graphic_object * decoration,
1785                               int window_width, int window_height)
1786 {
1787     smm_buffer_t * buf;
1788     void * buf_base;
1789     int x, y;
1790     lv_color_t * pixel;
1791     uint8_t bpp;
1792 
1793     switch(decoration->type) {
1794         case OBJECT_TITLEBAR:
1795             decoration->width = window_width;
1796             decoration->height = TITLE_BAR_HEIGHT;
1797             break;
1798         case OBJECT_BUTTON_CLOSE:
1799             decoration->width = BUTTON_SIZE;
1800             decoration->height = BUTTON_SIZE;
1801             break;
1802 #if LV_WAYLAND_XDG_SHELL
1803         case OBJECT_BUTTON_MAXIMIZE:
1804             decoration->width = BUTTON_SIZE;
1805             decoration->height = BUTTON_SIZE;
1806             break;
1807         case OBJECT_BUTTON_MINIMIZE:
1808             decoration->width = BUTTON_SIZE;
1809             decoration->height = BUTTON_SIZE;
1810             break;
1811 #endif
1812         case OBJECT_BORDER_TOP:
1813             decoration->width = window_width + 2 * (BORDER_SIZE);
1814             decoration->height = BORDER_SIZE;
1815             break;
1816         case OBJECT_BORDER_BOTTOM:
1817             decoration->width = window_width + 2 * (BORDER_SIZE);
1818             decoration->height = BORDER_SIZE;
1819             break;
1820         case OBJECT_BORDER_LEFT:
1821             decoration->width = BORDER_SIZE;
1822             decoration->height = window_height + TITLE_BAR_HEIGHT;
1823             break;
1824         case OBJECT_BORDER_RIGHT:
1825             decoration->width = BORDER_SIZE;
1826             decoration->height = window_height + TITLE_BAR_HEIGHT;
1827             break;
1828         default:
1829             LV_ASSERT_MSG(0, "Invalid object type");
1830             return false;
1831     }
1832 
1833     bpp = lv_color_format_get_size(LV_COLOR_FORMAT_NATIVE);
1834 
1835     LV_LOG_TRACE("decoration window %dx%d", decoration->width, decoration->height);
1836 
1837     smm_resize(decoration->buffer_group,
1838                (decoration->width * bpp) * decoration->height);
1839 
1840     buf = smm_acquire(decoration->buffer_group);
1841 
1842     if(buf == NULL) {
1843         LV_LOG_ERROR("cannot allocate buffer for decoration");
1844         return false;
1845     }
1846 
1847     buf_base = smm_map(buf);
1848     if(buf_base == NULL) {
1849         LV_LOG_ERROR("cannot map in allocated decoration buffer");
1850         smm_release(buf);
1851         return false;
1852     }
1853 
1854     switch(decoration->type) {
1855         case OBJECT_TITLEBAR:
1856             color_fill(buf_base, lv_color_make(0x66, 0x66, 0x66), decoration->width, decoration->height);
1857             break;
1858         case OBJECT_BUTTON_CLOSE:
1859             color_fill(buf_base, lv_color_make(0xCC, 0xCC, 0xCC), decoration->width, decoration->height);
1860             for(y = 0; y < decoration->height; y++) {
1861                 for(x = 0; x < decoration->width; x++) {
1862                     pixel = (lv_color_t *)((unsigned char *)buf_base + (y * (decoration->width * bpp)) + x * bpp);
1863                     if((x >= BUTTON_PADDING) && (x < decoration->width - BUTTON_PADDING)) {
1864                         if((x == y) || (x == decoration->width - 1 - y)) {
1865                             color_fill(pixel, lv_color_make(0x33, 0x33, 0x33), 1, 1);
1866                         }
1867                         else if((x == y - 1) || (x == decoration->width - y)) {
1868                             color_fill(pixel, lv_color_make(0x66, 0x66, 0x66), 1, 1);
1869                         }
1870                     }
1871                 }
1872             }
1873             break;
1874 #if LV_WAYLAND_XDG_SHELL
1875         case OBJECT_BUTTON_MAXIMIZE:
1876             color_fill(buf_base, lv_color_make(0xCC, 0xCC, 0xCC), decoration->width, decoration->height);
1877             for(y = 0; y < decoration->height; y++) {
1878                 for(x = 0; x < decoration->width; x++) {
1879                     pixel = (lv_color_t *)((unsigned char *)buf_base + (y * (decoration->width * bpp)) + x * bpp);
1880                     if(((x == BUTTON_PADDING) && (y >= BUTTON_PADDING) && (y < decoration->height - BUTTON_PADDING)) ||
1881                        ((x == (decoration->width - BUTTON_PADDING)) && (y >= BUTTON_PADDING) && (y <= decoration->height - BUTTON_PADDING)) ||
1882                        ((y == BUTTON_PADDING) && (x >= BUTTON_PADDING) && (x < decoration->width - BUTTON_PADDING)) ||
1883                        ((y == (BUTTON_PADDING + 1)) && (x >= BUTTON_PADDING) && (x < decoration->width - BUTTON_PADDING)) ||
1884                        ((y == (decoration->height - BUTTON_PADDING)) && (x >= BUTTON_PADDING) && (x < decoration->width - BUTTON_PADDING))) {
1885                         color_fill(pixel, lv_color_make(0x33, 0x33, 0x33), 1, 1);
1886                     }
1887                 }
1888             }
1889             break;
1890         case OBJECT_BUTTON_MINIMIZE:
1891             color_fill(buf_base, lv_color_make(0xCC, 0xCC, 0xCC), decoration->width, decoration->height);
1892             for(y = 0; y < decoration->height; y++) {
1893                 for(x = 0; x < decoration->width; x++) {
1894                     pixel = (lv_color_t *)((unsigned char *)buf_base + (y * (decoration->width * bpp)) + x * bpp);
1895                     if((x >= BUTTON_PADDING) && (x < decoration->width - BUTTON_PADDING) &&
1896                        (y > decoration->height - (2 * BUTTON_PADDING)) && (y < decoration->height - BUTTON_PADDING)) {
1897                         color_fill(pixel, lv_color_make(0x33, 0x33, 0x33), 1, 1);
1898                     }
1899                 }
1900             }
1901             break;
1902 #endif
1903         case OBJECT_BORDER_TOP:
1904         /* fallthrough */
1905         case OBJECT_BORDER_BOTTOM:
1906         /* fallthrough */
1907         case OBJECT_BORDER_LEFT:
1908         /* fallthrough */
1909         case OBJECT_BORDER_RIGHT:
1910             color_fill(buf_base, lv_color_make(0x66, 0x66, 0x66), decoration->width, decoration->height);
1911             break;
1912         default:
1913             LV_ASSERT_MSG(0, "Invalid object type");
1914             return false;
1915     }
1916 
1917     return attach_decoration(window, decoration, buf, window->body);
1918 }
1919 
detach_decoration(struct window * window,struct graphic_object * decoration)1920 static void detach_decoration(struct window * window,
1921                               struct graphic_object * decoration)
1922 {
1923 
1924     LV_UNUSED(window);
1925 
1926     if(decoration->subsurface) {
1927         wl_subsurface_destroy(decoration->subsurface);
1928         decoration->subsurface = NULL;
1929     }
1930 }
1931 #endif
1932 
resize_window(struct window * window,int width,int height)1933 static bool resize_window(struct window * window, int width, int height)
1934 {
1935     struct smm_buffer_t * body_buf1;
1936     struct smm_buffer_t * body_buf2;
1937     uint32_t stride;
1938     uint8_t bpp;
1939 #if LV_WAYLAND_WINDOW_DECORATIONS
1940     int b;
1941 #endif
1942 
1943 
1944     window->width = width;
1945     window->height = height;
1946 
1947 #if LV_WAYLAND_WINDOW_DECORATIONS
1948     if(!window->application->opt_disable_decorations && !window->fullscreen) {
1949         width -= (2 * BORDER_SIZE);
1950         height -= (TITLE_BAR_HEIGHT + (2 * BORDER_SIZE));
1951     }
1952 #endif
1953 
1954     bpp = lv_color_format_get_size(LV_COLOR_FORMAT_NATIVE);
1955 
1956     /* Update size for newly allocated buffers */
1957     smm_resize(window->body->buffer_group, ((width * bpp) * height) * 2);
1958 
1959     window->body->width = width;
1960     window->body->height = height;
1961 
1962     /* Pre-allocate two buffers for the window body here */
1963     body_buf1 = smm_acquire(window->body->buffer_group);
1964     body_buf2 = smm_acquire(window->body->buffer_group);
1965 
1966     if(smm_map(body_buf2) == NULL) {
1967         LV_LOG_ERROR("Cannot pre-allocate backing buffers for window body");
1968         wl_surface_destroy(window->body->surface);
1969         return false;
1970     }
1971 
1972     /* Moves the buffers to the the unused list of the group */
1973     smm_release(body_buf1);
1974     smm_release(body_buf2);
1975 
1976 
1977 #if LV_WAYLAND_WINDOW_DECORATIONS
1978     if(!window->application->opt_disable_decorations && !window->fullscreen) {
1979         for(b = 0; b < NUM_DECORATIONS; b++) {
1980             if(!create_decoration(window, window->decoration[b],
1981                                   window->body->width, window->body->height)) {
1982                 LV_LOG_ERROR("failed to create decoration %d", b);
1983             }
1984         }
1985 
1986     }
1987     else if(!window->application->opt_disable_decorations) {
1988         /* Entering fullscreen, detach decorations to prevent xdg_wm_base error 4 */
1989         /* requested geometry larger than the configured fullscreen state */
1990         for(b = 0; b < NUM_DECORATIONS; b++) {
1991             detach_decoration(window, window->decoration[b]);
1992         }
1993 
1994     }
1995 #endif
1996 
1997     LV_LOG_TRACE("resize window:%dx%d body:%dx%d frame: %d rendered: %d",
1998                  window->width, window->height,
1999                  window->body->width, window->body->height,
2000                  window->frame_counter, window->frame_done);
2001 
2002     width = window->body->width;
2003     height = window->body->height;
2004 
2005     if(window->lv_disp != NULL) {
2006         /* Resize draw buffer */
2007         stride = lv_draw_buf_width_to_stride(width,
2008                                              lv_display_get_color_format(window->lv_disp));
2009 
2010         window->lv_disp_draw_buf = lv_draw_buf_reshape(
2011                                        window->lv_disp_draw_buf,
2012                                        lv_display_get_color_format(window->lv_disp),
2013                                        width, height / LVGL_DRAW_BUFFER_DIV, stride);
2014 
2015         lv_display_set_resolution(window->lv_disp, width, height);
2016 
2017         window->body->input.pointer.x = LV_MIN((int32_t)window->body->input.pointer.x, (width - 1));
2018         window->body->input.pointer.y = LV_MIN((int32_t)window->body->input.pointer.y, (height - 1));
2019     }
2020 
2021     return true;
2022 }
2023 
2024 /* Create a window
2025  * @description Creates the graphical context for the window body, and then create a toplevel
2026  * wayland surface and commit it to obtain an XDG configuration event
2027  * @param width the height of the window w/decorations
2028  * @param height the width of the window w/decorations
2029 */
create_window(struct application * app,int width,int height,const char * title)2030 static struct window * create_window(struct application * app, int width, int height, const char * title)
2031 {
2032     struct window * window;
2033 
2034     window = lv_ll_ins_tail(&app->window_ll);
2035     LV_ASSERT_MALLOC(window);
2036     if(!window) {
2037         return NULL;
2038     }
2039 
2040     lv_memset(window, 0x00, sizeof(struct window));
2041 
2042     window->application = app;
2043 
2044     // Create wayland buffer and surface
2045     window->body = create_graphic_obj(app, window, OBJECT_WINDOW, NULL);
2046     window->width = width;
2047     window->height = height;
2048 
2049     if(!window->body) {
2050         LV_LOG_ERROR("cannot create window body");
2051         goto err_free_window;
2052     }
2053 
2054     // Create shell surface
2055     if(0) {
2056         // Needed for #if madness below
2057     }
2058 #if LV_WAYLAND_XDG_SHELL
2059     else if(app->xdg_wm) {
2060         window->xdg_surface = xdg_wm_base_get_xdg_surface(app->xdg_wm, window->body->surface);
2061         if(!window->xdg_surface) {
2062             LV_LOG_ERROR("cannot create XDG surface");
2063             goto err_destroy_surface;
2064         }
2065 
2066         xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener, window);
2067 
2068         window->xdg_toplevel = xdg_surface_get_toplevel(window->xdg_surface);
2069         if(!window->xdg_toplevel) {
2070             LV_LOG_ERROR("cannot get XDG toplevel surface");
2071             goto err_destroy_shell_surface;
2072         }
2073 
2074         xdg_toplevel_add_listener(window->xdg_toplevel, &xdg_toplevel_listener, window);
2075         xdg_toplevel_set_title(window->xdg_toplevel, title);
2076         xdg_toplevel_set_app_id(window->xdg_toplevel, title);
2077 
2078         // XDG surfaces need to be configured before a buffer can be attached.
2079         // An (XDG) surface commit (without an attached buffer) triggers this
2080         // configure event
2081         window->body->surface_configured = false;
2082     }
2083 #endif
2084 #if LV_WAYLAND_WL_SHELL
2085     else if(app->wl_shell) {
2086         window->wl_shell_surface = wl_shell_get_shell_surface(app->wl_shell, window->body->surface);
2087         if(!window->wl_shell_surface) {
2088             LV_LOG_ERROR("cannot create WL shell surface");
2089             goto err_destroy_surface;
2090         }
2091 
2092         wl_shell_surface_add_listener(window->wl_shell_surface, &shell_surface_listener, window);
2093         wl_shell_surface_set_toplevel(window->wl_shell_surface);
2094         wl_shell_surface_set_title(window->wl_shell_surface, title);
2095 
2096         /* For wl_shell, just draw the window, weston doesn't send it */
2097         draw_window(window, window->width, window->height);
2098     }
2099 #endif
2100     else {
2101         LV_LOG_ERROR("No shell available");
2102         goto err_destroy_surface;
2103     }
2104 
2105 
2106     return window;
2107 
2108 err_destroy_shell_surface:
2109 #if LV_WAYLAND_WL_SHELL
2110     if(window->wl_shell_surface) {
2111         wl_shell_surface_destroy(window->wl_shell_surface);
2112     }
2113 #endif
2114 #if LV_WAYLAND_XDG_SHELL
2115     if(window->xdg_surface) {
2116         xdg_surface_destroy(window->xdg_surface);
2117     }
2118 #endif
2119 
2120 err_destroy_surface:
2121     wl_surface_destroy(window->body->surface);
2122 
2123 err_free_window:
2124     lv_ll_remove(&app->window_ll, window);
2125     lv_free(window);
2126     return NULL;
2127 }
2128 
destroy_window(struct window * window)2129 static void destroy_window(struct window * window)
2130 {
2131     if(!window) {
2132         return;
2133     }
2134 
2135 #if LV_WAYLAND_WL_SHELL
2136     if(window->wl_shell_surface) {
2137         wl_shell_surface_destroy(window->wl_shell_surface);
2138     }
2139 #endif
2140 #if LV_WAYLAND_XDG_SHELL
2141     if(window->xdg_toplevel) {
2142         xdg_toplevel_destroy(window->xdg_toplevel);
2143         xdg_surface_destroy(window->xdg_surface);
2144     }
2145 #endif
2146 
2147 #if LV_WAYLAND_WINDOW_DECORATIONS
2148     int b;
2149     for(b = 0; b < NUM_DECORATIONS; b++) {
2150         if(window->decoration[b]) {
2151             destroy_graphic_obj(window->decoration[b]);
2152             window->decoration[b] = NULL;
2153         }
2154     }
2155 #endif
2156 
2157     destroy_graphic_obj(window->body);
2158 }
2159 
_lv_wayland_flush(lv_display_t * disp,const lv_area_t * area,unsigned char * color_p)2160 static void _lv_wayland_flush(lv_display_t * disp, const lv_area_t * area, unsigned char * color_p)
2161 {
2162     void * buf_base;
2163     struct wl_buffer * wl_buf;
2164     int32_t src_width;
2165     int32_t src_height;
2166     struct window * window;
2167     struct application * app;
2168     smm_buffer_t * buf;
2169     struct wl_callback * cb;
2170     lv_display_rotation_t rot;
2171     uint8_t bpp;
2172     int32_t x;
2173     int32_t y;
2174     int32_t w;
2175     int32_t h;
2176     int32_t hres;
2177     int32_t vres;
2178 
2179     window = lv_display_get_user_data(disp);
2180     app = window->application;
2181     buf = window->body->pending_buffer;
2182     src_width = lv_area_get_width(area);
2183     src_height = lv_area_get_height(area);
2184     bpp = lv_color_format_get_size(LV_COLOR_FORMAT_NATIVE);
2185 
2186     rot = lv_display_get_rotation(disp);
2187     w = lv_display_get_horizontal_resolution(disp);
2188     h = lv_display_get_vertical_resolution(disp);
2189 
2190     /* TODO actually test what happens if the rotation is 90 or 270 or 180 ? */
2191     hres = (rot == LV_DISPLAY_ROTATION_0) ? w : h;
2192     vres = (rot == LV_DISPLAY_ROTATION_0) ? h : w;
2193 
2194     /* If window has been / is being closed, or is not visible, skip flush */
2195     if(window->closed || window->shall_close) {
2196         goto skip;
2197     }
2198     /* Skip if the area is out the screen */
2199     else if((area->x2 < 0) || (area->y2 < 0) || (area->x1 > hres - 1) || (area->y1 > vres - 1)) {
2200         goto skip;
2201     }
2202 
2203     /* Acquire and map a buffer to attach/commit to surface */
2204     if(buf == NULL) {
2205         buf = smm_acquire(window->body->buffer_group);
2206         if(buf == NULL) {
2207             LV_LOG_ERROR("cannot acquire a window body buffer");
2208             goto skip;
2209         }
2210 
2211         window->body->pending_buffer = buf;
2212         SMM_TAG(buf,
2213                 TAG_BUFFER_DAMAGE,
2214                 window->dmg_cache.cache + window->dmg_cache.end);
2215     }
2216 
2217     buf_base = smm_map(buf);
2218     if(buf_base == NULL) {
2219         LV_LOG_ERROR("cannot map in window body buffer");
2220         goto skip;
2221     }
2222 
2223     /* Modify specified area in buffer */
2224     for(y = 0; y < src_height; ++y) {
2225         if(app->shm_format == WL_SHM_FORMAT_ARGB8888) {
2226             for(x = 0; x < src_width; ++x) {
2227                 lv_color_premultiply((lv_color32_t *)color_p + x);
2228             }
2229         }
2230         memcpy(((char *)buf_base) + ((((area->y1 + y) * hres) + area->x1) * bpp),
2231                color_p, src_width * bpp);
2232         color_p += src_width * bpp;
2233     }
2234 
2235     /* Mark surface damage */
2236     wl_surface_damage(window->body->surface,
2237                       area->x1,
2238                       area->y1,
2239                       src_width,
2240                       src_height);
2241 
2242     cache_add_area(window, buf, area);
2243 
2244 
2245     if(lv_display_flush_is_last(disp)) {
2246         /* Finally, attach buffer and commit to surface */
2247         wl_buf = SMM_BUFFER_PROPERTIES(buf)->tag[TAG_LOCAL];
2248         wl_surface_attach(window->body->surface, wl_buf, 0, 0);
2249         wl_surface_commit(window->body->surface);
2250         window->body->pending_buffer = NULL;
2251         window->frame_done = false;
2252 
2253         cb = wl_surface_frame(window->body->surface);
2254         wl_callback_add_listener(cb, &wl_surface_frame_listener, window->body);
2255         LV_LOG_TRACE("last flush frame: %d", window->frame_counter);
2256 
2257         window->flush_pending = true;
2258     }
2259 
2260     lv_display_flush_ready(disp);
2261     return;
2262 skip:
2263     if(buf != NULL) {
2264         /* Cleanup any intermediate state (in the event that this flush being
2265          * skipped is in the middle of a flush sequence)
2266          */
2267         cache_clear(window);
2268         SMM_TAG(buf, TAG_BUFFER_DAMAGE, NULL);
2269         smm_release(buf);
2270         window->body->pending_buffer = NULL;
2271     }
2272 }
2273 
_lv_wayland_handle_input(void)2274 static void _lv_wayland_handle_input(void)
2275 {
2276     int prepare_read = wl_display_prepare_read(application.display);
2277 
2278     while(prepare_read != 0) {
2279         wl_display_dispatch_pending(application.display);
2280     }
2281 
2282     wl_display_read_events(application.display);
2283     wl_display_dispatch_pending(application.display);
2284 }
2285 
_lv_wayland_handle_output(void)2286 static void _lv_wayland_handle_output(void)
2287 {
2288     struct window * window;
2289     bool shall_flush = application.cursor_flush_pending;
2290 
2291     LV_LL_READ(&application.window_ll, window) {
2292         if((window->shall_close) && (window->close_cb != NULL)) {
2293             window->shall_close = window->close_cb(window->lv_disp);
2294         }
2295 
2296         if(window->closed) {
2297             continue;
2298         }
2299         else if(window->shall_close) {
2300             window->closed = true;
2301             window->shall_close = false;
2302             shall_flush = true;
2303 
2304             window->body->input.pointer.x = 0;
2305             window->body->input.pointer.y = 0;
2306             window->body->input.pointer.left_button = LV_INDEV_STATE_RELEASED;
2307             window->body->input.pointer.right_button = LV_INDEV_STATE_RELEASED;
2308             window->body->input.pointer.wheel_button = LV_INDEV_STATE_RELEASED;
2309             window->body->input.pointer.wheel_diff = 0;
2310             if(window->application->pointer_obj == window->body) {
2311                 window->application->pointer_obj = NULL;
2312             }
2313 
2314             window->body->input.keyboard.key = 0;
2315             window->body->input.keyboard.state = LV_INDEV_STATE_RELEASED;
2316             if(window->application->keyboard_obj == window->body) {
2317                 window->application->keyboard_obj = NULL;
2318             }
2319             destroy_window(window);
2320         }
2321 
2322         shall_flush |= window->flush_pending;
2323     }
2324 
2325     if(shall_flush) {
2326         if(wl_display_flush(application.display) == -1) {
2327             if(errno != EAGAIN) {
2328                 LV_LOG_ERROR("failed to flush wayland display");
2329             }
2330         }
2331         else {
2332             /* All data flushed */
2333             application.cursor_flush_pending = false;
2334             LV_LL_READ(&application.window_ll, window) {
2335                 window->flush_pending = false;
2336             }
2337         }
2338     }
2339 }
2340 
_lv_wayland_pointer_read(lv_indev_t * drv,lv_indev_data_t * data)2341 static void _lv_wayland_pointer_read(lv_indev_t * drv, lv_indev_data_t * data)
2342 {
2343     struct window * window = lv_display_get_user_data(lv_indev_get_display(drv));
2344 
2345     if(!window || window->closed) {
2346         return;
2347     }
2348 
2349     data->point.x = window->body->input.pointer.x;
2350     data->point.y = window->body->input.pointer.y;
2351     data->state = window->body->input.pointer.left_button;
2352 }
2353 
_lv_wayland_pointeraxis_read(lv_indev_t * drv,lv_indev_data_t * data)2354 static void _lv_wayland_pointeraxis_read(lv_indev_t * drv, lv_indev_data_t * data)
2355 {
2356     struct window * window = lv_display_get_user_data(lv_indev_get_display(drv));
2357 
2358     if(!window || window->closed) {
2359         return;
2360     }
2361 
2362     data->state = window->body->input.pointer.wheel_button;
2363     data->enc_diff = window->body->input.pointer.wheel_diff;
2364 
2365     window->body->input.pointer.wheel_diff = 0;
2366 }
2367 
_lv_wayland_keyboard_read(lv_indev_t * drv,lv_indev_data_t * data)2368 static void _lv_wayland_keyboard_read(lv_indev_t * drv, lv_indev_data_t * data)
2369 {
2370     struct window * window = lv_display_get_user_data(lv_indev_get_display(drv));
2371     if(!window || window->closed) {
2372         return;
2373     }
2374 
2375     data->key = window->body->input.keyboard.key;
2376     data->state = window->body->input.keyboard.state;
2377 }
2378 
2379 #if LV_USE_GESTURE_RECOGNITION
2380 
_lv_wayland_touch_read(lv_indev_t * drv,lv_indev_data_t * data)2381 static void _lv_wayland_touch_read(lv_indev_t * drv, lv_indev_data_t * data)
2382 {
2383 
2384     struct window * window = lv_display_get_user_data(lv_indev_get_display(drv));
2385     lv_indev_gesture_recognizer_t * recognizer;
2386 
2387     if(!window || window->closed) {
2388         return;
2389     }
2390 
2391     /* Collect touches if there are any - send them to the gesture recognizer */
2392     recognizer = &window->body->input.recognizer;
2393 
2394     LV_LOG_TRACE("collected touch events: %d", window->body->input.touch_event_cnt);
2395 
2396     lv_indev_gesture_detect_pinch(recognizer, &window->body->input.touches[0],
2397                                   window->body->input.touch_event_cnt);
2398 
2399     window->body->input.touch_event_cnt = 0;
2400 
2401     /* Set the gesture information, before returning to LVGL */
2402     lv_indev_set_gesture_data(data, recognizer);
2403 
2404 }
2405 
2406 #endif /* END LV_USE_GESTURE_RECOGNITION */
2407 
2408 /**********************
2409  *   GLOBAL FUNCTIONS
2410  **********************/
2411 
2412 /**
2413  * Initialize Wayland driver
2414  */
wayland_init(void)2415 static void wayland_init(void)
2416 {
2417     struct smm_events evs = {
2418         NULL,
2419         sme_new_pool,
2420         sme_expand_pool,
2421         sme_free_pool,
2422         sme_new_buffer,
2423         sme_init_buffer,
2424         sme_free_buffer
2425     };
2426 
2427     application.xdg_runtime_dir = getenv("XDG_RUNTIME_DIR");
2428     LV_ASSERT_MSG(application.xdg_runtime_dir, "cannot get XDG_RUNTIME_DIR");
2429 
2430     // Create XKB context
2431     application.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
2432     LV_ASSERT_MSG(application.xkb_context, "failed to create XKB context");
2433     if(application.xkb_context == NULL) {
2434         return;
2435     }
2436 
2437     // Connect to Wayland display
2438     application.display = wl_display_connect(NULL);
2439     LV_ASSERT_MSG(application.display, "failed to connect to Wayland server");
2440     if(application.display == NULL) {
2441         return;
2442     }
2443 
2444     /* Add registry listener and wait for registry reception */
2445     application.shm_format = SHM_FORMAT_UNKNOWN;
2446     application.registry = wl_display_get_registry(application.display);
2447     wl_registry_add_listener(application.registry, &registry_listener, &application);
2448     wl_display_dispatch(application.display);
2449     wl_display_roundtrip(application.display);
2450 
2451     LV_ASSERT_MSG(application.compositor, "Wayland compositor not available");
2452     if(application.compositor == NULL) {
2453         return;
2454     }
2455 
2456     LV_ASSERT_MSG(application.shm, "Wayland SHM not available");
2457     if(application.shm == NULL) {
2458         return;
2459     }
2460 
2461     LV_ASSERT_MSG((application.shm_format != SHM_FORMAT_UNKNOWN), "WL_SHM_FORMAT not available");
2462     if(application.shm_format == SHM_FORMAT_UNKNOWN) {
2463         LV_LOG_TRACE("Unable to match a suitable SHM format for selected LVGL color depth");
2464         return;
2465     }
2466 
2467     smm_init(&evs);
2468     smm_setctx(&application);
2469 
2470 #ifdef LV_WAYLAND_WINDOW_DECORATIONS
2471     const char * env_disable_decorations = getenv("LV_WAYLAND_DISABLE_WINDOWDECORATION");
2472     application.opt_disable_decorations = ((env_disable_decorations != NULL) &&
2473                                            (env_disable_decorations[0] != '0'));
2474 #endif
2475 
2476     lv_ll_init(&application.window_ll, sizeof(struct window));
2477 
2478     lv_tick_set_cb(tick_get_cb);
2479 
2480     /* Used to wait for events when the window is minimized or hidden */
2481     application.wayland_pfd.fd = wl_display_get_fd(application.display);
2482     application.wayland_pfd.events = POLLIN;
2483 
2484 }
2485 
2486 /**
2487  * De-initialize Wayland driver
2488  */
wayland_deinit(void)2489 static void wayland_deinit(void)
2490 {
2491     struct window * window = NULL;
2492 
2493     LV_LL_READ(&application.window_ll, window) {
2494         if(!window->closed) {
2495             destroy_window(window);
2496         }
2497 
2498         lv_draw_buf_destroy(window->lv_disp_draw_buf);
2499         lv_display_delete(window->lv_disp);
2500     }
2501 
2502     smm_deinit();
2503 
2504     if(application.shm) {
2505         wl_shm_destroy(application.shm);
2506     }
2507 
2508 #if LV_WAYLAND_XDG_SHELL
2509     if(application.xdg_wm) {
2510         xdg_wm_base_destroy(application.xdg_wm);
2511     }
2512 #endif
2513 
2514 #if LV_WAYLAND_WL_SHELL
2515     if(application.wl_shell) {
2516         wl_shell_destroy(application.wl_shell);
2517     }
2518 #endif
2519 
2520     if(application.wl_seat) {
2521         wl_seat_destroy(application.wl_seat);
2522     }
2523 
2524     if(application.subcompositor) {
2525         wl_subcompositor_destroy(application.subcompositor);
2526     }
2527 
2528     if(application.compositor) {
2529         wl_compositor_destroy(application.compositor);
2530     }
2531 
2532     wl_registry_destroy(application.registry);
2533     wl_display_flush(application.display);
2534     wl_display_disconnect(application.display);
2535 
2536     lv_ll_clear(&application.window_ll);
2537 
2538 }
2539 
tick_get_cb(void)2540 static uint32_t tick_get_cb(void)
2541 {
2542     struct timespec t;
2543     clock_gettime(CLOCK_MONOTONIC, &t);
2544     uint64_t time_ms = t.tv_sec * 1000 + (t.tv_nsec / 1000000);
2545     return time_ms;
2546 }
2547 
2548 /**
2549  * Get Wayland display file descriptor
2550  * @return Wayland display file descriptor
2551  */
lv_wayland_get_fd(void)2552 int lv_wayland_get_fd(void)
2553 {
2554     return wl_display_get_fd(application.display);
2555 }
2556 
2557 /**
2558  * Create wayland window
2559  * @param hor_res initial horizontal window body size in pixels
2560  * @param ver_res initial vertical window body size in pixels
2561  * @param title window title
2562  * @param close_cb function to be called when the window gets closed by the user (optional)
2563  * @return new display backed by a Wayland window, or NULL on error
2564  */
lv_wayland_window_create(uint32_t hor_res,uint32_t ver_res,char * title,lv_wayland_display_close_f_t close_cb)2565 lv_display_t * lv_wayland_window_create(uint32_t hor_res, uint32_t ver_res, char * title,
2566                                         lv_wayland_display_close_f_t close_cb)
2567 {
2568     struct window * window;
2569     int32_t window_width;
2570     int32_t window_height;
2571     int32_t stride;
2572 
2573     wayland_init();
2574 
2575     window_width = hor_res;
2576     window_height = ver_res;
2577 
2578 #if LV_WAYLAND_WINDOW_DECORATIONS
2579 
2580     /* Decorations are enabled, calculate the body size */
2581     if(!application.opt_disable_decorations) {
2582         window_width = hor_res + (2 * BORDER_SIZE);
2583         window_height = ver_res + (TITLE_BAR_HEIGHT + (2 * BORDER_SIZE));
2584     }
2585 
2586 #endif
2587 
2588     window = create_window(&application, window_width, window_height, title);
2589     if(!window) {
2590         LV_LOG_ERROR("failed to create wayland window");
2591         return NULL;
2592     }
2593 
2594     window->close_cb = close_cb;
2595 
2596     /* Initialize display driver */
2597     window->lv_disp = lv_display_create(hor_res, ver_res);
2598     if(window->lv_disp == NULL) {
2599         LV_LOG_ERROR("failed to create lvgl display");
2600         return NULL;
2601     }
2602 
2603     stride = lv_draw_buf_width_to_stride(hor_res,
2604                                          lv_display_get_color_format(window->lv_disp));
2605 
2606     window->lv_disp_draw_buf = lv_draw_buf_create(
2607                                    hor_res,
2608                                    ver_res / LVGL_DRAW_BUFFER_DIV,
2609                                    lv_display_get_color_format(window->lv_disp),
2610                                    stride);
2611 
2612 
2613     lv_display_set_draw_buffers(window->lv_disp, window->lv_disp_draw_buf, NULL);
2614     lv_display_set_render_mode(window->lv_disp, LV_DISPLAY_RENDER_MODE_PARTIAL);
2615     lv_display_set_flush_cb(window->lv_disp, _lv_wayland_flush);
2616     lv_display_set_user_data(window->lv_disp, window);
2617 
2618     /* Register input */
2619     window->lv_indev_pointer = lv_indev_create();
2620     lv_indev_set_type(window->lv_indev_pointer, LV_INDEV_TYPE_POINTER);
2621     lv_indev_set_read_cb(window->lv_indev_pointer, _lv_wayland_pointer_read);
2622     lv_indev_set_display(window->lv_indev_pointer, window->lv_disp);
2623 
2624     if(!window->lv_indev_pointer) {
2625         LV_LOG_ERROR("failed to register pointer indev");
2626     }
2627 
2628     window->lv_indev_pointeraxis = lv_indev_create();
2629     lv_indev_set_type(window->lv_indev_pointeraxis, LV_INDEV_TYPE_ENCODER);
2630     lv_indev_set_read_cb(window->lv_indev_pointeraxis, _lv_wayland_pointeraxis_read);
2631     lv_indev_set_display(window->lv_indev_pointeraxis, window->lv_disp);
2632 
2633     if(!window->lv_indev_pointeraxis) {
2634         LV_LOG_ERROR("failed to register pointeraxis indev");
2635     }
2636 
2637 #if LV_USE_GESTURE_RECOGNITION
2638 
2639     window->lv_indev_touch = lv_indev_create();
2640     lv_indev_set_type(window->lv_indev_touch, LV_INDEV_TYPE_POINTER);
2641     lv_indev_set_read_cb(window->lv_indev_touch, _lv_wayland_touch_read);
2642     lv_indev_set_display(window->lv_indev_touch, window->lv_disp);
2643 
2644     if(!window->lv_indev_touch) {
2645         LV_LOG_ERROR("failed to register touch indev");
2646     }
2647 
2648 #endif /* END LV_USE_GESTURE_RECOGNITION */
2649 
2650     window->lv_indev_keyboard = lv_indev_create();
2651     lv_indev_set_type(window->lv_indev_keyboard, LV_INDEV_TYPE_KEYPAD);
2652     lv_indev_set_read_cb(window->lv_indev_keyboard, _lv_wayland_keyboard_read);
2653     lv_indev_set_display(window->lv_indev_keyboard, window->lv_disp);
2654 
2655     if(!window->lv_indev_keyboard) {
2656         LV_LOG_ERROR("failed to register keyboard indev");
2657     }
2658 
2659     return window->lv_disp;
2660 }
2661 
2662 /**
2663  * Close wayland window
2664  * @param disp LVGL display using window to be closed
2665  */
lv_wayland_window_close(lv_display_t * disp)2666 void lv_wayland_window_close(lv_display_t * disp)
2667 {
2668     struct window * window = lv_display_get_user_data(disp);
2669     if(!window || window->closed) {
2670         return;
2671     }
2672     window->shall_close = true;
2673     window->close_cb = NULL;
2674     wayland_deinit();
2675 }
2676 
2677 /**
2678  * Check if a Wayland window is open on the specified display. Otherwise (if
2679  * argument is NULL), check if any Wayland window is open.
2680  * @return true if window open, false otherwise
2681  */
lv_wayland_window_is_open(lv_display_t * disp)2682 bool lv_wayland_window_is_open(lv_display_t * disp)
2683 {
2684     struct window * window;
2685     bool open = false;
2686 
2687     if(disp == NULL) {
2688         LV_LL_READ(&application.window_ll, window) {
2689             if(!window->closed) {
2690                 open = true;
2691                 break;
2692             }
2693         }
2694     }
2695     else {
2696         window = lv_display_get_user_data(disp);
2697         open = (!window->closed);
2698     }
2699 
2700     return open;
2701 }
2702 /**
2703  * Set/unset window maximization mode
2704  * @description Maximization is nearly the same as fullscreen, except
2705  * window decorations and the compositor's panels must remain visible
2706  * @param disp LVGL display using window to be set/unset maximization
2707  * @param Maximization requested status (true = maximized)
2708  */
lv_wayland_window_set_maximized(lv_display_t * disp,bool maximized)2709 void lv_wayland_window_set_maximized(lv_display_t * disp, bool maximized)
2710 {
2711     struct window * window = lv_display_get_user_data(disp);
2712     if(!window || window->closed) {
2713         return;
2714     }
2715 
2716     if(window->maximized != maximized) {
2717 
2718 #if LV_WAYLAND_WL_SHELL
2719         if(window->wl_shell_surface) {
2720             if(maximized) {
2721                 /* Maximizing the wl_shell is possible, but requires binding to wl_output */
2722                 /* The wl_shell has been deperacted */
2723                 //wl_shell_surface_set_maximized(window->wl_shell_surface);
2724             }
2725             else {
2726                 wl_shell_surface_set_toplevel(window->wl_shell_surface);
2727             }
2728             window->maximized = maximized;
2729             window->flush_pending = true;
2730         }
2731 #endif
2732 
2733 #if LV_WAYLAND_XDG_SHELL
2734         if(window->xdg_toplevel) {
2735             if(maximized) {
2736                 xdg_toplevel_set_maximized(window->xdg_toplevel);
2737             }
2738             else {
2739                 xdg_toplevel_unset_maximized(window->xdg_toplevel);
2740             }
2741 
2742             window->maximized = maximized;
2743             window->flush_pending = true;
2744         }
2745 #endif
2746     }
2747 }
2748 
2749 /**
2750  * Set/unset window fullscreen mode
2751  * @param disp LVGL display using window to be set/unset fullscreen
2752  * @param fullscreen requested status (true = fullscreen)
2753  */
lv_wayland_window_set_fullscreen(lv_display_t * disp,bool fullscreen)2754 void lv_wayland_window_set_fullscreen(lv_display_t * disp, bool fullscreen)
2755 {
2756     struct window * window = lv_display_get_user_data(disp);
2757     if(!window || window->closed) {
2758         return;
2759     }
2760 
2761     if(window->fullscreen != fullscreen) {
2762         if(0) {
2763             // Needed for #if madness below
2764         }
2765 #if LV_WAYLAND_XDG_SHELL
2766         else if(window->xdg_toplevel) {
2767             if(fullscreen) {
2768                 xdg_toplevel_set_fullscreen(window->xdg_toplevel, NULL);
2769             }
2770             else {
2771                 xdg_toplevel_unset_fullscreen(window->xdg_toplevel);
2772             }
2773             window->fullscreen = fullscreen;
2774             window->flush_pending = true;
2775         }
2776 #endif
2777 #if LV_WAYLAND_WL_SHELL
2778         else if(window->wl_shell_surface) {
2779             if(fullscreen) {
2780                 wl_shell_surface_set_fullscreen(window->wl_shell_surface,
2781                                                 WL_SHELL_SURFACE_FULLSCREEN_METHOD_SCALE,
2782                                                 0, NULL);
2783             }
2784             else {
2785                 wl_shell_surface_set_toplevel(window->wl_shell_surface);
2786             }
2787             window->fullscreen = fullscreen;
2788             window->flush_pending = true;
2789         }
2790 #endif
2791         else {
2792             LV_LOG_WARN("Wayland fullscreen mode not supported");
2793         }
2794     }
2795 }
2796 
2797 /**
2798  * Get pointer input device for given LVGL display
2799  * @param disp LVGL display
2800  * @return input device connected to pointer events, or NULL on error
2801  */
lv_wayland_get_pointer(lv_display_t * disp)2802 lv_indev_t * lv_wayland_get_pointer(lv_display_t * disp)
2803 {
2804     struct window * window = lv_display_get_user_data(disp);
2805     if(!window) {
2806         return NULL;
2807     }
2808     return window->lv_indev_pointer;
2809 }
2810 
2811 /**
2812  * Get pointer axis input device for given LVGL display
2813  * @param disp LVGL display
2814  * @return input device connected to pointer axis events, or NULL on error
2815  */
lv_wayland_get_pointeraxis(lv_display_t * disp)2816 lv_indev_t * lv_wayland_get_pointeraxis(lv_display_t * disp)
2817 {
2818     struct window * window = lv_display_get_user_data(disp);
2819     if(!window) {
2820         return NULL;
2821     }
2822     return window->lv_indev_pointeraxis;
2823 }
2824 
2825 /**
2826  * Get keyboard input device for given LVGL display
2827  * @param disp LVGL display
2828  * @return input device connected to keyboard, or NULL on error
2829  */
lv_wayland_get_keyboard(lv_display_t * disp)2830 lv_indev_t * lv_wayland_get_keyboard(lv_display_t * disp)
2831 {
2832     struct window * window = lv_display_get_user_data(disp);
2833     if(!window) {
2834         return NULL;
2835     }
2836     return window->lv_indev_keyboard;
2837 }
2838 
2839 /**
2840  * Get touchscreen input device for given LVGL display
2841  * @param disp LVGL display
2842  * @return input device connected to touchscreen, or NULL on error
2843  */
lv_wayland_get_touchscreen(lv_display_t * disp)2844 lv_indev_t * lv_wayland_get_touchscreen(lv_display_t * disp)
2845 {
2846     struct window * window = lv_display_get_user_data(disp);
2847     if(!window) {
2848         return NULL;
2849     }
2850     return window->lv_indev_touch;
2851 }
2852 
2853 /**
2854  * Wayland specific timer handler (use in place of LVGL lv_timer_handler)
2855  */
lv_wayland_timer_handler(void)2856 bool lv_wayland_timer_handler(void)
2857 {
2858     struct window * window;
2859 
2860     /* Wayland input handling - it will also trigger the frame done handler */
2861     _lv_wayland_handle_input();
2862 
2863     /* Ready input timers (to probe for any input received) */
2864     LV_LL_READ(&application.window_ll, window) {
2865         LV_LOG_TRACE("handle timer frame: %d", window->frame_counter);
2866 
2867         if(window != NULL && window->frame_done == false
2868            && window->frame_counter > 0) {
2869             /* The last frame was not rendered */
2870             LV_LOG_TRACE("The window is hidden or minimized");
2871 
2872             /* Simply blocks until a frame done message arrives */
2873             poll(&application.wayland_pfd, 1, -1);
2874 
2875             /* Resume lvgl on the next cycle */
2876             return false;
2877 
2878         }
2879         else if(window != NULL && window->body->surface_configured == false) {
2880             /* Initial commit to trigger the configure event */
2881             /* Manually dispatching the queue is necessary, */
2882             /* to emit the configure event straight away */
2883             wl_surface_commit(window->body->surface);
2884             wl_display_dispatch(application.display);
2885         }
2886         else if(window != NULL && window->resize_pending) {
2887             if(resize_window(window, window->resize_width, window->resize_height)) {
2888                 window->resize_width = window->width;
2889                 window->resize_height = window->height;
2890                 window->resize_pending = false;
2891 
2892             }
2893             else {
2894 
2895                 LV_LOG_TRACE("Failed to resize window frame: %d",
2896                              window->frame_counter);
2897             }
2898         }
2899         else if(window->shall_close == true) {
2900 
2901             /* Destroy graphical context and execute close_cb */
2902             _lv_wayland_handle_output();
2903             wayland_deinit();
2904             return false;
2905         }
2906     }
2907 
2908     /* LVGL handling */
2909     lv_timer_handler();
2910 
2911     /* Wayland output handling */
2912     _lv_wayland_handle_output();
2913 
2914     /* Set 'errno' if a Wayland flush is outstanding (i.e. data still needs to
2915      * be sent to the compositor, but the compositor pipe/connection is unable
2916      * to take more data at this time).
2917      */
2918     LV_LL_READ(&application.window_ll, window) {
2919         if(window->flush_pending) {
2920             errno = EAGAIN;
2921             break;
2922         }
2923     }
2924 
2925     return true;
2926 }
2927 
2928 #endif /* LV_USE_WAYLAND */
2929 #endif /* _WIN32 */
2930