wlt: toolkit: fix forcing redraw during unneeded resize-round
[platform/upstream/kmscon.git] / src / wlt_toolkit.c
1 /*
2  * wlt - Toolkit Helper
3  *
4  * Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files
8  * (the "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included
15  * in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25
26 /*
27  * Wayland Terminal toolkit helpers
28  */
29
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <sys/mman.h>
35 #include <unistd.h>
36 #include <wayland-client.h>
37 #include <wayland-cursor.h>
38 #include <xkbcommon/xkbcommon.h>
39 #include "eloop.h"
40 #include "log.h"
41 #include "shl_dlist.h"
42 #include "shl_hook.h"
43 #include "tsm_vte.h"
44 #include "wlt_toolkit.h"
45
46 #define LOG_SUBSYSTEM "wlt_toolkit"
47
48 struct wlt_pool {
49         struct wl_shm_pool *w_pool;
50         size_t size;
51         uint8_t *data;
52 };
53
54 enum {
55         STATE_INIT = 0,
56         STATE_RUNNING,
57         STATE_HUP,
58 };
59
60 struct wlt_display {
61         unsigned long ref;
62         struct ev_eloop *eloop;
63         struct wl_display *dp;
64         struct ev_fd *dp_fd;
65         struct wl_global_listener *dp_listener;
66         struct shl_hook *listeners;
67         unsigned int state;
68
69         struct shl_dlist window_list;
70
71         struct wl_compositor *w_comp;
72         struct wl_seat *w_seat;
73         struct wl_shell *w_shell;
74         struct wl_shm *w_shm;
75
76         uint32_t last_serial;
77         uint32_t pointer_enter_serial;
78         struct wl_pointer *w_pointer;
79         struct wlt_window *pointer_focus;
80
81         uint32_t cursor_serial;
82         unsigned int current_cursor;
83         struct wl_surface *w_cursor_surface;
84         struct wl_cursor_theme *cursor_theme;
85         struct wl_cursor *cursors[WLT_CURSOR_NUM];
86
87         struct wl_keyboard *w_keyboard;
88         struct xkb_context *xkb_ctx;
89         struct xkb_keymap *xkb_keymap;
90         struct xkb_state *xkb_state;
91         struct wlt_window *keyboard_focus;
92         struct ev_timer *repeat_timer;
93         uint32_t repeat_sym;
94 };
95
96 struct wlt_window {
97         unsigned long ref;
98         struct shl_dlist list;
99         void *data;
100         wlt_window_close_cb close_cb;
101         bool close_pending;
102         struct wlt_display *disp;
103         struct wlt_pool *pool;
104
105         struct wl_surface *w_surface;
106         struct wl_shell_surface *w_shell_surface;
107         struct wl_buffer *w_buffer;
108
109         bool buffer_attached;
110         bool skip_damage;
111         bool need_resize;
112         bool need_redraw;
113         bool need_frame;
114         bool idle_pending;
115         unsigned int new_width;
116         unsigned int new_height;
117         unsigned int saved_width;
118         unsigned int saved_height;
119         unsigned int resize_edges;
120         bool maximized;
121         struct wlt_shm_buffer buffer;
122         struct wl_callback *w_frame;
123
124         struct shl_dlist widget_list;
125 };
126
127 struct wlt_widget {
128         struct shl_dlist list;
129         struct wlt_window *wnd;
130         void *data;
131         wlt_widget_redraw_cb redraw_cb;
132         wlt_widget_destroy_cb destroy_cb;
133         wlt_widget_prepare_resize_cb prepare_resize_cb;
134         wlt_widget_resize_cb resize_cb;
135         wlt_widget_pointer_enter_cb pointer_enter_cb;
136         wlt_widget_pointer_leave_cb pointer_leave_cb;
137         wlt_widget_pointer_motion_cb pointer_motion_cb;
138         wlt_widget_pointer_button_cb pointer_button_cb;
139         wlt_widget_keyboard_cb keyboard_cb;
140 };
141
142 static int wlt_pool_new(struct wlt_pool **out, struct wlt_display *disp,
143                         size_t size)
144 {
145         static const char template[] = "/wlterm-shared-XXXXXX";
146         struct wlt_pool *pool;
147         int fd, ret;
148         const char *path;
149         char *name;
150
151         path = getenv("XDG_RUNTIME_DIR");
152         if (!path) {
153                 log_error("XDG_RUNTIME_DIR not set");
154                 return -EFAULT;
155         }
156
157         pool = malloc(sizeof(*pool));
158         if (!pool)
159                 return -ENOMEM;
160         memset(pool, 0, sizeof(*pool));
161         pool->size = size;
162
163         name = malloc(strlen(path) + sizeof(template));
164         if (!name) {
165                 ret = -ENOMEM;
166                 goto err_free;
167         }
168         strcpy(name, path);
169         strcat(name, template);
170
171         fd = mkostemp(name, O_CLOEXEC);
172         if (fd < 0) {
173                 log_error("cannot create temporary file %s via mkostemp() (%d): %m",
174                           name, errno);
175                 ret = -EFAULT;
176                 free(name);
177                 goto err_free;
178         }
179
180         ret = unlink(name);
181         if (ret)
182                 log_warning("cannot unlink temporary file %s (%d): %m",
183                             name, errno);
184         free(name);
185
186         ret = ftruncate(fd, size);
187         if (ret) {
188                 log_error("cannot truncate temporary file to length %lu (%d): %m",
189                           size, errno);
190                 ret = -EFAULT;
191                 goto err_close;
192         }
193
194         pool->data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED,
195                           fd, 0);
196         if (pool->data == MAP_FAILED) {
197                 log_error("cannot mmap temporary file (%d): %m", errno);
198                 ret = -EFAULT;
199                 goto err_close;
200         }
201
202         pool->w_pool = wl_shm_create_pool(disp->w_shm, fd, size);
203         if (!pool->w_pool) {
204                 log_error("cannot create wayland shm pool object");
205                 ret = -EFAULT;
206                 goto err_map;
207         }
208
209         close(fd);
210         *out = pool;
211         return 0;
212
213 err_map:
214         munmap(pool->data, size);
215 err_close:
216         close(fd);
217 err_free:
218         free(pool);
219         return ret;
220 }
221
222 static void wlt_pool_free(struct wlt_pool *pool)
223 {
224         munmap(pool->data, pool->size);
225         wl_shm_pool_destroy(pool->w_pool);
226         free(pool);
227 }
228
229 /*
230  * These cursor-names are based on:
231  *   https://bugs.kde.org/attachment.cgi?id=67313
232  */
233
234 static char *(*cursors[])[] = {
235         [WLT_CURSOR_NONE] = &(char*[]) {
236                 NULL,
237         },
238         [WLT_CURSOR_TOP] = &(char*[]) {
239                 "top_side",
240                 "n-resize",
241                 NULL,
242         },
243         [WLT_CURSOR_BOTTOM] = &(char*[]) {
244                 "bottom_side",
245                 "s-resize",
246                 NULL,
247         },
248         [WLT_CURSOR_LEFT] = &(char*[]) {
249                 "left_side",
250                 "w-resize",
251                 NULL,
252         },
253         [WLT_CURSOR_RIGHT] = &(char*[]) {
254                 "right_side",
255                 "e-resize",
256                 NULL,
257         },
258         [WLT_CURSOR_TOP_LEFT] = &(char*[]) {
259                 "top_left_corner",
260                 "nw-resize",
261                 NULL,
262         },
263         [WLT_CURSOR_TOP_RIGHT] = &(char*[]) {
264                 "top_right_corner",
265                 "ne-resize",
266                 NULL,
267         },
268         [WLT_CURSOR_BOTTOM_LEFT] = &(char*[]) {
269                 "bottom_left_corner",
270                 "sw-resize",
271                 NULL,
272         },
273         [WLT_CURSOR_BOTTOM_RIGHT] = &(char*[]) {
274                 "bottom_right_corner",
275                 "se-resize",
276                 NULL,
277         },
278         [WLT_CURSOR_DRAGGING] = &(char*[]) {
279                 "grabbing",
280                 "closedhand",
281                 "208530c400c041818281048008011002",
282                 NULL,
283         },
284         [WLT_CURSOR_LEFT_PTR] = &(char*[]) {
285                 "left_ptr",
286                 "default",
287                 "top_left_arrow",
288                 "left-arrow",
289                 NULL,
290         },
291         [WLT_CURSOR_IBEAM] = &(char*[]) {
292                 "xterm",
293                 "ibeam",
294                 "text",
295                 NULL,
296         },
297 };
298
299 static int load_cursors(struct wlt_display *disp)
300 {
301         unsigned int i, j;
302         size_t size = sizeof(cursors) / sizeof(*cursors);
303         struct wl_cursor *cursor;
304
305         disp->w_cursor_surface = wl_compositor_create_surface(disp->w_comp);
306
307         disp->cursor_theme = wl_cursor_theme_load(NULL, 32, disp->w_shm);
308         if (!disp->cursor_theme) {
309                 log_warning("cannot load curdor theme");
310                 return -EFAULT;
311         }
312
313         for (i = 0; i < WLT_CURSOR_NUM; ++i) {
314                 cursor = NULL;
315                 if (i < size && cursors[i]) {
316                         for (j = 0; (*cursors[i])[j]; ++j) {
317                                 cursor = wl_cursor_theme_get_cursor(
318                                                         disp->cursor_theme,
319                                                         (*cursors[i])[j]);
320                                 if (cursor)
321                                         break;
322                         }
323                 }
324
325                 if (!cursor && i != WLT_CURSOR_NONE)
326                         log_warning("cannot load cursor for ID %d", i);
327
328                 disp->cursors[i] = cursor;
329         }
330
331         return 0;
332 }
333
334 static void unload_cursors(struct wlt_display *disp)
335 {
336         if (disp->cursor_theme)
337                 wl_cursor_theme_destroy(disp->cursor_theme);
338         if (disp->w_cursor_surface)
339                 wl_surface_destroy(disp->w_cursor_surface);
340 }
341
342 static void set_cursor(struct wlt_display *disp, unsigned int cursor)
343 {
344         bool force = false;
345         struct wl_cursor *cur;
346         struct wl_cursor_image *image;
347         struct wl_buffer *buffer;
348
349         if (cursor >= WLT_CURSOR_NUM)
350                 cursor = WLT_CURSOR_LEFT_PTR;
351
352         if (disp->pointer_enter_serial > disp->cursor_serial)
353                 force = true;
354
355         if (!force && cursor == disp->current_cursor)
356                 return;
357
358         disp->current_cursor = cursor;
359         disp->cursor_serial = disp->pointer_enter_serial;
360
361         cur = disp->cursors[cursor];
362         if (!cur) {
363                 wl_pointer_set_cursor(disp->w_pointer,
364                                       disp->pointer_enter_serial,
365                                       NULL, 0, 0);
366                 return;
367         }
368
369         image = cur->images[0];
370         buffer = wl_cursor_image_get_buffer(image);
371         if (!buffer) {
372                 log_error("cannot load buffer for cursor image");
373                 return;
374         }
375
376         wl_pointer_set_cursor(disp->w_pointer,
377                               disp->pointer_enter_serial,
378                               disp->w_cursor_surface,
379                               image->hotspot_x, image->hotspot_y);
380         wl_surface_attach(disp->w_cursor_surface, buffer, 0, 0);
381         wl_surface_damage(disp->w_cursor_surface, 0, 0,
382                           image->width, image->height);
383 }
384
385 static int dp_mask_update(uint32_t mask, void *data)
386 {
387         struct wlt_display *disp = data;
388         int ret;
389         int mode;
390
391         if (!disp->dp_fd)
392                 return 0;
393
394         mode = 0;
395         if (mask & WL_DISPLAY_READABLE)
396                 mode |= EV_READABLE;
397         if (mask & WL_DISPLAY_WRITABLE)
398                 mode |= EV_WRITEABLE;
399
400         ret = ev_fd_update(disp->dp_fd, mode);
401         if (ret)
402                 log_warning("cannot update wayland-fd event-polling modes (%d)",
403                             ret);
404
405         return 0;
406 }
407
408 static void dp_event(struct ev_fd *fd, int mask, void *data)
409 {
410         struct wlt_display *disp = data;
411         int mode;
412
413         if (mask & (EV_HUP | EV_ERR)) {
414                 log_warning("HUP/ERR on wayland socket");
415                 disp->state = STATE_HUP;
416                 shl_hook_call(disp->listeners, disp, (void*)WLT_DISPLAY_HUP);
417                 ev_eloop_rm_fd(disp->dp_fd);
418                 disp->dp_fd = NULL;
419                 return;
420         }
421
422         mode = 0;
423         if (mask & EV_READABLE)
424                 mode |= WL_DISPLAY_READABLE;
425         if (mask & EV_WRITEABLE)
426                 mode |= WL_DISPLAY_WRITABLE;
427
428         if (mode)
429                 wl_display_iterate(disp->dp, mode);
430 }
431
432 static void pointer_enter(void *data, struct wl_pointer *w_pointer,
433                           uint32_t serial, struct wl_surface *w_surface,
434                           wl_fixed_t x, wl_fixed_t y)
435 {
436         struct wlt_display *disp = data;
437         struct shl_dlist *iter;
438         struct wlt_window *wnd;
439         struct wlt_widget *widget;
440
441         if (!w_surface)
442                 return;
443
444         shl_dlist_for_each(iter, &disp->window_list) {
445                 wnd = shl_dlist_entry(iter, struct wlt_window, list);
446                 if (wnd->w_surface == w_surface)
447                         break;
448         }
449
450         if (iter == &disp->window_list) {
451                 log_debug("unknown surface");
452                 return;
453         }
454
455         disp->pointer_enter_serial = serial;
456         disp->last_serial = serial;
457         disp->pointer_focus = wnd;
458         shl_dlist_for_each(iter, &wnd->widget_list) {
459                 widget = shl_dlist_entry(iter, struct wlt_widget, list);
460                 if (widget->pointer_enter_cb)
461                         widget->pointer_enter_cb(widget, x >> 8, y >> 8,
462                                                  widget->data);
463         }
464 }
465
466 static void pointer_leave(void *data, struct wl_pointer *w_pointer,
467                           uint32_t serial, struct wl_surface *w_surface)
468 {
469         struct wlt_display *disp = data;
470         struct shl_dlist *iter;
471         struct wlt_window *wnd;
472         struct wlt_widget *widget;
473
474         wnd = disp->pointer_focus;
475         disp->pointer_focus = NULL;
476         disp->last_serial = serial;
477
478         if (!wnd)
479                 return;
480
481         shl_dlist_for_each(iter, &wnd->widget_list) {
482                 widget = shl_dlist_entry(iter, struct wlt_widget, list);
483                 if (widget->pointer_leave_cb)
484                         widget->pointer_leave_cb(widget, widget->data);
485         }
486 }
487
488 static void pointer_motion(void *data, struct wl_pointer *w_pointer,
489                            uint32_t time, wl_fixed_t x, wl_fixed_t y)
490 {
491         struct wlt_display *disp = data;
492         struct shl_dlist *iter;
493         struct wlt_window *wnd;
494         struct wlt_widget *widget;
495
496         wnd = disp->pointer_focus;
497         if (!wnd)
498                 return;
499
500         shl_dlist_for_each(iter, &wnd->widget_list) {
501                 widget = shl_dlist_entry(iter, struct wlt_widget, list);
502                 if (widget->pointer_motion_cb)
503                         widget->pointer_motion_cb(widget, x >> 8, y >> 8,
504                                                   widget->data);
505         }
506 }
507
508 static void pointer_button(void *data, struct wl_pointer *w_pointer,
509                            uint32_t serial, uint32_t time, uint32_t button,
510                            uint32_t state)
511 {
512         struct wlt_display *disp = data;
513         struct shl_dlist *iter;
514         struct wlt_window *wnd;
515         struct wlt_widget *widget;
516
517         wnd = disp->pointer_focus;
518         disp->last_serial = serial;
519
520         if (!wnd)
521                 return;
522
523         shl_dlist_for_each(iter, &wnd->widget_list) {
524                 widget = shl_dlist_entry(iter, struct wlt_widget, list);
525                 if (widget->pointer_button_cb)
526                         widget->pointer_button_cb(widget, button, state,
527                                                   widget->data);
528         }
529 }
530
531 static void pointer_axis(void *data, struct wl_pointer *w_pointer,
532                          uint32_t time, uint32_t axis, wl_fixed_t value)
533 {
534 }
535
536 static const struct wl_pointer_listener pointer_listener = {
537         .enter = pointer_enter,
538         .leave = pointer_leave,
539         .motion = pointer_motion,
540         .button = pointer_button,
541         .axis = pointer_axis,
542 };
543
544 static void keyboard_keymap(void *data, struct wl_keyboard *keyboard,
545                             uint32_t format, int fd, uint32_t size)
546 {
547         struct wlt_display *disp = data;
548         char *map;
549
550         if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
551                 log_error("invalid keyboard format");
552                 close(fd);
553                 return;
554         }
555
556         map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
557         if (map == MAP_FAILED) {
558                 log_error("cannot mmap keyboard keymap");
559                 close(fd);
560                 return;
561         }
562
563         disp->xkb_keymap = xkb_map_new_from_string(disp->xkb_ctx, map,
564                                                    XKB_KEYMAP_FORMAT_TEXT_V1,
565                                                    0);
566         munmap(map, size);
567         close(fd);
568
569         if (!disp->xkb_keymap) {
570                 log_error("cannot create xkb keymap");
571                 return;
572         }
573
574         disp->xkb_state = xkb_state_new(disp->xkb_keymap);
575         if (!disp->xkb_state) {
576                 log_error("cannot create XKB state object");
577                 xkb_map_unref(disp->xkb_keymap);
578                 disp->xkb_keymap = NULL;
579                 return;
580         }
581 }
582
583 static void keyboard_enter(void *data, struct wl_keyboard *keyboard,
584                            uint32_t serial, struct wl_surface *w_surface,
585                            struct wl_array *keys)
586 {
587         struct wlt_display *disp = data;
588         struct shl_dlist *iter;
589         struct wlt_window *wnd;
590
591         disp->last_serial = serial;
592         if (!disp->xkb_state)
593                 return;
594
595         if (!w_surface)
596                 return;
597
598         shl_dlist_for_each(iter, &disp->window_list) {
599                 wnd = shl_dlist_entry(iter, struct wlt_window, list);
600                 if (wnd->w_surface == w_surface)
601                         break;
602         }
603
604         if (iter == &disp->window_list)
605                 return;
606
607         disp->keyboard_focus = wnd;
608 }
609
610 static void keyboard_leave(void *data, struct wl_keyboard *keyboard,
611                            uint32_t serial, struct wl_surface *surface)
612 {
613         struct wlt_display *disp = data;
614
615         disp->last_serial = serial;
616         disp->keyboard_focus = NULL;
617         ev_timer_update(disp->repeat_timer, NULL);
618 }
619
620 static unsigned int get_effective_modmask(struct xkb_state *state)
621 {
622         unsigned int mods = 0;
623
624         if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_SHIFT,
625                                                 XKB_STATE_EFFECTIVE))
626                 mods |= TSM_SHIFT_MASK;
627         if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CAPS,
628                                                 XKB_STATE_EFFECTIVE))
629                 mods |= TSM_LOCK_MASK;
630         if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL,
631                                                 XKB_STATE_EFFECTIVE))
632                 mods |= TSM_CONTROL_MASK;
633         if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT,
634                                                 XKB_STATE_EFFECTIVE))
635                 mods |= TSM_MOD1_MASK;
636         if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_LOGO,
637                                                 XKB_STATE_EFFECTIVE))
638                 mods |= TSM_MOD4_MASK;
639
640         return mods;
641 }
642
643 static void keyboard_key(void *data, struct wl_keyboard *keyboard,
644                          uint32_t serial, uint32_t time, uint32_t key,
645                          uint32_t state_w)
646 {
647         struct wlt_display *disp = data;
648         struct wlt_window *wnd = disp->keyboard_focus;
649         uint32_t code, num_syms;
650         unsigned int mask;
651         enum wl_keyboard_key_state state = state_w;
652         const xkb_keysym_t *syms;
653         xkb_keysym_t sym;
654         struct shl_dlist *iter;
655         struct wlt_widget *widget;
656         struct itimerspec spec;
657
658         disp->last_serial = serial;
659         if (!disp->xkb_state)
660                 return;
661
662         code = key + 8;
663         if (!wnd)
664                 return;
665
666         mask = get_effective_modmask(disp->xkb_state);
667         num_syms = xkb_key_get_syms(disp->xkb_state, code, &syms);
668         sym = XKB_KEY_NoSymbol;
669         if (num_syms == 1)
670                 sym = syms[0];
671
672         shl_dlist_for_each(iter, &wnd->widget_list) {
673                 widget = shl_dlist_entry(iter, struct wlt_widget, list);
674                 if (widget->keyboard_cb)
675                         widget->keyboard_cb(widget, mask, sym, state,
676                                             widget->data);
677         }
678
679         if (state == WL_KEYBOARD_KEY_STATE_RELEASED &&
680             sym == disp->repeat_sym) {
681                 ev_timer_update(disp->repeat_timer, NULL);
682         } else if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
683                 disp->repeat_sym = sym;
684                 spec.it_interval.tv_sec = 0;
685                 spec.it_interval.tv_nsec = 25 * 1000000;
686                 spec.it_value.tv_sec = 0;
687                 spec.it_value.tv_nsec = 250 * 1000000;
688                 ev_timer_update(disp->repeat_timer, &spec);
689         }
690 }
691
692 static void repeat_event(struct ev_timer *timer, uint64_t num, void *data)
693 {
694         struct wlt_display *disp = data;
695         struct wlt_window *wnd = disp->keyboard_focus;
696         struct wlt_widget *widget;
697         struct shl_dlist *iter;
698         unsigned int mask;
699
700         if (!wnd)
701                 return;
702
703         mask = get_effective_modmask(disp->xkb_state);
704         shl_dlist_for_each(iter, &wnd->widget_list) {
705                 widget = shl_dlist_entry(iter, struct wlt_widget, list);
706                 if (widget->keyboard_cb)
707                         widget->keyboard_cb(widget, mask, disp->repeat_sym,
708                                             WL_KEYBOARD_KEY_STATE_PRESSED,
709                                             widget->data);
710         }
711 }
712
713 static void keyboard_modifiers(void *data, struct wl_keyboard *keyboard,
714                                uint32_t serial, uint32_t mods_depressed,
715                                uint32_t mods_latched, uint32_t mods_locked,
716                                uint32_t group)
717 {
718         struct wlt_display *disp = data;
719
720         disp->last_serial = serial;
721         if (!disp->xkb_state)
722                 return;
723
724         xkb_state_update_mask(disp->xkb_state, mods_depressed, mods_latched,
725                               mods_locked, 0, 0, group);
726 }
727
728 static const struct wl_keyboard_listener keyboard_listener = {
729         .keymap = keyboard_keymap,
730         .enter = keyboard_enter,
731         .leave = keyboard_leave,
732         .key = keyboard_key,
733         .modifiers = keyboard_modifiers,
734 };
735
736 static void check_ready(struct wlt_display *disp)
737 {
738         if (disp->state > STATE_INIT)
739                 return;
740
741         if (disp->w_comp &&
742             disp->w_seat &&
743             disp->w_shell &&
744             disp->w_shm &&
745             disp->w_pointer &&
746             disp->w_keyboard) {
747                 log_debug("wayland display initialized");
748                 load_cursors(disp);
749
750                 disp->state = STATE_RUNNING;
751                 shl_hook_call(disp->listeners, disp, (void*)WLT_DISPLAY_READY);
752         }
753 }
754
755 static void seat_capabilities(void *data, struct wl_seat *seat,
756                               enum wl_seat_capability caps)
757 {
758         struct wlt_display *disp = data;
759
760         if (caps & WL_SEAT_CAPABILITY_POINTER && !disp->w_pointer) {
761                 disp->w_pointer = wl_seat_get_pointer(seat);
762                 wl_pointer_add_listener(disp->w_pointer, &pointer_listener,
763                                         disp);
764         }
765
766         if (caps & WL_SEAT_CAPABILITY_KEYBOARD && !disp->w_keyboard) {
767                 disp->w_keyboard = wl_seat_get_keyboard(seat);
768                 wl_keyboard_add_listener(disp->w_keyboard, &keyboard_listener,
769                                          disp);
770         }
771
772         check_ready(disp);
773 }
774
775 static const struct wl_seat_listener seat_listener = {
776         .capabilities = seat_capabilities,
777 };
778
779 static void dp_global(struct wl_display *dp, uint32_t id, const char *iface,
780                       uint32_t version, void *data)
781 {
782         struct wlt_display *disp = data;
783
784         if (!strcmp(iface, "wl_display")) {
785                 log_debug("new wl_display global");
786                 return;
787         } else if (!strcmp(iface, "wl_compositor")) {
788                 if (disp->w_comp) {
789                         log_error("global wl_compositor advertised twice");
790                         return;
791                 }
792                 disp->w_comp = wl_display_bind(disp->dp,
793                                                id,
794                                                &wl_compositor_interface);
795                 if (!disp->w_comp) {
796                         log_error("cannot bind wl_compositor object");
797                         return;
798                 }
799         } else if (!strcmp(iface, "wl_seat")) {
800                 if (disp->w_seat) {
801                         log_error("global wl_seat advertised twice");
802                         return;
803                 }
804                 disp->w_seat = wl_display_bind(disp->dp,
805                                                id,
806                                                &wl_seat_interface);
807                 if (!disp->w_seat) {
808                         log_error("cannot bind wl_seat object");
809                         return;
810                 }
811
812                 wl_seat_add_listener(disp->w_seat, &seat_listener, disp);
813         } else if (!strcmp(iface, "wl_shell")) {
814                 if (disp->w_shell) {
815                         log_error("global wl_shell advertised twice");
816                         return;
817                 }
818                 disp->w_shell = wl_display_bind(disp->dp,
819                                                 id,
820                                                 &wl_shell_interface);
821                 if (!disp->w_shell) {
822                         log_error("cannot bind wl_shell object");
823                         return;
824                 }
825         } else if (!strcmp(iface, "wl_shm")) {
826                 if (disp->w_shm) {
827                         log_error("global wl_shm advertised twice");
828                         return;
829                 }
830                 disp->w_shm = wl_display_bind(disp->dp,
831                                               id,
832                                               &wl_shm_interface);
833                 if (!disp->w_shm) {
834                         log_error("cannot bind wl_shm object");
835                         return;
836                 }
837         } else {
838                 log_debug("ignoring new unknown global %s", iface);
839                 return;
840         }
841
842         log_debug("new global %s", iface);
843
844         check_ready(disp);
845 }
846
847 int wlt_display_new(struct wlt_display **out,
848                     struct ev_eloop *eloop)
849 {
850         struct wlt_display *disp;
851         int ret;
852
853         if (!out || !eloop)
854                 return -EINVAL;
855
856         log_debug("creating new wlt-display");
857
858         disp = malloc(sizeof(*disp));
859         if (!disp)
860                 return -ENOMEM;
861         memset(disp, 0, sizeof(*disp));
862         disp->ref = 1;
863         disp->eloop = eloop;
864         shl_dlist_init(&disp->window_list);
865
866         ret = shl_hook_new(&disp->listeners);
867         if (ret) {
868                 log_error("cannot create listeners hook");
869                 goto err_free;
870         }
871
872         disp->dp = wl_display_connect(NULL);
873         if (!disp->dp) {
874                 log_error("cannot connect to wayland socket (%d): %m", errno);
875                 ret = -EFAULT;
876                 goto err_listener;
877         }
878
879         ret = ev_eloop_new_fd(eloop,
880                               &disp->dp_fd,
881                               wl_display_get_fd(disp->dp,
882                                                 dp_mask_update,
883                                                 disp),
884                               EV_READABLE,
885                               dp_event,
886                               disp);
887         if (ret) {
888                 log_error("cannot create event-fd for wayland display (%d)",
889                           ret);
890                 goto err_dp;
891         }
892
893         ret = ev_eloop_new_timer(eloop, &disp->repeat_timer, NULL,
894                                  repeat_event, disp);
895         if (ret) {
896                 log_error("cannot create repeat-timer");
897                 goto err_dp_fd;
898         }
899
900         disp->dp_listener = wl_display_add_global_listener(disp->dp,
901                                                            dp_global,
902                                                            disp);
903         if (!disp->dp_listener) {
904                 log_error("cannot add wayland global listener (%d)", errno);
905                 goto err_timer;
906         }
907
908         disp->xkb_ctx = xkb_context_new(0);
909         if (!disp->xkb_ctx) {
910                 log_error("cannot create XKB context");
911                 goto err_timer;
912         }
913
914         log_debug("wlt-display waiting for globals...");
915
916         ev_eloop_ref(disp->eloop);
917         *out = disp;
918         return 0;
919
920 err_timer:
921         ev_eloop_rm_timer(disp->repeat_timer);
922 err_dp_fd:
923         ev_eloop_rm_fd(disp->dp_fd);
924 err_dp:
925         wl_display_disconnect(disp->dp);
926 err_listener:
927         shl_hook_free(disp->listeners);
928 err_free:
929         free(disp);
930         return ret;
931 }
932
933 void wlt_display_ref(struct wlt_display *disp)
934 {
935         if (!disp || !disp->ref)
936                 return;
937
938         ++disp->ref;
939 }
940
941 void wlt_display_unref(struct wlt_display *disp)
942 {
943         if (!disp || !disp->ref || --disp->ref)
944                 return;
945
946         unload_cursors(disp);
947         wl_display_remove_global_listener(disp->dp, disp->dp_listener);
948         wl_display_flush(disp->dp);
949         wl_display_disconnect(disp->dp);
950         ev_eloop_rm_timer(disp->repeat_timer);
951         ev_eloop_rm_fd(disp->dp_fd);
952         xkb_context_unref(disp->xkb_ctx);
953         shl_hook_free(disp->listeners);
954         ev_eloop_unref(disp->eloop);
955         free(disp);
956 }
957
958 int wlt_display_register_cb(struct wlt_display *disp,
959                             wlt_display_cb cb, void *data)
960 {
961         if (!disp)
962                 return -EINVAL;
963
964         return shl_hook_add_cast(disp->listeners, cb, data);
965 }
966
967 void wlt_display_unregister_cb(struct wlt_display *disp,
968                                wlt_display_cb cb, void *data)
969 {
970         if (!disp)
971                 return;
972
973         shl_hook_rm_cast(disp->listeners, cb, data);
974 }
975
976 static void shell_surface_ping(void *data, struct wl_shell_surface *s,
977                                uint32_t serial)
978 {
979         wl_shell_surface_pong(s, serial);
980 }
981
982 static void wlt_window_do_redraw(struct wlt_window *wnd,
983                                  unsigned int oldw,
984                                  unsigned int oldh)
985 {
986         struct shl_dlist *iter;
987         struct wlt_widget *widget;
988         unsigned int x, y;
989         struct wlt_rect alloc;
990
991         alloc.x = 0;
992         alloc.y = 0;
993         alloc.width = wnd->buffer.width;
994         alloc.height = wnd->buffer.height;
995         shl_dlist_for_each(iter, &wnd->widget_list) {
996                 widget = shl_dlist_entry(iter, struct wlt_widget, list);
997                 if (widget->resize_cb)
998                         widget->resize_cb(widget, &alloc, widget->data);
999         }
1000
1001         memset(wnd->buffer.data, 0,
1002                wnd->buffer.stride * wnd->buffer.height);
1003
1004         wnd->skip_damage = true;
1005         shl_dlist_for_each(iter, &wnd->widget_list) {
1006                 widget = shl_dlist_entry(iter, struct wlt_widget, list);
1007                 if (widget->redraw_cb)
1008                         widget->redraw_cb(widget, widget->data);
1009         }
1010         wnd->skip_damage = false;
1011
1012         if (!wnd->buffer_attached) {
1013                 wnd->buffer_attached = true;
1014                 if (wnd->resize_edges & WL_SHELL_SURFACE_RESIZE_LEFT)
1015                         x = (int)oldw - wnd->buffer.width;
1016                 else
1017                         x = 0;
1018                 if (wnd->resize_edges & WL_SHELL_SURFACE_RESIZE_TOP)
1019                         y = (int)oldh - wnd->buffer.height;
1020                 else
1021                         y = 0;
1022                 wl_surface_attach(wnd->w_surface, wnd->w_buffer, x, y);
1023                 wnd->resize_edges = 0;
1024         }
1025
1026         wl_surface_damage(wnd->w_surface, 0, 0, wnd->buffer.width,
1027                           wnd->buffer.height);
1028 }
1029
1030 static int resize_window(struct wlt_window *wnd, unsigned int width,
1031                          unsigned int height, bool force_redraw)
1032 {
1033         struct shl_dlist *iter;
1034         struct wlt_widget *widget;
1035         struct wl_buffer *old_buffer = NULL;
1036         struct wlt_pool *old_pool = NULL;
1037         size_t nsize;
1038         int ret;
1039         unsigned int oldw, oldh, neww, newh;
1040
1041         if (!wnd || !width || !height)
1042                 return -EINVAL;
1043
1044         neww = 0;
1045         newh = 0;
1046         shl_dlist_for_each(iter, &wnd->widget_list) {
1047                 widget = shl_dlist_entry(iter, struct wlt_widget, list);
1048                 if (widget->prepare_resize_cb)
1049                         widget->prepare_resize_cb(widget, width, height,
1050                                                   &neww, &newh,
1051                                                   widget->data);
1052         }
1053
1054         if (neww)
1055                 width = neww;
1056         if (newh)
1057                 height = newh;
1058
1059         if (width == wnd->buffer.width &&
1060             height == wnd->buffer.height) {
1061                 if (force_redraw)
1062                         wlt_window_do_redraw(wnd, width, height);
1063                 return 0;
1064         }
1065
1066         oldw = wnd->buffer.width;
1067         oldh = wnd->buffer.height;
1068
1069         nsize = width * height * 4;
1070         if (wnd->pool && wnd->pool->size >= nsize) {
1071                 old_buffer = wnd->w_buffer;
1072                 wnd->w_buffer = wl_shm_pool_create_buffer(wnd->pool->w_pool,
1073                                                 0, width, height,
1074                                                 width * 4,
1075                                                 WL_SHM_FORMAT_ARGB8888);
1076                 if (!wnd->w_buffer) {
1077                         log_error("cannot create wayland shm buffer");
1078                         wnd->w_buffer = old_buffer;
1079                         return -ENOMEM;
1080                 }
1081         } else {
1082                 old_pool = wnd->pool;
1083                 ret = wlt_pool_new(&wnd->pool, wnd->disp, nsize);
1084                 if (ret) {
1085                         log_error("cannot create memory pool");
1086                         wnd->pool = old_pool;
1087                         return ret;
1088                 }
1089
1090                 old_buffer = wnd->w_buffer;
1091                 wnd->w_buffer = wl_shm_pool_create_buffer(wnd->pool->w_pool,
1092                                                 0, width, height,
1093                                                 width * 4,
1094                                                 WL_SHM_FORMAT_ARGB8888);
1095                 if (!wnd->w_buffer) {
1096                         log_error("cannot create wayland shm buffer");
1097                         wnd->w_buffer = old_buffer;
1098                         wlt_pool_free(wnd->pool);
1099                         wnd->pool = old_pool;
1100                         return -ENOMEM;
1101                 }
1102         }
1103
1104         wnd->buffer.data = wnd->pool->data;
1105         wnd->buffer.width = width;
1106         wnd->buffer.height = height;
1107         wnd->buffer.stride = width * 4;
1108         wnd->buffer_attached = false;
1109
1110         wlt_window_do_redraw(wnd, oldw, oldh);
1111
1112         if (old_buffer)
1113                 wl_buffer_destroy(old_buffer);
1114         if (old_pool)
1115                 wlt_pool_free(old_pool);
1116
1117         return 0;
1118 }
1119
1120 static void frame_callback(void *data, struct wl_callback *w_callback,
1121                            uint32_t time);
1122 static void idle_frame(struct ev_eloop *eloop, void *unused, void *data);
1123
1124 static const struct wl_callback_listener frame_callback_listener = {
1125         .done = frame_callback,
1126 };
1127
1128 static void do_frame(struct wlt_window *wnd)
1129 {
1130         bool force;
1131
1132         wnd->idle_pending = false;
1133         ev_eloop_unregister_idle_cb(wnd->disp->eloop, idle_frame, wnd);
1134
1135         if (wnd->need_resize) {
1136                 force = wnd->need_redraw;
1137                 wnd->need_frame = true;
1138                 wnd->need_resize = false;
1139                 wnd->need_redraw = false;
1140                 resize_window(wnd, wnd->new_width, wnd->new_height, force);
1141         }
1142
1143         if (wnd->need_redraw) {
1144                 wnd->need_frame = true;
1145                 wnd->need_redraw = false;
1146                 wlt_window_do_redraw(wnd, wnd->buffer.width,
1147                                      wnd->buffer.height);
1148         }
1149
1150         if (wnd->need_frame) {
1151                 wnd->w_frame = wl_surface_frame(wnd->w_surface);
1152                 wl_callback_add_listener(wnd->w_frame,
1153                                          &frame_callback_listener, wnd);
1154         }
1155 }
1156
1157 static void frame_callback(void *data, struct wl_callback *w_callback,
1158                            uint32_t time)
1159 {
1160         struct wlt_window *wnd = data;
1161
1162         wl_callback_destroy(w_callback);
1163         wnd->w_frame = NULL;
1164         wnd->need_frame = false;
1165
1166         do_frame(wnd);
1167 }
1168
1169 static void idle_frame(struct ev_eloop *eloop, void *unused, void *data)
1170 {
1171         struct wlt_window *wnd = data;
1172
1173         do_frame(wnd);
1174 }
1175
1176 /*
1177  * Buffer Handling and Frame Scheduling
1178  * We use wl_shm for buffer allocation. This means, we have a single buffer
1179  * client side and the server loads it into its backbuffer for rendering. If the
1180  * server does not do this, we are screwed anyway, but that's on behalf of the
1181  * server, so we don't care.
1182  *
1183  * However, this means, when we create a buffer, we need to notify the
1184  * compositor and then wait until the compositor has created its back-buffer,
1185  * before we continue using this buffer. We can use the "frame" callback to get
1186  * notified about this.
1187  *
1188  * The logic we have is:
1189  * You set the boolean flags what action is needed in wlt_window and then call
1190  * "schedule_frame()". If we didn't already do any buffer operations in this
1191  * frame, then this function schedules an idle-callback which then performs
1192  * the requested functions (flags in wlt_window). Afterwards, it sets a marker
1193  * that this frame was already used and schedules a frame-callback.
1194  * If during this time another call to schedule_frame() is made, we do nothing
1195  * but wait for the frame-callback. It will then directly perform all the
1196  * requested functions and reschedule a frame-callback.
1197  * If nothing was schedule for one frame, we have no more interest in
1198  * frame-callbacks and thus we set "need_frame" to false again and don't
1199  * schedule any more frame-callbacks.
1200  */
1201 static void schedule_frame(struct wlt_window *wnd)
1202 {
1203         int ret;
1204
1205         if (!wnd || wnd->w_frame)
1206                 return;
1207
1208         if (wnd->need_frame || wnd->idle_pending)
1209                 return;
1210
1211         ret = ev_eloop_register_idle_cb(wnd->disp->eloop, idle_frame, wnd);
1212         if (ret)
1213                 log_error("cannot schedule idle callback: %d", ret);
1214         else
1215                 wnd->idle_pending = true;
1216 }
1217
1218 static void shell_surface_configure(void *data, struct wl_shell_surface *s,
1219                                     uint32_t edges, int32_t width,
1220                                     int32_t height)
1221 {
1222         struct wlt_window *wnd = data;
1223
1224         if (width <= 0)
1225                 width = 1;
1226         if (height <= 0)
1227                 height = 1;
1228
1229         wnd->resize_edges = edges;
1230         wlt_window_set_size(wnd, width, height);
1231 }
1232
1233 static void shell_surface_popup_done(void *data, struct wl_shell_surface *s)
1234 {
1235 }
1236
1237 static const struct wl_shell_surface_listener shell_surface_listener = {
1238         .ping = shell_surface_ping,
1239         .configure = shell_surface_configure,
1240         .popup_done = shell_surface_popup_done,
1241 };
1242
1243 static void close_window(struct ev_eloop *eloop, void *unused, void *data)
1244 {
1245         struct wlt_window *wnd = data;
1246
1247         ev_eloop_unregister_idle_cb(eloop, close_window, wnd);
1248         wnd->close_pending = false;
1249
1250         if (wnd->close_cb)
1251                 wnd->close_cb(wnd, wnd->data);
1252 }
1253
1254 int wlt_display_create_window(struct wlt_display *disp,
1255                               struct wlt_window **out,
1256                               unsigned int width,
1257                               unsigned int height,
1258                               void *data)
1259 {
1260         struct wlt_window *wnd;
1261         int ret;
1262
1263         if (!disp || !out || !width || !height)
1264                 return -EINVAL;
1265
1266         if (disp->state != STATE_RUNNING) {
1267                 log_error("cannot create window, display is not running but in state %u",
1268                           disp->state);
1269                 return -EBUSY;
1270         }
1271
1272         wnd = malloc(sizeof(*wnd));
1273         if (!wnd)
1274                 return -ENOMEM;
1275         memset(wnd, 0, sizeof(*wnd));
1276         wnd->ref = 1;
1277         wnd->disp = disp;
1278         wnd->data = data;
1279         shl_dlist_init(&wnd->widget_list);
1280
1281         wnd->w_surface = wl_compositor_create_surface(disp->w_comp);
1282         if (!wnd->w_surface) {
1283                 log_error("cannot create wayland surface");
1284                 ret = -EFAULT;
1285                 goto err_free;
1286         }
1287
1288         wnd->w_shell_surface = wl_shell_get_shell_surface(disp->w_shell,
1289                                                           wnd->w_surface);
1290         if (!wnd->w_shell_surface) {
1291                 log_error("cannot retrieve shell-surface for wayland surface");
1292                 ret = -EFAULT;
1293                 goto err_surface;
1294         }
1295
1296         wl_shell_surface_add_listener(wnd->w_shell_surface,
1297                                       &shell_surface_listener, wnd);
1298         wl_shell_surface_set_toplevel(wnd->w_shell_surface);
1299
1300         ret = resize_window(wnd, width, height, true);
1301         if (ret)
1302                 goto err_shell_surface;
1303
1304         wlt_display_ref(disp);
1305         shl_dlist_link(&disp->window_list, &wnd->list);
1306         *out = wnd;
1307         return 0;
1308
1309 err_shell_surface:
1310         wl_shell_surface_destroy(wnd->w_shell_surface);
1311 err_surface:
1312         wl_surface_destroy(wnd->w_surface);
1313 err_free:
1314         free(wnd);
1315         return ret;
1316 }
1317
1318 void wlt_window_ref(struct wlt_window *wnd)
1319 {
1320         if (!wnd || !wnd->ref)
1321                 return;
1322
1323         ++wnd->ref;
1324 }
1325
1326 void wlt_window_unref(struct wlt_window *wnd)
1327 {
1328         struct wlt_widget *widget;
1329
1330         if (!wnd || !wnd->ref || --wnd->ref)
1331                 return;
1332
1333         while (!shl_dlist_empty(&wnd->widget_list)) {
1334                 widget = shl_dlist_entry(wnd->widget_list.next,
1335                                          struct wlt_widget, list);
1336                 wlt_widget_destroy(widget);
1337         }
1338
1339         if (wnd->close_pending)
1340                 ev_eloop_unregister_idle_cb(wnd->disp->eloop, close_window,
1341                                             wnd);
1342         if (wnd->idle_pending)
1343                 ev_eloop_unregister_idle_cb(wnd->disp->eloop, idle_frame, wnd);
1344         shl_dlist_unlink(&wnd->list);
1345         if (wnd->w_frame)
1346                 wl_callback_destroy(wnd->w_frame);
1347         if (wnd->w_buffer)
1348                 wl_buffer_destroy(wnd->w_buffer);
1349         if (wnd->pool)
1350                 wlt_pool_free(wnd->pool);
1351         wl_shell_surface_destroy(wnd->w_shell_surface);
1352         wl_surface_destroy(wnd->w_surface);
1353         wlt_display_unref(wnd->disp);
1354         free(wnd);
1355 }
1356
1357 int wlt_window_create_widget(struct wlt_window *wnd,
1358                              struct wlt_widget **out,
1359                              void *data)
1360 {
1361         struct wlt_widget *widget;
1362
1363         if (!wnd || !out)
1364                 return -EINVAL;
1365
1366         widget = malloc(sizeof(*widget));
1367         if (!widget)
1368                 return -ENOMEM;
1369         memset(widget, 0, sizeof(*widget));
1370         widget->wnd = wnd;
1371         widget->data = data;
1372
1373         wlt_window_schedule_redraw(wnd);
1374         shl_dlist_link_tail(&wnd->widget_list, &widget->list);
1375         *out = widget;
1376         return 0;
1377 }
1378
1379 void wlt_window_schedule_redraw(struct wlt_window *wnd)
1380 {
1381         if (!wnd)
1382                 return;
1383
1384         wnd->need_redraw = true;
1385         schedule_frame(wnd);
1386 }
1387
1388 void wlt_window_damage(struct wlt_window *wnd,
1389                        struct wlt_rect *damage)
1390 {
1391         if (!wnd || !damage || wnd->skip_damage)
1392                 return;
1393
1394         wl_surface_damage(wnd->w_surface, damage->x, damage->y,
1395                           damage->width, damage->height);
1396 }
1397
1398 void wlt_window_get_buffer(struct wlt_window *wnd,
1399                            const struct wlt_rect *alloc,
1400                            struct wlt_shm_buffer *buf)
1401 {
1402         struct wlt_shm_buffer *rbuf;
1403
1404         if (!wnd || !buf)
1405                 return;
1406
1407         rbuf = &wnd->buffer;
1408
1409         if (!alloc) {
1410                 memcpy(buf, rbuf, sizeof(*buf));
1411                 return;
1412         }
1413
1414         if (alloc->x >= rbuf->width ||
1415             alloc->y >= rbuf->height) {
1416                 memset(buf, 0, sizeof(*buf));
1417                 return;
1418         }
1419
1420         /* set width */
1421         if (alloc->x + alloc->width > rbuf->width)
1422                 buf->width = rbuf->width - alloc->x;
1423         else
1424                 buf->width = alloc->width;
1425
1426         /* set height */
1427         if (alloc->y + alloc->height > rbuf->height)
1428                 buf->height = rbuf->height - alloc->y;
1429         else
1430                 buf->height = alloc->height;
1431
1432         /* set stride */
1433         buf->stride  = rbuf->stride;
1434
1435         /* set data */
1436         buf->data  = rbuf->data;
1437         buf->data += alloc->y * rbuf->stride;
1438         buf->data += alloc->x * 4;
1439 }
1440
1441 void wlt_window_move(struct wlt_window *wnd)
1442 {
1443         if (!wnd)
1444                 return;
1445
1446         wl_shell_surface_move(wnd->w_shell_surface,
1447                               wnd->disp->w_seat,
1448                               wnd->disp->last_serial);
1449 }
1450
1451 void wlt_window_resize(struct wlt_window *wnd, uint32_t edges)
1452 {
1453         if (!wnd)
1454                 return;
1455
1456         wl_shell_surface_resize(wnd->w_shell_surface,
1457                                 wnd->disp->w_seat,
1458                                 wnd->disp->last_serial,
1459                                 edges);
1460 }
1461
1462 int wlt_window_set_size(struct wlt_window *wnd,
1463                         unsigned int width, unsigned int height)
1464 {
1465         if (!wnd || !width || !height)
1466                 return -EINVAL;
1467
1468         wnd->new_width = width;
1469         wnd->new_height = height;
1470         wnd->need_resize = true;
1471         schedule_frame(wnd);
1472
1473         return 0;
1474 }
1475
1476 void wlt_window_set_cursor(struct wlt_window *wnd, unsigned int cursor)
1477 {
1478         if (!wnd)
1479                 return;
1480
1481         set_cursor(wnd->disp, cursor);
1482 }
1483
1484 void wlt_window_set_close_cb(struct wlt_window *wnd,
1485                              wlt_window_close_cb cb)
1486 {
1487         if (!wnd)
1488                 return;
1489
1490         wnd->close_cb = cb;
1491 }
1492
1493 void wlt_window_close(struct wlt_window *wnd)
1494 {
1495         if (!wnd)
1496                 return;
1497
1498         wnd->close_pending = true;
1499         ev_eloop_register_idle_cb(wnd->disp->eloop, close_window, wnd);
1500 }
1501
1502 void wlt_window_toggle_maximize(struct wlt_window *wnd)
1503 {
1504         if (!wnd)
1505                 return;
1506
1507         if (wnd->maximized) {
1508                 wl_shell_surface_set_toplevel(wnd->w_shell_surface);
1509                 wlt_window_set_size(wnd, wnd->saved_width, wnd->saved_height);
1510         } else {
1511                 wnd->saved_width = wnd->buffer.width;
1512                 wnd->saved_height = wnd->buffer.height;
1513                 wl_shell_surface_set_maximized(wnd->w_shell_surface, NULL);
1514         }
1515
1516         wnd->maximized = !wnd->maximized;
1517 }
1518
1519 struct ev_eloop *wlt_window_get_eloop(struct wlt_window *wnd)
1520 {
1521         if (!wnd)
1522                 return NULL;
1523
1524         return wnd->disp->eloop;
1525 }
1526
1527 void wlt_widget_destroy(struct wlt_widget *widget)
1528 {
1529         if (!widget)
1530                 return;
1531
1532         if (widget->destroy_cb)
1533                 widget->destroy_cb(widget, widget->data);
1534         shl_dlist_unlink(&widget->list);
1535         free(widget);
1536 }
1537
1538 struct wlt_window *wlt_widget_get_window(struct wlt_widget *widget)
1539 {
1540         if (!widget)
1541                 return NULL;
1542
1543         return widget->wnd;
1544 }
1545
1546 void wlt_widget_set_redraw_cb(struct wlt_widget *widget,
1547                               wlt_widget_redraw_cb cb)
1548 {
1549         if (!widget)
1550                 return;
1551
1552         widget->redraw_cb = cb;
1553 }
1554
1555 void wlt_widget_set_destroy_cb(struct wlt_widget *widget,
1556                                wlt_widget_destroy_cb cb)
1557 {
1558         if (!widget)
1559                 return;
1560
1561         widget->destroy_cb = cb;
1562 }
1563
1564 void wlt_widget_set_resize_cb(struct wlt_widget *widget,
1565                               wlt_widget_prepare_resize_cb prepare_cb,
1566                               wlt_widget_resize_cb cb)
1567 {
1568         if (!widget)
1569                 return;
1570
1571         widget->prepare_resize_cb = prepare_cb;
1572         widget->resize_cb = cb;
1573 }
1574
1575 void wlt_widget_set_pointer_cb(struct wlt_widget *widget,
1576                                wlt_widget_pointer_enter_cb enter_cb,
1577                                wlt_widget_pointer_leave_cb leave_cb,
1578                                wlt_widget_pointer_motion_cb motion_cb,
1579                                wlt_widget_pointer_button_cb button_cb)
1580 {
1581         if (!widget)
1582                 return;
1583
1584         widget->pointer_enter_cb = enter_cb;
1585         widget->pointer_leave_cb = leave_cb;
1586         widget->pointer_motion_cb = motion_cb;
1587         widget->pointer_button_cb = button_cb;
1588 }
1589
1590 void wlt_widget_set_keyboard_cb(struct wlt_widget *widget,
1591                                 wlt_widget_keyboard_cb cb)
1592 {
1593         if (!widget)
1594                 return;
1595
1596         widget->keyboard_cb = cb;
1597 }