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, ®istry_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