1 #include <pangolin/platform.h>
2 #include <pangolin/display/display.h>
3 #include <pangolin/display/display_internal.h>
4 #include <pangolin/factory/factory_registry.h>
5 #include <pangolin/gl/colour.h>
6 #include <pangolin/gl/gldraw.h>
7 
8 #include <wayland-client.h>
9 #include <wayland-egl.h>
10 #include <EGL/egl.h>
11 #include <xkbcommon/xkbcommon.h>
12 #include <wayland-cursor.h>
13 #include <linux/input.h>
14 #include <sys/mman.h>
15 
16 #include <mutex>
17 #include <string.h>
18 #include <unistd.h>
19 #include <cstdlib>
20 
21 #if USE_WL_XDG
22 #include <xdg-shell-client-protocol.h>
23 #endif
24 
25 #define WAYLAND_VERSION_GE(MAJ, MIN) WAYLAND_VERSION_MAJOR >= MAJ && WAYLAND_VERSION_MINOR >= MIN
26 
27 namespace pangolin {
28 
29 extern __thread PangolinGl* context;
30 
31 namespace wayland {
32 
33 static const std::map<enum wl_shell_surface_resize, std::string> resize_cursor = {
34     {WL_SHELL_SURFACE_RESIZE_NONE, "grabbing"},
35     {WL_SHELL_SURFACE_RESIZE_TOP, "top_side"},
36     {WL_SHELL_SURFACE_RESIZE_BOTTOM, "bottom_side"},
37     {WL_SHELL_SURFACE_RESIZE_LEFT, "left_side"},
38     {WL_SHELL_SURFACE_RESIZE_TOP_LEFT, "top_left_corner"},
39     {WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT, "bottom_left_corner"},
40     {WL_SHELL_SURFACE_RESIZE_RIGHT, "right_side"},
41     {WL_SHELL_SURFACE_RESIZE_TOP_RIGHT, "top_right_corner"},
42     {WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT, "bottom_right_corner"}
43 };
44 
45 struct ButtonSurface {
46     struct wl_surface *surface;
47     struct wl_subsurface *subsurface;
48     struct wl_egl_window *egl_window;
49     EGLSurface egl_surface;
50     EGLContext egl_context;
51     EGLDisplay egl_display;
52     const int32_t x, y;
53     const uint width, height;
54     pangolin::Colour colour;
55 
56     enum type {
57         CLOSE = 100,
58         MAXIMISE
59     } function;
60 
ButtonSurfacepangolin::wayland::ButtonSurface61     ButtonSurface(wl_compositor* compositor, wl_subcompositor* subcompositor,
62                   wl_surface* source, EGLDisplay egl_display, EGLConfig config,
63                   int32_t x, int32_t y, uint width, uint height,
64                   type fnct, pangolin::Colour colour
65                   ) :
66         egl_display(egl_display),
67         x(x), y(y),
68         width(width), height(height),
69         colour(colour),
70         function(fnct)
71     {
72         surface = wl_compositor_create_surface(compositor);
73         subsurface = wl_subcompositor_get_subsurface(subcompositor, surface, source);
74         wl_subsurface_set_desync(subsurface);
75         egl_context = eglCreateContext (egl_display, config, EGL_NO_CONTEXT, nullptr);
76         egl_window = wl_egl_window_create(surface, width, height);
77         egl_surface = eglCreateWindowSurface(egl_display, config, (EGLNativeWindowType)egl_window, nullptr);
78     }
79 
~ButtonSurfacepangolin::wayland::ButtonSurface80     ~ButtonSurface() {
81         if(egl_surface) eglDestroySurface(egl_display, egl_surface);
82         if(egl_window)  wl_egl_window_destroy(egl_window);
83         if(egl_context) eglDestroyContext(egl_display, egl_context);
84 
85         if(subsurface)  wl_subsurface_destroy(subsurface);
86         if(surface)     wl_surface_destroy(surface);
87     }
88 
repositionpangolin::wayland::ButtonSurface89     void reposition(const int main_w) const {
90         wl_subsurface_set_position(subsurface, main_w-x-width, y);
91     }
92 
drawpangolin::wayland::ButtonSurface93     void draw() const {
94         eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
95         glClearColor(colour.r, colour.g, colour.b, colour.a);
96         glClear(GL_COLOR_BUFFER_BIT);
97         switch (function) {
98         case CLOSE:
99             glLineWidth(3);
100             glColor3f(1, 1, 1);
101             glBegin(GL_LINES);
102             glVertex2f(-1, -1);
103             glVertex2f(1, 1);
104             glVertex2f(1, -1);
105             glVertex2f(-1, 1);
106             glEnd();
107             break;
108         case MAXIMISE:
109             glLineWidth(2);
110             glColor3f(0, 0, 0);
111             glBegin(GL_LINE_LOOP);
112             glVertex2f(-0.7f, -0.7f);
113             glVertex2f(0.7f, -0.7f);
114             glVertex2f(0.7f, 0.7f);
115             glVertex2f(-0.7f, 0.7f);
116             glEnd();
117             glLineWidth(3);
118             glBegin(GL_LINES);
119             glVertex2f(+0.7f, +0.7f);
120             glVertex2f(-0.7f, +0.7f);
121             glEnd();
122             break;
123         }
124         eglSwapBuffers(egl_display, egl_surface);
125     }
126 };
127 
128 struct DecorationSurface {
129     struct wl_surface *surface;
130     struct wl_subsurface *subsurface;
131     struct wl_egl_window *egl_window;
132     EGLSurface egl_surface;
133     EGLContext egl_context;
134     EGLDisplay egl_display;
135     pangolin::Colour colour;
136     uint border_size;
137     uint title_bar_size;
138 
139     enum wl_shell_surface_resize function;
140 
DecorationSurfacepangolin::wayland::DecorationSurface141     DecorationSurface(wl_compositor* compositor, wl_subcompositor* subcompositor,
142                       wl_surface* source, EGLDisplay egl_display, EGLConfig config,
143                       const uint _border_size, const uint _title_bar_size,
144                       enum wl_shell_surface_resize type, pangolin::Colour _colour
145                       ) :
146         egl_display(egl_display),
147         colour(_colour),
148         border_size(_border_size),
149         title_bar_size(_title_bar_size),
150         function(type)
151     {
152         surface = wl_compositor_create_surface(compositor);
153         subsurface = wl_subcompositor_get_subsurface(subcompositor, surface, source);
154         wl_subsurface_set_desync(subsurface);
155         egl_context = eglCreateContext(egl_display, config, EGL_NO_CONTEXT, nullptr);
156         egl_window = wl_egl_window_create(surface, 50, 50);
157         egl_surface = eglCreateWindowSurface(egl_display, config, (EGLNativeWindowType)egl_window, nullptr);
158     }
159 
~DecorationSurfacepangolin::wayland::DecorationSurface160     ~DecorationSurface() {
161         if(egl_surface) eglDestroySurface(egl_display, egl_surface);
162         if(egl_window)  wl_egl_window_destroy(egl_window);
163         if(egl_context) eglDestroyContext(egl_display, egl_context);
164 
165         if(subsurface)  wl_subsurface_destroy(subsurface);
166         if(surface)     wl_surface_destroy(surface);
167     }
168 
calc_dimpangolin::wayland::DecorationSurface169     void calc_dim(const int main_w, const int main_h, int &x, int &y, int &w, int &h) const {
170         // get position and dimension from type and main surface
171         switch (function) {
172         case WL_SHELL_SURFACE_RESIZE_NONE:
173             x=0; y=-title_bar_size;
174             w=main_w; h=title_bar_size;
175             break;
176         case WL_SHELL_SURFACE_RESIZE_TOP:
177             x=0; y=-title_bar_size-border_size;
178             w=main_w; h=border_size;
179             break;
180         case WL_SHELL_SURFACE_RESIZE_BOTTOM:
181             x=0; y=main_h;
182             w=main_w; h=border_size;
183             break;
184         case WL_SHELL_SURFACE_RESIZE_LEFT:
185             x=-border_size; y=-title_bar_size;
186             w=border_size; h=main_h+title_bar_size;
187             break;
188         case WL_SHELL_SURFACE_RESIZE_TOP_LEFT:
189             x=-border_size; y=-border_size-title_bar_size;
190             w=border_size; h=border_size;
191             break;
192         case WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT:
193             x=-border_size; y=main_h;
194             w=border_size; h=border_size;
195             break;
196         case WL_SHELL_SURFACE_RESIZE_RIGHT:
197             x=main_w; y=-title_bar_size;
198             w=border_size; h=main_h+title_bar_size;
199             break;
200         case WL_SHELL_SURFACE_RESIZE_TOP_RIGHT:
201             x=main_w; y=-border_size-title_bar_size;
202             w=border_size; h=border_size;
203             break;
204         case WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT:
205             x=main_w; y=main_h;
206             w=border_size; h=border_size;
207             break;
208         }
209     }
210 
resizepangolin::wayland::DecorationSurface211     void resize(const int main_w, const int main_h) const {
212         int x=0, y=0, w=0, h=0;
213         calc_dim(main_w, main_h, x, y, w, h);
214         wl_subsurface_set_position(subsurface, x, y);
215         wl_egl_window_resize(egl_window, w, h, 0, 0);
216     }
217 
drawpangolin::wayland::DecorationSurface218     void draw() const {
219         eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
220         glClearColor(colour.r, colour.g, colour.b, colour.a);
221         glClear(GL_COLOR_BUFFER_BIT);
222         eglSwapBuffers(egl_display, egl_surface);
223     }
224 };
225 
226 struct Decoration {
Decorationpangolin::wayland::Decoration227     Decoration(const uint border_size,
228                const uint title_size,
229                const pangolin::Colour colour,
230                wl_compositor* compositor,
231                wl_subcompositor* subcompositor,
232                wl_surface* surface,
233                EGLDisplay egl_display,
234                EGLConfig config
235                ) :
236         border_size(border_size),
237         title_size(title_size),
238         colour(colour),
239         egl_display(egl_display),
240         compositor(compositor),
241         subcompositor(subcompositor),
242         surface(surface),
243         config(config)
244     { }
245 
createpangolin::wayland::Decoration246     void create() {
247         // reserve memory to prevent that DecorationSurface's destructor gets
248         // called by 'emplace_back'
249         decorations.reserve(9);
250 
251         // title bar, 2D movement
252         decorations.emplace_back(compositor, subcompositor, surface, egl_display, config, border_size, title_size, WL_SHELL_SURFACE_RESIZE_NONE, colour);
253 
254         // sides, 1D resizing
255         decorations.emplace_back(compositor, subcompositor, surface, egl_display, config, border_size, title_size, WL_SHELL_SURFACE_RESIZE_LEFT, colour);
256         decorations.emplace_back(compositor, subcompositor, surface, egl_display, config, border_size, title_size, WL_SHELL_SURFACE_RESIZE_RIGHT, colour);
257         decorations.emplace_back(compositor, subcompositor, surface, egl_display, config, border_size, title_size, WL_SHELL_SURFACE_RESIZE_TOP, colour);
258         decorations.emplace_back(compositor, subcompositor, surface, egl_display, config, border_size, title_size, WL_SHELL_SURFACE_RESIZE_BOTTOM, colour);
259 
260         // corners, 2D resizing
261         decorations.emplace_back(compositor, subcompositor, surface, egl_display, config, border_size, title_size, WL_SHELL_SURFACE_RESIZE_TOP_LEFT, colour);
262         decorations.emplace_back(compositor, subcompositor, surface, egl_display, config, border_size, title_size, WL_SHELL_SURFACE_RESIZE_TOP_RIGHT, colour);
263         decorations.emplace_back(compositor, subcompositor, surface, egl_display, config, border_size, title_size, WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT, colour);
264         decorations.emplace_back(compositor, subcompositor, surface, egl_display, config, border_size, title_size, WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT, colour);
265 
266         // buttons
267         buttons.reserve(2);
268         buttons.emplace_back(compositor, subcompositor, decorations[0].surface, egl_display, config, 5, 1, button_width, button_height,  ButtonSurface::type::CLOSE, pangolin::Colour(0, 110/255.0, 182/255.0));
269         buttons.emplace_back(compositor, subcompositor, decorations[0].surface, egl_display, config, 10+button_width, 1, button_width, button_height,  ButtonSurface::type::MAXIMISE, pangolin::Colour(1.0, 204/255.0f, 0));
270     }
271 
destroypangolin::wayland::Decoration272     void destroy() {
273         decorations.clear();
274         buttons.clear();
275     }
276 
resizepangolin::wayland::Decoration277     void resize(const int32_t width, const int32_t height) {
278         for(const DecorationSurface &d : decorations) { d.resize(width, height); }
279         for(const ButtonSurface &b : buttons) { b.reposition(width); }
280     }
281 
drawpangolin::wayland::Decoration282     void draw() {
283         for(const DecorationSurface &d : decorations) { d.draw(); }
284         for(const ButtonSurface &b : buttons) { b.draw(); }
285     }
286 
setTypeFromSurfacepangolin::wayland::Decoration287     void setTypeFromSurface(const wl_surface *surface) {
288         for(const DecorationSurface &d : decorations) {
289             if(d.surface==surface) {
290                 last_type = d.function;
291                 return;
292             }
293         }
294         for(const ButtonSurface &b : buttons) {
295             if(b.surface==surface) {
296                 last_type = b.function;
297                 return;
298             }
299         }
300         // surface is not part of the window decoration
301         last_type = -1;
302     }
303 
getCursorForCurrentSurfacepangolin::wayland::Decoration304     const std::string getCursorForCurrentSurface() const {
305         return resize_cursor.count((enum wl_shell_surface_resize)last_type) ? resize_cursor.at((enum wl_shell_surface_resize)last_type) : "left_ptr";
306     }
307 
308     std::vector<DecorationSurface> decorations;
309     int last_type;
310 
311     std::vector<ButtonSurface> buttons;
312 
313     const uint border_size;
314     const uint title_size;
315     const pangolin::Colour colour;
316     EGLDisplay egl_display;
317     wl_compositor* compositor;
318     wl_subcompositor* subcompositor;
319     wl_surface* surface;
320     EGLConfig config;
321 
322     static const uint button_width;
323     static const uint button_height;
324 };
325 
326 const uint Decoration::button_width = 25;
327 const uint Decoration::button_height = 15;
328 
329 struct WaylandDisplay {
330     WaylandDisplay(const int width, const int height, const std::string title = "");
331 
332     ~WaylandDisplay();
333 
334     struct wl_display *wdisplay = nullptr;
335     struct wl_registry *wregistry = nullptr;
336     struct wl_compositor *wcompositor = nullptr;
337     struct wl_subcompositor *wsubcompositor = nullptr;
338     struct wl_surface *wsurface = nullptr;
339     struct wl_egl_window *egl_window = nullptr;
340 #if USE_WL_XDG
341     struct xdg_wm_base *xshell = nullptr;
342     struct xdg_surface *xshell_surface = nullptr;
343     struct xdg_toplevel *xshell_toplevel = nullptr;
344 #else
345     struct wl_shell *wshell = nullptr;
346     struct wl_shell_surface *wshell_surface = nullptr;
347 #endif
348 
349     struct wl_seat *wseat = nullptr;
350     struct wl_keyboard *wkeyboard = nullptr;
351     struct wl_pointer *pointer = nullptr;
352 
353     // for cursor
354     struct wl_shm *shm = nullptr;
355     struct wl_cursor_theme *cursor_theme = nullptr;
356     struct wl_surface *cursor_surface = nullptr;
357 
358     // xkbcommon
359     struct xkb_context *xkb_context = nullptr;
360     struct xkb_keymap *keymap = nullptr;
361     struct xkb_state *xkb_state = nullptr;
362 
363     std::unique_ptr<Decoration> decoration;
364 
365     bool pressed = false;
366     int lastx=0;
367     int lasty=0;
368 
369     std::vector<EGLConfig> egl_configs;
370     EGLSurface egl_surface = nullptr;
371     EGLContext egl_context = nullptr;
372     EGLDisplay egl_display = nullptr;
373 
374     EGLint width, height;
375     bool is_fullscreen;
376     bool is_maximised;
377     static constexpr EGLint attribs[] = {
378         EGL_RENDERABLE_TYPE , EGL_OPENGL_BIT,
379         EGL_RED_SIZE        , 8,
380         EGL_GREEN_SIZE      , 8,
381         EGL_BLUE_SIZE       , 8,
382         EGL_ALPHA_SIZE      , 8,
383         EGL_DEPTH_SIZE      , 24,
384         EGL_STENCIL_SIZE    , 8,
385         EGL_NONE
386     };
387 };
388 
389 constexpr EGLint WaylandDisplay::attribs[];
390 
391 struct WaylandWindow : public PangolinGl
392 {
393     WaylandWindow(const int width, const int height, std::unique_ptr<WaylandDisplay> display);
394 
395     ~WaylandWindow() override;
396 
397     void ToggleFullscreen() override;
398 
399     void Move(const int x, const int y) override;
400 
401     void Resize(const unsigned int w, const unsigned int h) override;
402 
403     void MakeCurrent() override;
404 
405     void RemoveCurrent() override;
406 
407     void SwapBuffers() override;
408 
409     void ProcessEvents() override;
410 
411     std::unique_ptr<WaylandDisplay> display;
412 };
413 
414 // map wayland ids to pangolin ids
415 static const std::map<uint,int> wl_button_ids = {
416     {BTN_LEFT, 0},
417     {BTN_MIDDLE, 1},
418     {BTN_RIGHT, 2},
419 };
420 
421 static const std::map<uint,KeyModifier> wl_key_mod_ids = {
422     {KEY_LEFTSHIFT, KeyModifierShift},
423     {KEY_RIGHTSHIFT, KeyModifierShift},
424     {KEY_LEFTCTRL, KeyModifierCtrl},
425     {KEY_RIGHTCTRL, KeyModifierCtrl},
426     {KEY_LEFTALT, KeyModifierAlt},
427     {KEY_RIGHTALT, KeyModifierAlt},
428 };
429 
430 static const std::map<uint,int> wl_key_special_ids = {
431     {KEY_F1, PANGO_KEY_F1},
432     {KEY_F2, PANGO_KEY_F2},
433     {KEY_F3, PANGO_KEY_F3},
434     {KEY_F4, PANGO_KEY_F4},
435     {KEY_F5, PANGO_KEY_F5},
436     {KEY_F6, PANGO_KEY_F6},
437     {KEY_F7, PANGO_KEY_F7},
438     {KEY_F8, PANGO_KEY_F8},
439     {KEY_F9, PANGO_KEY_F9},
440     {KEY_F10, PANGO_KEY_F10},
441     {KEY_F11, PANGO_KEY_F11},
442     {KEY_F12, PANGO_KEY_F12},
443 
444     {KEY_LEFT, PANGO_KEY_LEFT},
445     {KEY_UP, PANGO_KEY_UP},
446     {KEY_RIGHT, PANGO_KEY_RIGHT},
447     {KEY_DOWN, PANGO_KEY_DOWN},
448 
449     {KEY_PAGEUP, PANGO_KEY_PAGE_UP},
450     {KEY_PAGEDOWN, PANGO_KEY_PAGE_DOWN},
451     {KEY_HOME, PANGO_KEY_HOME},
452     {KEY_END, PANGO_KEY_END},
453     {KEY_INSERT, PANGO_KEY_INSERT},
454 };
455 
456 #if USE_WL_XDG
handle_configure_toplevel(void * data,struct xdg_toplevel *,int32_t width,int32_t height,struct wl_array *)457 static void handle_configure_toplevel(void *data, struct xdg_toplevel */*xdg_toplevel*/, int32_t width, int32_t height, struct wl_array */*states*/) {
458 #else
459 static void handle_configure(void *data, struct wl_shell_surface */*shell_surface*/, uint32_t /*edges*/, int32_t width, int32_t height) {
460 #endif
461 
462     const static uint min_width = 70;
463     const static uint min_height = 70;
464 
465     WaylandDisplay* const w = static_cast<WaylandDisplay*>(data);
466 
467     const bool provided = !(width==0 && height==0);
468 
469     int main_w, main_h;
470     if(provided) {
471         // use the provided dimensions
472         if(w->is_fullscreen) {
473             // without decorations
474             main_w = width;
475             main_h = height;
476         }
477         else {
478             // with decorations
479             main_w = std::max(width-int(2*w->decoration->border_size), int(min_width));
480             main_h = std::max(height-2*int(w->decoration->border_size)-int(w->decoration->title_size), int(min_height));
481         }
482     }
483     else {
484         // restore from saved dimensions
485         main_w = std::max((w->width)-int(2*w->decoration->border_size), int(min_width));
486         main_h = std::max((w->height)-2*int(w->decoration->border_size)-int(w->decoration->title_size), int(min_height));
487     }
488 
489     // resize main surface
490     wl_egl_window_resize(w->egl_window, main_w, main_h, 0, 0);
491 
492     // set opaque region
493     struct wl_region* wregion = wl_compositor_create_region(w->wcompositor);
494     wl_region_add(wregion, 0, 0, main_w, main_h);
495     wl_surface_set_opaque_region(w->wsurface, wregion);
496     wl_region_destroy(wregion);
497 
498     // resize all decoration elements
499     w->decoration->resize(main_w, main_h);
500 
501     // notify Panglin views about resized area
502     pangolin::process::Resize(main_w, main_h);
503 }
504 
505 #if USE_WL_XDG
506 static void handle_configure(void */*data*/, struct xdg_surface *xdg_surface, uint32_t serial) {
507     xdg_surface_ack_configure(xdg_surface, serial);
508 }
509 
510 static const struct xdg_surface_listener shell_surface_listener = {
511     handle_configure,
512 };
513 
514 static void handle_toplevel_close(void */*data*/, struct xdg_toplevel */*xdg_toplevel*/) {
515     pangolin::QuitAll();
516 }
517 
518 static const struct xdg_toplevel_listener toplevel_listener = {
519     .configure = handle_configure_toplevel,
520     .close = handle_toplevel_close,
521 };
522 
523 static void xdg_wm_base_ping(void */*data*/, struct xdg_wm_base *xdg_wm_base, uint32_t serial) {
524     xdg_wm_base_pong(xdg_wm_base, serial);
525 }
526 
527 static const struct xdg_wm_base_listener shell_listener = {
528     .ping = xdg_wm_base_ping
529 };
530 #else
531 static void handle_ping(void */*data*/, struct wl_shell_surface *shell_surface, uint32_t serial) {
532     wl_shell_surface_pong(shell_surface, serial);
533 }
534 
535 static void handle_popup_done(void */*data*/, struct wl_shell_surface */*shell_surface*/) { }
536 
537 static const struct wl_shell_surface_listener shell_surface_listener = {
538     handle_ping,
539     handle_configure,
540     handle_popup_done
541 };
542 #endif
543 
544 static void pointer_handle_enter(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t /*sx*/, wl_fixed_t /*sy*/) {
545     WaylandDisplay* const w = static_cast<WaylandDisplay*>(data);
546     w->decoration->setTypeFromSurface(surface);
547 
548     const std::string cursor = w->decoration->getCursorForCurrentSurface();
549 
550     const auto image = wl_cursor_theme_get_cursor(w->cursor_theme, cursor.c_str())->images[0];
551     wl_pointer_set_cursor(pointer, serial, w->cursor_surface, image->hotspot_x, image->hotspot_y);
552     wl_surface_attach(w->cursor_surface, wl_cursor_image_get_buffer(image), 0, 0);
553     wl_surface_damage(w->cursor_surface, 0, 0, image->width, image->height);
554     wl_surface_commit(w->cursor_surface);
555 }
556 
557 static void pointer_handle_leave(void *data, struct wl_pointer */*pointer*/, uint32_t /*serial*/, struct wl_surface */*surface*/) {
558     WaylandDisplay* const w = static_cast<WaylandDisplay*>(data);
559     w->pressed = false;
560 }
561 
562 static void pointer_handle_motion(void *data, struct wl_pointer */*pointer*/, uint32_t /*time*/, wl_fixed_t sx, wl_fixed_t sy) {
563     WaylandDisplay* const w = static_cast<WaylandDisplay*>(data);
564 
565     w->lastx=wl_fixed_to_int(sx);
566     w->lasty=wl_fixed_to_int(sy);
567     if(w->pressed) {
568         pangolin::process::MouseMotion(w->lastx, w->lasty);
569     }
570     else {
571         pangolin::process::PassiveMouseMotion(w->lastx, w->lasty);
572     }
573 }
574 
575 static void pointer_handle_button(void *data, struct wl_pointer */*wl_pointer*/, uint32_t serial, uint32_t /*time*/, uint32_t button, uint32_t state) {
576     WaylandDisplay* const w = static_cast<WaylandDisplay*>(data);
577 
578     if(w->decoration->last_type<0) {
579         // input goes to pangoling view
580         if(!wl_button_ids.count(button))
581             return;
582 
583         w->pressed = (state==WL_POINTER_BUTTON_STATE_PRESSED);
584         pangolin::process::Mouse(wl_button_ids.at(button), (state==WL_POINTER_BUTTON_STATE_RELEASED), w->lastx, w->lasty);
585     }
586     else {
587         // input goes to window decoration
588         // resizing using window decoration
589         if((button==BTN_LEFT) && (state==WL_POINTER_BUTTON_STATE_PRESSED)) {
590             switch (w->decoration->last_type) {
591             case WL_SHELL_SURFACE_RESIZE_NONE:
592 #if USE_WL_XDG
593                 xdg_toplevel_move(w->xshell_toplevel, w->wseat, serial);
594 #else
595                 wl_shell_surface_move(w->wshell_surface, w->wseat, serial);
596 #endif
597                 break;
598             case WL_SHELL_SURFACE_RESIZE_TOP:
599             case WL_SHELL_SURFACE_RESIZE_BOTTOM:
600             case WL_SHELL_SURFACE_RESIZE_LEFT:
601             case WL_SHELL_SURFACE_RESIZE_TOP_LEFT:
602             case WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT:
603             case WL_SHELL_SURFACE_RESIZE_RIGHT:
604             case WL_SHELL_SURFACE_RESIZE_TOP_RIGHT:
605             case WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT:
606 #if USE_WL_XDG
607                 xdg_toplevel_resize(w->xshell_toplevel, w->wseat, serial, w->decoration->last_type);
608 #else
609                 wl_shell_surface_resize(w->wshell_surface, w->wseat, serial, w->decoration->last_type);
610 #endif
611                 break;
612             case ButtonSurface::type::CLOSE:
613                 pangolin::QuitAll();
614                 break;
615             case ButtonSurface::type::MAXIMISE:
616                 w->is_maximised = !w->is_maximised;
617                 if(w->is_maximised) {
618 #if USE_WL_XDG
619                     xdg_toplevel_set_maximized(w->xshell_toplevel);
620 #else
621                     // store original window size
622                     wl_egl_window_get_attached_size(w->egl_window, &w->width, &w->height);
623                     wl_shell_surface_set_maximized(w->wshell_surface, nullptr);
624 #endif
625                 }
626                 else {
627 #if USE_WL_XDG
628                     xdg_toplevel_unset_maximized(w->xshell_toplevel);
629 #else
630                     wl_shell_surface_set_toplevel(w->wshell_surface);
631                     handle_configure(data, nullptr, 0,
632                                      w->width+2*w->decoration->border_size,
633                                      w->height+2*w->decoration->border_size+w->decoration->title_size);
634 #endif
635                 }
636 
637                 break;
638             }
639         }
640     }
641 }
642 
643 static void pointer_handle_axis(void *data, struct wl_pointer */*wl_pointer*/, uint32_t /*time*/, uint32_t axis, wl_fixed_t value) {
644     WaylandDisplay* const w = static_cast<WaylandDisplay*>(data);
645 
646     int button_id = -1;
647     switch (axis) {
648     case REL_X: button_id = (value<0) ? 3 : 4; break;   // up, down
649     case REL_Y: button_id = (value<0) ? 5 : 6; break;   // left, right
650     }
651 
652     if(button_id>0) {
653         pangolin::process::Mouse(button_id, 0, w->lastx, w->lasty);
654     }
655 }
656 
657 #if WAYLAND_VERSION_GE(1,12)
658 
659 static void pointer_handle_frame(void */*data*/, struct wl_pointer */*wl_pointer*/) { }
660 
661 static void pointer_handle_axis_source(void */*data*/, struct wl_pointer */*wl_pointer*/, uint32_t /*axis_source*/) { }
662 
663 static void pointer_handle_axis_stop(void */*data*/, struct wl_pointer */*wl_pointer*/, uint32_t /*time*/, uint32_t /*axis*/) { }
664 
665 static void pointer_handle_axis_discrete(void */*data*/, struct wl_pointer */*wl_pointer*/, uint32_t /*axis*/, int32_t /*discrete*/) { }
666 
667 #endif
668 
669 static const struct wl_pointer_listener pointer_listener = {
670     pointer_handle_enter,
671     pointer_handle_leave,
672     pointer_handle_motion,
673     pointer_handle_button,
674     pointer_handle_axis,
675 #if WAYLAND_VERSION_GE(1,12)
676     pointer_handle_frame,
677     pointer_handle_axis_source,
678     pointer_handle_axis_stop,
679     pointer_handle_axis_discrete,
680 #endif
681 };
682 
683 static void keyboard_handle_keymap(void *data, struct wl_keyboard */*keyboard*/, uint32_t /*format*/, int fd, uint32_t size) {
684     WaylandDisplay* const w = static_cast<WaylandDisplay*>(data);
685 
686     char *keymap_string = static_cast<char*>(mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0));
687     xkb_keymap_unref(w->keymap);
688     w->keymap = xkb_keymap_new_from_string(w->xkb_context, keymap_string, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
689     munmap(keymap_string, size);
690     close(fd);
691     xkb_state_unref(w->xkb_state);
692     w->xkb_state = xkb_state_new(w->keymap);
693 }
694 
695 static void keyboard_handle_enter(void */*data*/, struct wl_keyboard */*keyboard*/, uint32_t /*serial*/, struct wl_surface */*surface*/, struct wl_array */*keys*/) { }
696 
697 static void keyboard_handle_leave(void */*data*/, struct wl_keyboard */*keyboard*/, uint32_t /*serial*/, struct wl_surface */*surface*/) { }
698 
699 static void keyboard_handle_key(void *data, struct wl_keyboard */*keyboard*/, uint32_t /*serial*/, uint32_t /*time*/, uint32_t key, uint32_t state) {
700     WaylandDisplay* const w = static_cast<WaylandDisplay*>(data);
701 
702     // modifier keys
703     if(wl_key_mod_ids.count(key)) {
704         if(state==WL_KEYBOARD_KEY_STATE_PRESSED) {
705             pangolin::context->mouse_state |=  wl_key_mod_ids.at(key);
706         }
707         else if (state==WL_KEYBOARD_KEY_STATE_RELEASED) {
708             pangolin::context->mouse_state &= ~wl_key_mod_ids.at(key);
709         }
710         return;
711     }
712 
713     // character and special keys
714     int pango_key = -1;
715     if(wl_key_special_ids.count(key)) {
716         // special keys
717         pango_key = PANGO_SPECIAL + wl_key_special_ids.at(key);
718     }
719     else {
720         // character keys
721         const uint32_t utf32 = xkb_state_key_get_utf32(w->xkb_state, key+8);
722         // filter non-ASCII
723         if(utf32>0 && utf32<=127) {
724             pango_key = int(utf32);
725         }
726     }
727 
728     if(pango_key>0) {
729         if(state==WL_KEYBOARD_KEY_STATE_PRESSED) {
730             pangolin::process::Keyboard(uint8_t(pango_key), w->lastx, w->lasty);
731         }else if(state==WL_KEYBOARD_KEY_STATE_RELEASED){
732             pangolin::process::KeyboardUp(uint8_t(pango_key), w->lastx, w->lasty);
733         }
734     }
735 }
736 
737 static void 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) {
738     WaylandDisplay* const w = static_cast<WaylandDisplay*>(data);
739 
740     xkb_state_update_mask(w->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
741 }
742 
743 #if WAYLAND_VERSION_GE(1,12)
744 
745 static void keyboard_handle_repeat_info(void */*data*/, struct wl_keyboard */*wl_keyboard*/, int32_t /*rate*/, int32_t /*delay*/) { }
746 
747 #endif
748 
749 static const struct wl_keyboard_listener keyboard_listener = {
750     keyboard_handle_keymap,
751     keyboard_handle_enter,
752     keyboard_handle_leave,
753     keyboard_handle_key,
754     keyboard_handle_modifiers,
755 #if WAYLAND_VERSION_GE(1,12)
756     keyboard_handle_repeat_info,
757 #endif
758 };
759 
760 static void seat_handle_capabilities(void *data, struct wl_seat *seat, uint32_t caps1) {
761     WaylandDisplay* const w = static_cast<WaylandDisplay*>(data);
762 
763     enum wl_seat_capability caps;
764     caps = (enum wl_seat_capability)caps1;
765     if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
766         w->wkeyboard = wl_seat_get_keyboard(seat);
767         wl_keyboard_add_listener(w->wkeyboard, &keyboard_listener, data);
768     } else {
769         wl_keyboard_destroy(w->wkeyboard);
770         w->wkeyboard = nullptr;
771     }
772     if (caps & WL_SEAT_CAPABILITY_POINTER) {
773         w->pointer = wl_seat_get_pointer(seat);
774         w->cursor_surface = wl_compositor_create_surface(w->wcompositor);
775         wl_pointer_add_listener(w->pointer, &pointer_listener, data);
776     } else {
777         wl_pointer_destroy(w->pointer);
778         w->pointer = nullptr;
779     }
780 }
781 
782 static void seat_handle_name(void */*data*/, struct wl_seat */*wl_seat*/, const char */*name*/) { }
783 
784 static const struct wl_seat_listener seat_listener = {
785     seat_handle_capabilities,
786     seat_handle_name,
787 };
788 
789 static void global_registry_handler(void *data, struct wl_registry *registry, uint32_t id, const char *interface, uint32_t version) {
790     WaylandDisplay* const w = static_cast<WaylandDisplay*>(data);
791 
792     if (strcmp(interface, wl_compositor_interface.name) == 0) {
793         w->wcompositor = reinterpret_cast<wl_compositor*> (wl_registry_bind(registry, id, &wl_compositor_interface, version));
794     }
795     else if (strcmp(interface, wl_subcompositor_interface.name) == 0) {
796         w->wsubcompositor = static_cast<wl_subcompositor*>(wl_registry_bind(registry, id, &wl_subcompositor_interface, version));
797     }
798 #if USE_WL_XDG
799     else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
800         w->xshell = reinterpret_cast<xdg_wm_base*> (wl_registry_bind(registry, id, &xdg_wm_base_interface, version));
801     }
802 #else
803     else if (strcmp(interface, wl_shell_interface.name) == 0) {
804         w->wshell = reinterpret_cast<wl_shell*> (wl_registry_bind(registry, id, &wl_shell_interface, version));
805     }
806 #endif
807     else if (strcmp(interface, wl_seat_interface.name) == 0) {
808         w->wseat = reinterpret_cast<wl_seat*>(wl_registry_bind(registry, id, &wl_seat_interface, version));
809         wl_seat_add_listener(w->wseat, &seat_listener, data);
810     }
811     else if (strcmp(interface, wl_shm_interface.name) == 0) {
812         w->shm = static_cast<wl_shm*>(wl_registry_bind(registry, id, &wl_shm_interface, version));
813         w->cursor_theme = wl_cursor_theme_load(nullptr, 16, w->shm);
814     }
815 }
816 
817 static void global_registry_remover(void */*data*/, struct wl_registry */*registry*/, uint32_t /*id*/) { }
818 
819 static const struct wl_registry_listener wregistry_listener = {
820     global_registry_handler,
821     global_registry_remover
822 };
823 
824 WaylandDisplay::WaylandDisplay(const int width, const int height, const std::string title) : width(width), height(height) {
825     xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
826 
827     wdisplay = wl_display_connect(nullptr);
828     if (wdisplay == nullptr) {
829         throw std::runtime_error("Cannot connect to Wayland compositor!");
830     }
831 
832     wregistry = wl_display_get_registry(wdisplay);
833     wl_registry_add_listener(wregistry, &wregistry_listener, this);
834 
835     wl_display_roundtrip(wdisplay);
836 
837     egl_display = eglGetDisplay((EGLNativeDisplayType)wdisplay);
838     if(!egl_display) {
839         std::cerr << "Failed to open EGL display" << std::endl;
840     }
841 
842     EGLint major, minor;
843     if(eglInitialize(egl_display, &major, &minor)==EGL_FALSE) {
844         std::cerr << "EGL init failed" << std::endl;
845     }
846 
847     if(eglBindAPI(EGL_OPENGL_API)==EGL_FALSE) {
848         std::cerr << "EGL bind failed" << std::endl;
849     }
850 
851     EGLint count;
852     eglGetConfigs(egl_display, nullptr, 0, &count);
853 
854     egl_configs.resize(count);
855 
856     EGLint numConfigs;
857     eglChooseConfig(egl_display, attribs, egl_configs.data(), count, &numConfigs);
858 
859     egl_context = eglCreateContext(egl_display, egl_configs[0], EGL_NO_CONTEXT, nullptr);
860 
861     wsurface = wl_compositor_create_surface(wcompositor);
862 
863     egl_window = wl_egl_window_create(wsurface, width, height);
864     if(!egl_window) {
865         std::cerr << "Cannot create EGL window" << std::endl;
866     }
867 
868     egl_surface = eglCreateWindowSurface(egl_display, egl_configs[0], (EGLNativeWindowType)egl_window, nullptr);
869     if (egl_surface == EGL_NO_SURFACE) {
870         std::cerr << "Cannot create EGL surface" << std::endl;
871     }
872 
873     if(
874 #if USE_WL_XDG
875     xshell
876 #else
877     wshell
878 #endif
879     ==nullptr) {
880         throw std::runtime_error("No Wayland shell available!");
881     }
882 
883 #if USE_WL_XDG
884     xdg_wm_base_add_listener(xshell, &shell_listener, this);
885     xshell_surface = xdg_wm_base_get_xdg_surface(xshell, wsurface);
886     xdg_surface_add_listener(xshell_surface, &shell_surface_listener, this);
887     xshell_toplevel = xdg_surface_get_toplevel(xshell_surface);
888     xdg_toplevel_add_listener(xshell_toplevel, &toplevel_listener, this);
889     xdg_toplevel_set_title(xshell_toplevel, title.c_str());
890     xdg_toplevel_set_app_id(xshell_toplevel, title.c_str());
891 #else
892     wshell_surface = wl_shell_get_shell_surface(wshell, wsurface);
893     wl_shell_surface_add_listener(wshell_surface, &shell_surface_listener, this);
894     wl_shell_surface_set_toplevel(wshell_surface);
895     wl_shell_surface_set_title(wshell_surface, title.c_str());
896     wl_shell_surface_set_class(wshell_surface, title.c_str());
897 #endif
898 
899     wl_display_sync(wdisplay);
900 
901     // construct window decoration
902     const pangolin::Colour grey(0.5f, 0.5f, 0.5f, 0.5f);
903     decoration = std::unique_ptr<Decoration>(new Decoration(5, 20, grey, wcompositor, wsubcompositor, wsurface, egl_display, egl_configs[0]));
904     decoration->create();
905     decoration->resize(width, height);
906 }
907 
908 WaylandDisplay::~WaylandDisplay() {
909     if(decoration)  decoration->destroy();
910 
911     // cleanup EGL
912     if(egl_context) eglDestroyContext(egl_display, egl_context);
913     if(egl_surface) eglDestroySurface(egl_display, egl_surface);
914     if(egl_window)  wl_egl_window_destroy(egl_window);
915     if(egl_display) eglTerminate(egl_display);
916 
917     // cleanup Wayland
918 #if USE_WL_XDG
919     if(xshell_surface)  xdg_surface_destroy(xshell_surface);
920     if(xshell_toplevel) xdg_toplevel_destroy(xshell_toplevel);
921     if(xshell)          xdg_wm_base_destroy(xshell);
922 #else
923     if(wshell_surface)  wl_shell_surface_destroy(wshell_surface);
924     if(wshell)          wl_shell_destroy(wshell);
925 #endif
926 
927     if(wsurface)        wl_surface_destroy(wsurface);
928     if(wregistry)       wl_registry_destroy(wregistry);
929     if(wdisplay)        wl_display_disconnect(wdisplay);
930 
931     if(xkb_context) xkb_context_unref(xkb_context);
932 }
933 
934 WaylandWindow::WaylandWindow(const int w, const int h, std::unique_ptr<WaylandDisplay> display) : display(std::move(display)){
935     windowed_size[0] = w;
936     windowed_size[1] = h;
937 }
938 
939 WaylandWindow::~WaylandWindow() { }
940 
941 void WaylandWindow::MakeCurrent() {
942     eglMakeCurrent(display->egl_display, display->egl_surface, display->egl_surface, display->egl_context);
943     context = this;
944 }
945 
946 void WaylandWindow::RemoveCurrent() {
947     eglMakeCurrent(display->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
948 }
949 
950 void WaylandWindow::ToggleFullscreen() {
951     is_fullscreen = !is_fullscreen;         // state for Pangolin
952     display->is_fullscreen = is_fullscreen; // state for Wayland
953     if(is_fullscreen) {
954         display->decoration->destroy();
955 #if USE_WL_XDG
956         xdg_toplevel_set_fullscreen(display->xshell_toplevel, nullptr);
957 #else
958         wl_shell_surface_set_fullscreen(display->wshell_surface, WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0, nullptr);
959 #endif
960     }
961     else {
962         display->decoration->create();
963 #if USE_WL_XDG
964         xdg_toplevel_unset_fullscreen(display->xshell_toplevel);
965 #else
966         wl_shell_surface_set_toplevel(display->wshell_surface);
967 #endif
968 
969 #if !USE_WL_XDG
970         if(display->is_maximised) {
971             wl_shell_surface_set_maximized(display->wshell_surface, nullptr);
972         }
973         else {
974             handle_configure(static_cast<void*>(display.get()), nullptr, 0,
975                              windowed_size[0]+2*display->decoration->border_size,
976                              windowed_size[1]+2*display->decoration->border_size+display->decoration->title_size);
977         }
978 #endif
979     }
980 
981     wl_display_sync(display->wdisplay);
982 }
983 
984 void WaylandWindow::Move(const int /*x*/, const int /*y*/) { }
985 
986 void WaylandWindow::Resize(const unsigned int /*w*/, const unsigned int /*h*/) { }
987 
988 void WaylandWindow::ProcessEvents() { }
989 
990 void WaylandWindow::SwapBuffers() {
991     eglSwapBuffers(display->egl_display, display->egl_surface);
992 
993     // draw all decoration elements
994     display->decoration->draw();
995 
996     MakeCurrent();
997 
998     wl_display_roundtrip(display->wdisplay);
999 }
1000 
1001 
1002 std::unique_ptr<WindowInterface> CreateWaylandWindowAndBind(const std::string window_title, const int w, const int h, const std::string /*display_name*/, const bool /*double_buffered*/, const int /*sample_buffers*/, const int /*samples*/) {
1003 
1004     try{
1005         std::unique_ptr<WaylandDisplay> newdisplay = std::unique_ptr<WaylandDisplay>(new WaylandDisplay(w, h, window_title));
1006 
1007         // glewInit() fails with SIGSEGV for glew < 2.0 since it links to GLX
1008         if(atoi((char*)glewGetString(GLEW_VERSION_MAJOR))<2)
1009             return nullptr;
1010 
1011         WaylandWindow* win = new WaylandWindow(w, h, std::move(newdisplay));
1012 
1013         return std::unique_ptr<WindowInterface>(win);
1014     }
1015     catch(const std::runtime_error&) {
1016         // return null pointer for fallback to X11
1017         return nullptr;
1018     }
1019 }
1020 
1021 } // namespace wayland
1022 
PANGOLIN_REGISTER_FACTORY(WaylandWindow)1023 PANGOLIN_REGISTER_FACTORY(WaylandWindow)
1024 {
1025   struct WaylandWindowFactory : public FactoryInterface<WindowInterface> {
1026     std::unique_ptr<WindowInterface> Open(const Uri& uri) override {
1027 
1028       const std::string window_title = uri.Get<std::string>("window_title", "window");
1029       const int w = uri.Get<int>("w", 640);
1030       const int h = uri.Get<int>("h", 480);
1031       const std::string display_name = uri.Get<std::string>("display_name", "");
1032       const bool double_buffered = uri.Get<bool>("double_buffered", true);
1033       const int sample_buffers = uri.Get<int>("sample_buffers", 1);
1034       const int samples = uri.Get<int>("samples", 1);
1035       return std::unique_ptr<WindowInterface>(wayland::CreateWaylandWindowAndBind(window_title, w, h, display_name, double_buffered, sample_buffers, samples));
1036     }
1037 
1038     virtual ~WaylandWindowFactory() { }
1039   };
1040 
1041     auto factory = std::make_shared<WaylandWindowFactory>();
1042     FactoryRegistry<WindowInterface>::I().RegisterFactory(factory, 10, "wayland");
1043     FactoryRegistry<WindowInterface>::I().RegisterFactory(factory, 9,  "linux");
1044     FactoryRegistry<WindowInterface>::I().RegisterFactory(factory, 90,  "default");
1045 }
1046 
1047 } // namespace pangolin
1048 
1049