lib.cpp (24361B)
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 68 TileKey(int ax, int ay) : x(ax), y(ay) {} 69 }; 70 71 bool operator==(const TileKey& k0, const TileKey& k1) { 72 return k0.x == k1.x && k0.y == k1.y; 73 } 74 75 struct TileKeyHasher { 76 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 522 void com_wl_unbind_surface(WLWindow* window) { 523 eglMakeCurrent(window->eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, 524 window->eglContext); 525 } 526 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. 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 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 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 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 620 void com_wl_deinit(WLWindow* window) { UNUSED(window); } 621 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 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 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 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 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 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 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 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 }