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