1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 #define UNICODE
6 
7 #include <algorithm>
8 #include <assert.h>
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <map>
12 #include <math.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <sys/mman.h>
16 #include <unistd.h>
17 #include <unordered_map>
18 #include <vector>
19 
20 #include <wayland-client.h>
21 #include <wayland-egl.h>
22 
23 #include <EGL/egl.h>
24 #include <EGL/eglext.h>
25 #include <GL/gl.h>
26 #include <GLES2/gl2.h>
27 
28 #include "viewporter-client-protocol.h"
29 #include "xdg-shell-client-protocol.h"
30 
31 #define UNUSED(x) (void)(x)
32 
33 #define MIN(x, y) (((x) < (y)) ? (x) : (y))
34 #define MAX(x, y) (((x) > (y)) ? (x) : (y))
35 
36 #define NUM_QUERIES 2
37 
38 #define VIRTUAL_OFFSET 512 * 1024
39 
40 enum SyncMode {
41   None_ = 0,
42   Swap = 1,
43   Commit = 2,
44   Flush = 3,
45   Query = 4,
46 };
47 
48 // The OS compositor representation of a picture cache tile.
49 struct Tile {
50   uint64_t surface_id;
51   int x;
52   int y;
53 
54   struct wl_surface* surface;
55   struct wl_subsurface* subsurface;
56   struct wp_viewport* viewport;
57   struct wl_egl_window* egl_window;
58   EGLSurface egl_surface;
59   bool is_visible;
60 
61   std::vector<EGLint> damage_rects;
62 };
63 
64 struct TileKey {
65   int x;
66   int y;
67 
TileKeyTileKey68   TileKey(int ax, int ay) : x(ax), y(ay) {}
69 };
70 
operator ==(const TileKey & k0,const TileKey & k1)71 bool operator==(const TileKey& k0, const TileKey& k1) {
72   return k0.x == k1.x && k0.y == k1.y;
73 }
74 
75 struct TileKeyHasher {
operator ()TileKeyHasher76   size_t operator()(const TileKey& key) const { return key.x ^ key.y; }
77 };
78 
79 struct Surface {
80   uint64_t id;
81   int tile_width;
82   int tile_height;
83   bool is_opaque;
84   std::unordered_map<TileKey, Tile*, TileKeyHasher> tiles;
85 };
86 
87 struct WLDisplay {
88   struct wl_display* display;
89   struct wl_registry* registry;
90   struct wl_compositor* compositor;
91   struct wl_subcompositor* subcompositor;
92   struct xdg_wm_base* wm_base;
93   struct wl_seat* seat;
94   struct wl_pointer* pointer;
95   struct wl_touch* touch;
96   struct wl_keyboard* keyboard;
97   struct wl_shm* shm;
98   struct wl_cursor_theme* cursor_theme;
99   struct wl_cursor* default_cursor;
100   struct wl_surface* cursor_surface;
101   struct wp_viewporter* viewporter;
102 
103   PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC swap_buffers_with_damage;
104 };
105 
106 struct WLGeometry {
107   int width, height;
108 };
109 
110 struct WLWindow {
111   WLGeometry geometry;
112   bool enable_compositor;
113   SyncMode sync_mode;
114   bool closed;
115 
116   WLDisplay* display;
117   struct wl_surface* surface;
118   struct xdg_surface* xdg_surface;
119   struct xdg_toplevel* xdg_toplevel;
120   struct wl_callback* callback;
121   struct wp_viewport* viewport;
122   bool wait_for_configure;
123 
124   struct wl_egl_window* egl_window;
125   EGLSurface egl_surface;
126 
127   EGLDeviceEXT eglDevice;
128   EGLDisplay eglDisplay;
129   EGLContext eglContext;
130   EGLConfig config;
131 
132   // Maintain list of layer state between frames to avoid visual tree rebuild.
133   std::vector<uint64_t> currentLayers;
134   std::vector<uint64_t> prevLayers;
135 
136   // Maps WR surface IDs to each OS surface
137   std::unordered_map<uint64_t, Surface> surfaces;
138   std::vector<Tile*> destroyedTiles;
139   std::vector<Tile*> hiddenTiles;
140 };
141 
142 extern "C" {
143 
144 static void init_wl_registry(WLWindow* window);
145 static void init_xdg_window(WLWindow* window);
146 
com_wl_create_window(int width,int height,bool enable_compositor,SyncMode sync_mode)147 WLWindow* com_wl_create_window(int width, int height, bool enable_compositor,
148                                SyncMode sync_mode) {
149   WLDisplay* display = new WLDisplay;
150   WLWindow* window = new WLWindow;
151 
152   window->display = display;
153   window->geometry.width = width;
154   window->geometry.height = height;
155   window->enable_compositor = enable_compositor;
156   window->sync_mode = sync_mode;
157   window->closed = false;
158 
159   display->display = wl_display_connect(NULL);
160   assert(display->display);
161 
162   init_wl_registry(window);
163   if (enable_compositor && !display->viewporter) {
164     fprintf(stderr, "Native compositor mode requires wp_viewporter support\n");
165     window->closed = true;
166   }
167 
168   window->eglDisplay =
169       eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, display->display, NULL);
170 
171   eglInitialize(window->eglDisplay, nullptr, nullptr);
172   eglBindAPI(EGL_OPENGL_API);
173 
174   EGLint num_configs = 0;
175   EGLint cfg_attribs[] = {EGL_SURFACE_TYPE,
176                           EGL_WINDOW_BIT,
177                           EGL_RENDERABLE_TYPE,
178                           EGL_OPENGL_BIT,
179                           EGL_RED_SIZE,
180                           8,
181                           EGL_GREEN_SIZE,
182                           8,
183                           EGL_BLUE_SIZE,
184                           8,
185                           EGL_ALPHA_SIZE,
186                           8,
187                           EGL_DEPTH_SIZE,
188                           24,
189                           EGL_NONE};
190   EGLConfig configs[32];
191 
192   eglChooseConfig(window->eglDisplay, cfg_attribs, configs,
193                   sizeof(configs) / sizeof(EGLConfig), &num_configs);
194   assert(num_configs > 0);
195   window->config = configs[0];
196 
197   EGLint ctx_attribs[] = {EGL_CONTEXT_OPENGL_PROFILE_MASK,
198                           EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
199                           EGL_CONTEXT_MAJOR_VERSION,
200                           3,
201                           EGL_CONTEXT_MINOR_VERSION,
202                           2,
203                           EGL_NONE};
204 
205   // Create an EGL context that can be used for drawing
206   window->eglContext = eglCreateContext(window->eglDisplay, window->config,
207                                         EGL_NO_CONTEXT, ctx_attribs);
208 
209   window->surface = wl_compositor_create_surface(display->compositor);
210   init_xdg_window(window);
211 
212   struct wl_region* region =
213       wl_compositor_create_region(window->display->compositor);
214   wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX);
215   wl_surface_set_opaque_region(window->surface, region);
216   wl_region_destroy(region);
217 
218   if (enable_compositor) {
219     xdg_toplevel_set_title(window->xdg_toplevel,
220                            "example-compositor (Wayland)");
221   } else {
222     xdg_toplevel_set_title(window->xdg_toplevel, "example-compositor (Simple)");
223   }
224 
225   window->wait_for_configure = true;
226   wl_surface_commit(window->surface);
227 
228   EGLBoolean ok = eglMakeCurrent(window->eglDisplay, EGL_NO_SURFACE,
229                                  EGL_NO_SURFACE, window->eglContext);
230   assert(ok);
231 
232   display->swap_buffers_with_damage =
233       (PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC)eglGetProcAddress(
234           "eglSwapBuffersWithDamageKHR");
235 
236   return window;
237 }
238 
com_wl_tick(WLWindow * window)239 bool com_wl_tick(WLWindow* window) {
240   if (window->wait_for_configure) {
241     int ret = 0;
242     while (window->wait_for_configure && !window->closed && ret != -1) {
243       wl_display_dispatch(window->display->display);
244     }
245   } else {
246     wl_display_dispatch_pending(window->display->display);
247   }
248 
249   return !window->closed;
250 }
251 
unmap_hidden_tiles(WLWindow * window)252 static void unmap_hidden_tiles(WLWindow* window) {
253   for (Tile* tile : window->hiddenTiles) {
254     if (tile->subsurface) {
255       wl_subsurface_destroy(tile->subsurface);
256       tile->subsurface = nullptr;
257     }
258   }
259   window->hiddenTiles.clear();
260 }
261 
clean_up_tiles(WLWindow * window)262 static void clean_up_tiles(WLWindow* window) {
263   for (Tile* tile : window->destroyedTiles) {
264     eglDestroySurface(window->eglDisplay, tile->egl_surface);
265     wl_egl_window_destroy(tile->egl_window);
266     wp_viewport_destroy(tile->viewport);
267     wl_surface_destroy(tile->surface);
268     delete tile;
269   }
270   window->destroyedTiles.clear();
271 }
272 
handle_callback(void * data,struct wl_callback * callback,uint32_t time)273 static void handle_callback(void* data, struct wl_callback* callback,
274                             uint32_t time) {
275   WLWindow* window = (WLWindow*)data;
276   UNUSED(time);
277 
278   assert(window->callback == callback);
279 
280   wl_callback_destroy(callback);
281   window->callback = nullptr;
282 }
283 
284 static const struct wl_callback_listener frame_listener = {handle_callback};
285 
com_wl_swap_buffers(WLWindow * window)286 void com_wl_swap_buffers(WLWindow* window) {
287   if (window->enable_compositor) {
288     for (auto surface_it = window->surfaces.begin();
289          surface_it != window->surfaces.end(); ++surface_it) {
290       Surface* surface = &surface_it->second;
291 
292       for (auto tile_it = surface->tiles.begin();
293            tile_it != surface->tiles.end(); ++tile_it) {
294         Tile* tile = tile_it->second;
295 
296         if (!tile->damage_rects.empty() && tile->is_visible) {
297           eglMakeCurrent(window->eglDisplay, tile->egl_surface,
298                          tile->egl_surface, window->eglContext);
299           eglSwapInterval(window->eglDisplay, 0);
300 
301           /* if (window->display->swap_buffers_with_damage) {
302             window->display->swap_buffers_with_damage(
303                 window->eglDisplay, tile->egl_surface,
304                 tile->damage_rects.data(), tile->damage_rects.size() / 4);
305           } else */
306           eglSwapBuffers(window->eglDisplay, tile->egl_surface);
307           tile->damage_rects.clear();
308 
309           eglMakeCurrent(window->eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
310                          window->eglContext);
311         } else {
312           wl_surface_commit(tile->surface);
313         }
314       }
315     }
316     wl_surface_commit(window->surface);
317     unmap_hidden_tiles(window);
318     clean_up_tiles(window);
319 
320     int ret = 0;
321     switch (window->sync_mode) {
322       case SyncMode::None_:
323         wl_display_roundtrip(window->display->display);
324         break;
325       case SyncMode::Swap:
326         window->callback = wl_surface_frame(window->surface);
327         wl_callback_add_listener(window->callback, &frame_listener, window);
328         wl_surface_commit(window->surface);
329 
330         while (window->callback && !window->closed && ret != -1) {
331           ret = wl_display_dispatch(window->display->display);
332         }
333         break;
334       default:
335         assert(false);
336         break;
337     }
338   } else {
339     // If not using native mode, then do a normal EGL swap buffers.
340     switch (window->sync_mode) {
341       case SyncMode::None_:
342         eglSwapInterval(window->eglDisplay, 0);
343         break;
344       case SyncMode::Swap:
345         eglSwapInterval(window->eglDisplay, 1);
346         break;
347       default:
348         assert(false);
349         break;
350     }
351     eglSwapBuffers(window->eglDisplay, window->egl_surface);
352   }
353 }
354 
355 // Create a new native surface
com_wl_create_surface(WLWindow * window,uint64_t surface_id,int tile_width,int tile_height,bool is_opaque)356 void com_wl_create_surface(WLWindow* window, uint64_t surface_id,
357                            int tile_width, int tile_height, bool is_opaque) {
358   assert(window->surfaces.count(surface_id) == 0);
359 
360   Surface surface;
361   surface.id = surface_id;
362   surface.tile_width = tile_width;
363   surface.tile_height = tile_height;
364   surface.is_opaque = is_opaque;
365 
366   window->surfaces.emplace(surface_id, surface);
367 }
368 
com_wl_create_tile(WLWindow * window,uint64_t surface_id,int x,int y)369 void com_wl_create_tile(WLWindow* window, uint64_t surface_id, int x, int y) {
370   WLDisplay* display = window->display;
371 
372   assert(window->surfaces.count(surface_id) == 1);
373   Surface* surface = &window->surfaces.at(surface_id);
374 
375   TileKey key(x, y);
376   assert(surface->tiles.count(key) == 0);
377 
378   Tile* tile = new Tile;
379   tile->surface_id = surface_id;
380   tile->x = x;
381   tile->y = y;
382   tile->is_visible = false;
383 
384   tile->surface = wl_compositor_create_surface(display->compositor);
385   tile->viewport =
386       wp_viewporter_get_viewport(display->viewporter, tile->surface);
387 
388   if (surface->is_opaque) {
389     struct wl_region* region =
390         wl_compositor_create_region(window->display->compositor);
391     wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX);
392     wl_surface_set_opaque_region(tile->surface, region);
393     wl_region_destroy(region);
394   }
395 
396   tile->egl_window = wl_egl_window_create(tile->surface, surface->tile_width,
397                                           surface->tile_height);
398   tile->egl_surface = eglCreateWindowSurface(window->eglDisplay, window->config,
399                                              tile->egl_window, NULL);
400   assert(tile->egl_surface != EGL_NO_SURFACE);
401 
402   surface->tiles.emplace(key, tile);
403 }
404 
show_tile(WLWindow * window,Tile * tile)405 static void show_tile(WLWindow* window, Tile* tile) {
406   if (tile->is_visible) {
407     assert(tile->subsurface);
408     return;
409   }
410 
411   tile->subsurface = wl_subcompositor_get_subsurface(
412       window->display->subcompositor, tile->surface, window->surface);
413 
414   /* This is not comprehensive yet, see hide_tile() */
415   Surface* surface = &window->surfaces.at(tile->surface_id);
416   for (auto tile_it = surface->tiles.begin(); tile_it != surface->tiles.end();
417        ++tile_it) {
418     Tile* other_tile = tile_it->second;
419 
420     if (other_tile->is_visible) {
421       wl_subsurface_place_above(tile->subsurface, other_tile->surface);
422     }
423   }
424 
425   tile->is_visible = true;
426 }
427 
hide_tile(WLWindow * window,Tile * tile)428 static void hide_tile(WLWindow* window, Tile* tile) {
429   if (!tile->is_visible) {
430     return;
431   }
432 
433   /*
434    * This is a workaround for missing API on the egl-wayland platform. We
435    * likely want to replace it a solution that detaches the buffer from
436    * the surface, which would require us to manage buffers manually.
437    */
438   wl_subsurface_set_position(tile->subsurface, window->geometry.width / 2,
439                              window->geometry.height / 2);
440   wp_viewport_set_source(tile->viewport, wl_fixed_from_int(0),
441                          wl_fixed_from_int(0), wl_fixed_from_int(1),
442                          wl_fixed_from_int(1));
443   wl_subsurface_place_below(tile->subsurface, window->surface);
444   tile->is_visible = false;
445   window->hiddenTiles.push_back(tile);
446 }
447 
com_wl_destroy_tile(WLWindow * window,uint64_t surface_id,int x,int y)448 void com_wl_destroy_tile(WLWindow* window, uint64_t surface_id, int x, int y) {
449   assert(window->surfaces.count(surface_id) == 1);
450 
451   Surface* surface = &window->surfaces.at(surface_id);
452   TileKey key(x, y);
453   assert(surface->tiles.count(key) == 1);
454   Tile* tile = surface->tiles[key];
455 
456   hide_tile(window, tile);
457   wl_surface_commit(tile->surface);
458 
459   window->destroyedTiles.push_back(tile);
460   surface->tiles.erase(key);
461 }
462 
com_wl_destroy_surface(WLWindow * window,uint64_t surface_id)463 void com_wl_destroy_surface(WLWindow* window, uint64_t surface_id) {
464   assert(window->surfaces.count(surface_id) == 1);
465 
466   Surface* surface = &window->surfaces.at(surface_id);
467   for (auto tile_it = surface->tiles.begin(); tile_it != surface->tiles.end();
468        tile_it = surface->tiles.begin()) {
469     Tile* tile = tile_it->second;
470 
471     com_wl_destroy_tile(window, surface_id, tile->x, tile->y);
472   }
473 
474   window->surfaces.erase(surface_id);
475 }
476 
com_wl_destroy_window(WLWindow * window)477 void com_wl_destroy_window(WLWindow* window) {
478   for (auto surface_it = window->surfaces.begin();
479        surface_it != window->surfaces.end(); ++surface_it) {
480     Surface& surface = surface_it->second;
481 
482     com_wl_destroy_surface(window, surface.id);
483   }
484 
485   if (window->egl_surface != EGL_NO_SURFACE) {
486     eglDestroySurface(window->eglDisplay, window->egl_surface);
487   }
488   eglDestroyContext(window->eglDisplay, window->eglContext);
489   eglTerminate(window->eglDisplay);
490 
491   delete window;
492 }
493 
494 // Bind a native surface to allow issuing GL commands to it
com_wl_bind_surface(WLWindow * window,uint64_t surface_id,int tile_x,int tile_y,int * x_offset,int * y_offset,int dirty_x0,int dirty_y0,int dirty_width,int dirty_height)495 GLuint com_wl_bind_surface(WLWindow* window, uint64_t surface_id, int tile_x,
496                            int tile_y, int* x_offset, int* y_offset,
497                            int dirty_x0, int dirty_y0, int dirty_width,
498                            int dirty_height) {
499   *x_offset = 0;
500   *y_offset = 0;
501 
502   assert(window->surfaces.count(surface_id) == 1);
503   Surface* surface = &window->surfaces[surface_id];
504 
505   TileKey key(tile_x, tile_y);
506   assert(surface->tiles.count(key) == 1);
507   Tile* tile = surface->tiles[key];
508 
509   tile->damage_rects.push_back(dirty_x0);
510   tile->damage_rects.push_back(dirty_y0);
511   tile->damage_rects.push_back(dirty_width);
512   tile->damage_rects.push_back(dirty_height);
513 
514   EGLBoolean ok = eglMakeCurrent(window->eglDisplay, tile->egl_surface,
515                                  tile->egl_surface, window->eglContext);
516   assert(ok);
517 
518   return 0;
519 }
520 
521 // Unbind a currently bound native surface
com_wl_unbind_surface(WLWindow * window)522 void com_wl_unbind_surface(WLWindow* window) {
523   eglMakeCurrent(window->eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
524                  window->eglContext);
525 }
526 
com_wl_begin_transaction(WLWindow *)527 void com_wl_begin_transaction(WLWindow*) {}
528 
529 // Add a native surface to the visual tree. Called per-frame to build the
530 // composition.
com_wl_add_surface(WLWindow * window,uint64_t surface_id,int offset_x,int offset_y,int clip_x,int clip_y,int clip_w,int clip_h)531 void com_wl_add_surface(WLWindow* window, uint64_t surface_id, int offset_x,
532                         int offset_y, int clip_x, int clip_y, int clip_w,
533                         int clip_h) {
534   Surface* surface = &window->surfaces[surface_id];
535   window->currentLayers.push_back(surface_id);
536 
537   for (auto tile_it = surface->tiles.begin(); tile_it != surface->tiles.end();
538        ++tile_it) {
539     Tile* tile = tile_it->second;
540 
541     int pos_x = MAX((tile->x * surface->tile_width) + offset_x, clip_x);
542     int pos_y = MAX((tile->y * surface->tile_height) + offset_y, clip_y);
543 
544     float view_x = MAX((clip_x - offset_x) - tile->x * surface->tile_width, 0);
545     float view_y = MAX((clip_y - offset_y) - tile->y * surface->tile_height, 0);
546 
547     float view_w = MIN(surface->tile_width - view_x, (clip_x + clip_w) - pos_x);
548     float view_h =
549         MIN(surface->tile_height - view_y, (clip_y + clip_h) - pos_y);
550     view_w = MIN(window->geometry.width - pos_x, view_w);
551     view_h = MIN(window->geometry.height - pos_y, view_h);
552 
553     if (view_w > 0 && view_h > 0) {
554       show_tile(window, tile);
555 
556       wl_surface_set_buffer_transform(tile->surface,
557                                       WL_OUTPUT_TRANSFORM_FLIPPED_180);
558       wl_subsurface_set_position(tile->subsurface, pos_x, pos_y);
559       wp_viewport_set_source(tile->viewport, wl_fixed_from_double(view_x),
560                              wl_fixed_from_double(view_y),
561                              wl_fixed_from_double(view_w),
562                              wl_fixed_from_double(view_h));
563     } else {
564       hide_tile(window, tile);
565     }
566   }
567 }
568 
com_wl_end_transaction(WLWindow * window)569 void com_wl_end_transaction(WLWindow* window) {
570   bool same = window->prevLayers == window->currentLayers;
571   if (!same) {
572     struct wl_surface* prev_surface = window->surface;
573 
574     for (auto it = window->currentLayers.begin();
575          it != window->currentLayers.end(); ++it) {
576       Surface* surface = &window->surfaces[*it];
577 
578       struct wl_surface* next_surface = nullptr;
579       for (auto tile_it = surface->tiles.begin();
580            tile_it != surface->tiles.end(); ++tile_it) {
581         Tile* tile = tile_it->second;
582 
583         if (tile->is_visible) {
584           wl_subsurface_place_above(tile->subsurface, prev_surface);
585 
586           if (!next_surface) {
587             next_surface = tile->surface;
588           }
589         }
590       }
591       prev_surface = next_surface;
592     }
593   }
594 
595   window->prevLayers.swap(window->currentLayers);
596   window->currentLayers.clear();
597 }
598 
glInvalidateFramebuffer(GLenum target,GLsizei numAttachments,const GLenum * attachments)599 void glInvalidateFramebuffer(GLenum target, GLsizei numAttachments,
600                              const GLenum* attachments) {
601   UNUSED(target);
602   UNUSED(numAttachments);
603   UNUSED(attachments);
604 }
605 
606 // Get a pointer to an EGL symbol
com_wl_get_proc_address(const char * name)607 void* com_wl_get_proc_address(const char* name) {
608   /* Disable glInvalidateFramebuffer for now as it triggers errors.
609    * This is likely due to the egl-wayland platform, which we may want to
610    * replace with a custom implementation in order to have more control
611    * over the low-lever bits.
612    */
613   if (strcmp(name, "glInvalidateFramebuffer") == 0) {
614     return (void*)glInvalidateFramebuffer;
615   }
616 
617   return (void*)eglGetProcAddress(name);
618 }
619 
com_wl_deinit(WLWindow * window)620 void com_wl_deinit(WLWindow* window) { UNUSED(window); }
621 
handle_xdg_surface_configure(void * data,struct xdg_surface * surface,uint32_t serial)622 static void handle_xdg_surface_configure(void* data,
623                                          struct xdg_surface* surface,
624                                          uint32_t serial) {
625   WLWindow* window = (WLWindow*)data;
626 
627   xdg_surface_ack_configure(surface, serial);
628 
629   if (window->wait_for_configure) {
630     if (window->enable_compositor) {
631       int width = window->geometry.width;
632       int height = window->geometry.height;
633 
634       window->egl_window = wl_egl_window_create(window->surface, 1, 1);
635       window->egl_surface = eglCreateWindowSurface(
636           window->eglDisplay, window->config, window->egl_window, NULL);
637       assert(window->egl_surface != EGL_NO_SURFACE);
638 
639       EGLBoolean ok = eglMakeCurrent(window->eglDisplay, window->egl_surface,
640                                      window->egl_surface, window->eglContext);
641       assert(ok);
642 
643       glClearColor(1.0, 1.0, 1.0, 1.0);
644       glClear(GL_COLOR_BUFFER_BIT);
645 
646       window->viewport = wp_viewporter_get_viewport(window->display->viewporter,
647                                                     window->surface);
648       wp_viewport_set_destination(window->viewport, width, height);
649 
650       eglSwapBuffers(window->eglDisplay, window->egl_surface);
651     } else {
652       window->egl_window = wl_egl_window_create(
653           window->surface, window->geometry.width, window->geometry.height);
654       window->egl_surface = eglCreateWindowSurface(
655           window->eglDisplay, window->config, window->egl_window, NULL);
656       assert(window->egl_surface != EGL_NO_SURFACE);
657 
658       EGLBoolean ok = eglMakeCurrent(window->eglDisplay, window->egl_surface,
659                                      window->egl_surface, window->eglContext);
660       assert(ok);
661     }
662   }
663 
664   window->wait_for_configure = false;
665 }
666 
667 static const struct xdg_surface_listener xdg_surface_listener = {
668     handle_xdg_surface_configure};
669 
handle_xdg_toplevel_configure(void * data,struct xdg_toplevel * toplevel,int32_t width,int32_t height,struct wl_array * states)670 static void handle_xdg_toplevel_configure(void* data,
671                                           struct xdg_toplevel* toplevel,
672                                           int32_t width, int32_t height,
673                                           struct wl_array* states) {
674   WLWindow* window = (WLWindow*)data;
675   UNUSED(toplevel);
676   UNUSED(states);
677 
678   if (width > 0 && height > 0) {
679     window->geometry.width = width;
680     window->geometry.height = height;
681 
682     if (!window->wait_for_configure) {
683       if (window->enable_compositor) {
684         wp_viewport_set_destination(window->viewport, window->geometry.width,
685                                     window->geometry.height);
686       } else {
687         wl_egl_window_resize(window->egl_window, window->geometry.width,
688                              window->geometry.height, 0, 0);
689       }
690     }
691   }
692 }
693 
handle_xdg_toplevel_close(void * data,struct xdg_toplevel * toplevel)694 static void handle_xdg_toplevel_close(void* data,
695                                       struct xdg_toplevel* toplevel) {
696   UNUSED(toplevel);
697   WLWindow* window = (WLWindow*)data;
698   window->closed = true;
699 }
700 
701 static const struct xdg_toplevel_listener xdg_toplevel_listener = {
702     handle_xdg_toplevel_configure,
703     handle_xdg_toplevel_close,
704 };
705 
xdg_wm_base_ping(void * data,struct xdg_wm_base * shell,uint32_t serial)706 static void xdg_wm_base_ping(void* data, struct xdg_wm_base* shell,
707                              uint32_t serial) {
708   UNUSED(data);
709   xdg_wm_base_pong(shell, serial);
710 }
711 
712 static const struct xdg_wm_base_listener wm_base_listener = {
713     xdg_wm_base_ping,
714 };
715 
registry_handle_global(void * data,struct wl_registry * registry,uint32_t name,const char * interface,uint32_t version)716 static void registry_handle_global(void* data, struct wl_registry* registry,
717                                    uint32_t name, const char* interface,
718                                    uint32_t version) {
719   WLDisplay* d = (WLDisplay*)data;
720 
721   if (strcmp(interface, "wl_compositor") == 0) {
722     d->compositor = (struct wl_compositor*)wl_registry_bind(
723         registry, name, &wl_compositor_interface, MIN(version, 4));
724   } else if (strcmp(interface, "wp_viewporter") == 0) {
725     d->viewporter = (struct wp_viewporter*)wl_registry_bind(
726         registry, name, &wp_viewporter_interface, 1);
727   } else if (strcmp(interface, "xdg_wm_base") == 0) {
728     d->wm_base = (struct xdg_wm_base*)wl_registry_bind(
729         registry, name, &xdg_wm_base_interface, 1);
730     xdg_wm_base_add_listener(d->wm_base, &wm_base_listener, NULL);
731   } else if (strcmp(interface, "wl_subcompositor") == 0) {
732     d->subcompositor = (struct wl_subcompositor*)wl_registry_bind(
733         registry, name, &wl_subcompositor_interface, 1);
734   }
735 }
736 
registry_handle_global_remove(void * data,struct wl_registry * registry,uint32_t name)737 static void registry_handle_global_remove(void* data,
738                                           struct wl_registry* registry,
739                                           uint32_t name) {
740   UNUSED(data);
741   UNUSED(registry);
742   UNUSED(name);
743 }
744 
745 static const struct wl_registry_listener registry_listener = {
746     registry_handle_global, registry_handle_global_remove};
747 
init_wl_registry(WLWindow * window)748 static void init_wl_registry(WLWindow* window) {
749   WLDisplay* display = window->display;
750 
751   display->registry = wl_display_get_registry(display->display);
752   wl_registry_add_listener(display->registry, &registry_listener, display);
753 
754   wl_display_roundtrip(display->display);
755 
756   assert(display->compositor);
757   assert(display->wm_base);
758   assert(display->subcompositor);
759 }
760 
init_xdg_window(WLWindow * window)761 static void init_xdg_window(WLWindow* window) {
762   window->xdg_surface =
763       xdg_wm_base_get_xdg_surface(window->display->wm_base, window->surface);
764   assert(window->xdg_surface);
765   xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener, window);
766 
767   window->xdg_toplevel = xdg_surface_get_toplevel(window->xdg_surface);
768   xdg_toplevel_add_listener(window->xdg_toplevel, &xdg_toplevel_listener,
769                             window);
770   assert(window->xdg_toplevel);
771 }
772 }
773