tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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, &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 
    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 }