diff options
| author | nasr <nsrddyn@gmail.com> | 2026-10-09 12:37:56 +0200 |
|---|---|---|
| committer | nasr <nsrddyn@gmail.com> | 2026-10-09 12:37:56 +0200 |
| commit | 9a2fe3e091849518f3d2f79114ba67fa4fc63adf (patch) | |
| tree | 62678a2ff13752d8f8916aca8b6ff70d9bbd6d80 /source/base/base_platform_linux_wayland.cpp | |
| parent | e467661baf3d035fb4726559abee52a0c8897ab0 (diff) | |
feature: window with sqaures etc etcmain
Diffstat (limited to 'source/base/base_platform_linux_wayland.cpp')
| -rw-r--r-- | source/base/base_platform_linux_wayland.cpp | 365 |
1 files changed, 233 insertions, 132 deletions
diff --git a/source/base/base_platform_linux_wayland.cpp b/source/base/base_platform_linux_wayland.cpp index 772080b..5823a69 100644 --- a/source/base/base_platform_linux_wayland.cpp +++ b/source/base/base_platform_linux_wayland.cpp | |||
| @@ -1,193 +1,294 @@ | |||
| 1 | // https://gaultier.github.io/blog/wayland_from_scratch.html#creating-a-registry | ||
| 2 | // TODO: replace unix_get_env with a generic macro | ||
| 3 | 1 | ||
| 2 | //- software framebuffer | ||
| 3 | // TODO: delete this section once vulkan is presenting. vkCreateWaylandSurfaceKHR only needs | ||
| 4 | // state->display and state->surface, the driver then creates and attaches buffers itself. | ||
| 4 | 5 | ||
| 6 | // creates a wl_buffer backed by shared memory that both we and the compositor can read. | ||
| 7 | internal struct wl_buffer *wayland_create_shm_buffer(WaylandState *state) { | ||
| 8 | state->stride = state->width * 4; // 4 bytes per pixel (XRGB8888) | ||
| 9 | state->size = state->stride * state->height; | ||
| 5 | 10 | ||
| 6 | //- wayland | 11 | int fd = memfd_create("loo-shared-memory", MFD_CLOEXEC); |
| 7 | internal int wayland_display_connect(MemArena *arena) { | 12 | AssertLog((fd != -1), "Failed to open buffer for shared memory."); |
| 13 | AssertLog((ftruncate(fd, state->size) != -1), "Failed to truncate buffer."); | ||
| 8 | 14 | ||
| 9 | String8 str8_xdg_runtime_dir = unix_get_env("XDG_RUNTIME_DIR"); | 15 | state->pixels = (u8 *)mmap(NULL, state->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); |
| 10 | char *xdg_runtime_dir = null_terminate(arena, &str8_xdg_runtime_dir); | 16 | AssertLog((state->pixels != MAP_FAILED), "Failed to mmap shared memory."); |
| 11 | sockaddr_un addr = {}; | ||
| 12 | u64 socket_path_len = 0; | ||
| 13 | 17 | ||
| 14 | // copy the xdg_runtime_dir value to the beginning of the bfufer | 18 | // a pool is the compositor-side view of our shared memory. libwayland sends the fd |
| 15 | { | 19 | // over the socket for us (the SCM_RIGHTS dance, in the other direction). |
| 16 | addr.sun_family = AF_UNIX; | 20 | struct wl_shm_pool *pool = wl_shm_create_pool(state->shm, fd, state->size); |
| 17 | MemCpy(addr.sun_path, xdg_runtime_dir, str8_xdg_runtime_dir.size); | 21 | |
| 18 | socket_path_len += str8_xdg_runtime_dir.size; | 22 | // a buffer is a typed slice of the pool: offset, size, stride and pixel format |
| 19 | addr.sun_path[socket_path_len++] = '/'; // add an ending slash | 23 | struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, state->width, state->height, |
| 20 | } | 24 | state->stride, WL_SHM_FORMAT_XRGB8888); |
| 21 | 25 | ||
| 22 | String8 str8_wayland_display_value = unix_get_env("WAYLAND_DISPLAY"); | 26 | wl_shm_pool_destroy(pool); |
| 23 | Assert(str8_wayland_display_value.size != 0); | 27 | close(fd); |
| 24 | char *wayland_display = null_terminate(arena, &str8_wayland_display_value); | 28 | |
| 29 | return buffer; | ||
| 30 | } | ||
| 25 | 31 | ||
| 26 | if(wayland_display == NULL) | 32 | internal void wayland_draw_gradient(WaylandState *state) { |
| 27 | { | 33 | u32 *pixels = (u32 *)state->pixels; |
| 28 | const char *wayland_display_default = "wayland-0"; | 34 | for (u32 y = 0; y < state->height; y++) { |
| 29 | u64 wayland_display_default_len = cstring8_length((u8 *)wayland_display_default); | 35 | for (u32 x = 0; x < state->width; x++) { |
| 30 | MemCpy(addr.sun_path, wayland_display_default, wayland_display_default_len); | 36 | u8 r = (u8)(x * 255 / state->width); |
| 37 | u8 g = (u8)(y * 255 / state->height); | ||
| 38 | u8 b = 0x80; | ||
| 39 | pixels[y * state->width + x] = (r << 16) | (g << 8) | b; | ||
| 40 | } | ||
| 31 | } | 41 | } |
| 32 | else | ||
| 33 | { | ||
| 34 | MemCpy(addr.sun_path + str8_xdg_runtime_dir.size + 1, wayland_display, str8_wayland_display_value.size); | ||
| 35 | } | 42 | } |
| 36 | 43 | ||
| 37 | int fd = socket(AF_UNIX, SOCK_STREAM, 0); | 44 | internal void draw_square(u32 *pixels, WaylandState *state, u32 x_position, u32 y_position) { |
| 38 | 45 | ||
| 39 | #if DEBUG // helps to read those errors? i guess i can just read stderr? | 46 | for(u32 y = y_position; y < y_position + 10; ++y) { |
| 40 | 47 | ||
| 48 | for(u32 x = x_position; x < x_position + 10; ++x) { | ||
| 41 | 49 | ||
| 50 | u8 r = 0xff; | ||
| 51 | u8 g = 0xff; | ||
| 52 | u8 b = 0xff; | ||
| 53 | pixels[y * state->width + x] = (r << 16) | (g << 8) | b; | ||
| 42 | 54 | ||
| 43 | if(fd == -1) | 55 | } |
| 44 | { | ||
| 45 | perror("failed to open socket"); | ||
| 46 | } | 56 | } |
| 47 | 57 | ||
| 58 | } | ||
| 48 | 59 | ||
| 49 | if(connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) | 60 | internal void wayland_draw_weird_gradient(WaylandState *state) { |
| 50 | { | ||
| 51 | perror("failed to connect to socket"); | ||
| 52 | 61 | ||
| 53 | } | 62 | local_persist u32 frame = 0; |
| 63 | frame += 1; | ||
| 54 | 64 | ||
| 55 | #endif | 65 | local_persist u32 start_x = state->width / 2; |
| 66 | local_persist u32 start_y = state->width / 2; | ||
| 56 | 67 | ||
| 68 | start_x += frame; | ||
| 57 | 69 | ||
| 58 | AssertLog((fd != -1), "failed to open socket\n"); | 70 | draw_square((u32 *)state->pixels, state, start_x , start_y); |
| 59 | AssertLog((connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1), "failed to connect to socket\n"); | ||
| 60 | 71 | ||
| 61 | return fd; | ||
| 62 | 72 | ||
| 63 | } | 73 | } |
| 64 | 74 | ||
| 65 | 75 | ||
| 66 | //- helper functions | 76 | //- xdg_wm_base events |
| 67 | |||
| 68 | internal void buf_write_u32(char *buf, u64 *buf_size, u64 buf_cap, | ||
| 69 | u32 x) { | ||
| 70 | Assert(*buf_size + sizeof(x) <= buf_cap); | ||
| 71 | Assert(((size_t)buf + *buf_size) % sizeof(x) == 0); | ||
| 72 | 77 | ||
| 73 | *(u32 *)(buf + *buf_size) = x; | 78 | // ping: the compositor checks we're still alive. if we don't pong, it may show the |
| 74 | *buf_size += sizeof(x); | 79 | internal void wayland_xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, u32 serial) { |
| 80 | xdg_wm_base_pong(xdg_wm_base, serial); | ||
| 75 | } | 81 | } |
| 76 | 82 | ||
| 77 | internal void buf_write_u16(char *buf, u64 *buf_size, u64 buf_cap, | 83 | global_variable const struct xdg_wm_base_listener wayland_xdg_wm_base_listener = { |
| 78 | u16 x) { | 84 | wayland_xdg_wm_base_ping, |
| 79 | Assert(*buf_size + sizeof(x) <= buf_cap); | 85 | }; |
| 80 | Assert(((size_t)buf + *buf_size) % sizeof(x) == 0); | 86 | |
| 87 | |||
| 88 | //- frame | ||
| 89 | |||
| 90 | |||
| 91 | internal void wayland_frame_done(void *data, struct wl_callback *callback, u32 time_ms); | ||
| 92 | |||
| 93 | global_variable const struct wl_callback_listener wayland_frame_listener = { | ||
| 94 | wayland_frame_done, | ||
| 95 | |||
| 96 | }; | ||
| 81 | 97 | ||
| 82 | *(u16 *)(buf + *buf_size) = x; | ||
| 83 | *buf_size += sizeof(x); | ||
| 84 | } | ||
| 85 | 98 | ||
| 86 | internal void buf_write_string(char *buf, u64 *buf_size, u64 buf_cap, | 99 | internal void wayland_frame_done(void *data, struct wl_callback *callback, u32 time_ms) { |
| 87 | char *src, u32 src_len) { | 100 | |
| 88 | Assert(*buf_size + src_len <= buf_cap); | 101 | WaylandState *state = (WaylandState *)data; |
| 102 | wl_callback_destroy(callback); | ||
| 103 | |||
| 104 | wayland_draw_weird_gradient(state); | ||
| 105 | struct wl_callback *next = wl_surface_frame(state->surface); | ||
| 106 | wl_callback_add_listener(next, &wayland_frame_listener, state); | ||
| 107 | |||
| 108 | wl_surface_attach(state->surface, state->buffer, 0, 0); | ||
| 109 | wl_surface_damage(state->surface, 0, 0, state->width, state->height); | ||
| 110 | wl_surface_commit(state->surface); | ||
| 89 | 111 | ||
| 90 | buf_write_u32(buf, buf_size, buf_cap, src_len); | ||
| 91 | memcpy(buf + *buf_size, src, roundup_4(src_len)); | ||
| 92 | *buf_size += roundup_4(src_len); | ||
| 93 | } | 112 | } |
| 94 | 113 | ||
| 95 | internal u32 buf_read_u32(char **buf, u64 *buf_size) { | ||
| 96 | Assert(*buf_size >= sizeof(u32)); | ||
| 97 | Assert((size_t)*buf % sizeof(u32) == 0); | ||
| 98 | 114 | ||
| 99 | u32 res = *(u32 *)(*buf); | ||
| 100 | *buf += sizeof(res); | ||
| 101 | *buf_size -= sizeof(res); | ||
| 102 | 115 | ||
| 103 | return res; | 116 | //- xdg_surface events |
| 117 | |||
| 118 | internal void wayland_xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, u32 serial) { | ||
| 119 | WaylandState *state = (WaylandState *)data; | ||
| 120 | |||
| 121 | xdg_surface_ack_configure(xdg_surface, serial); | ||
| 122 | |||
| 123 | if (state->buffer == NULL) { | ||
| 124 | state->buffer = wayland_create_shm_buffer(state); | ||
| 125 | wayland_draw_weird_gradient(state); | ||
| 126 | |||
| 127 | struct wl_callback *cb = wl_surface_frame(state->surface); | ||
| 128 | wl_callback_add_listener(cb, &wayland_frame_listener, state); | ||
| 129 | wl_surface_attach(state->surface, state->buffer, 0, 0); | ||
| 130 | wl_surface_damage(state->surface, 0, 0, state->width, state->height); | ||
| 131 | wl_surface_commit(state->surface); | ||
| 132 | } | ||
| 133 | |||
| 134 | // attach = "this buffer is the surface's content", damage = "this region changed", | ||
| 135 | // commit = "apply everything I sent since the last commit, atomically". | ||
| 136 | // a wl_surface has pending state and current state, nothing is visible until commit. | ||
| 137 | wl_surface_attach(state->surface, state->buffer, 0, 0); | ||
| 138 | wl_surface_damage(state->surface, 0, 0, state->width, state->height); | ||
| 139 | wl_surface_commit(state->surface); | ||
| 104 | } | 140 | } |
| 105 | 141 | ||
| 106 | internal u16 buf_read_u16(char **buf, u64 *buf_size) { | 142 | global_variable const struct xdg_surface_listener wayland_xdg_surface_listener = { |
| 107 | Assert(*buf_size >= sizeof(u16)); | 143 | wayland_xdg_surface_configure, |
| 108 | Assert((size_t)*buf % sizeof(u16) == 0); | 144 | }; |
| 145 | |||
| 146 | |||
| 147 | //- xdg_toplevel events | ||
| 109 | 148 | ||
| 110 | u16 res = *(u16 *)(*buf); | 149 | // TODO: handle resizing. with vulkan that means recreating the swapchain. |
| 111 | *buf += sizeof(res); | 150 | internal void wayland_xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, |
| 112 | *buf_size -= sizeof(res); | 151 | s32 width, s32 height, struct wl_array *states) { |
| 152 | |||
| 153 | WaylandState *state = (WaylandState *)data; | ||
| 154 | Log("<- xdg_toplevel.configure: width=%d height=%d\n", width, height); | ||
| 155 | state->height = height; | ||
| 156 | state->width = width; | ||
| 157 | |||
| 158 | } | ||
| 113 | 159 | ||
| 114 | return res; | 160 | internal void wayland_xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel) { |
| 161 | WaylandState *state = (WaylandState *)data; | ||
| 162 | Log("<- xdg_toplevel.close\n"); | ||
| 163 | state->should_close = true; | ||
| 115 | } | 164 | } |
| 116 | 165 | ||
| 117 | internal void buf_read_n(char **buf, u64 *buf_size, char *dst, u64 n) { | 166 | internal void wayland_xdg_toplevel_resize_edge(void *data, struct xdg_toplevel *xdg_toplevel, |
| 118 | Assert(*buf_size >= n); | 167 | s32 width, s32 height, struct wl_array *states) { |
| 119 | 168 | ||
| 120 | memcpy(dst, *buf, n); | ||
| 121 | 169 | ||
| 122 | *buf += n; | 170 | } |
| 123 | *buf_size -= n; | 171 | internal void wayland_xdg_toplevel_configure_bounds(void *data, struct xdg_toplevel *xdg_toplevel, |
| 172 | s32 width, s32 height) { | ||
| 124 | } | 173 | } |
| 125 | 174 | ||
| 126 | global_variable u64 wayland_current_id = 1; | 175 | internal void wayland_xdg_toplevel_wm_capabilities(void *data, struct xdg_toplevel *xdg_toplevel, |
| 176 | struct wl_array *capabilities) { | ||
| 177 | } | ||
| 127 | 178 | ||
| 128 | internal u32 wayland_wl_display_get_registry(int fd) { | 179 | global_variable const struct xdg_toplevel_listener wayland_xdg_toplevel_listener = { |
| 129 | uint64_t msg_size = 0; | 180 | wayland_xdg_toplevel_configure, |
| 130 | char msg[128] = ""; | 181 | wayland_xdg_toplevel_close, |
| 131 | buf_write_u32(msg, &msg_size, sizeof(msg), wayland_display_object_id); | 182 | wayland_xdg_toplevel_configure_bounds, |
| 183 | wayland_xdg_toplevel_wm_capabilities, | ||
| 184 | }; | ||
| 132 | 185 | ||
| 133 | buf_write_u16(msg, &msg_size, sizeof(msg), | ||
| 134 | wayland_wl_display_get_registry_opcode); | ||
| 135 | 186 | ||
| 136 | u16 msg_announced_size = wayland_header_size + sizeof(wayland_current_id); | 187 | //- wl_registry events |
| 137 | Assert(roundup_4(msg_announced_size) == msg_announced_size); | ||
| 138 | buf_write_u16(msg, &msg_size, sizeof(msg), msg_announced_size); | ||
| 139 | 188 | ||
| 140 | wayland_current_id++; | 189 | internal void wayland_registry_global(void *data, struct wl_registry *registry, u32 name, |
| 141 | buf_write_u32(msg, &msg_size, sizeof(msg), wayland_current_id); | 190 | const char *interface, u32 version) { |
| 191 | WaylandState *state = (WaylandState *)data; | ||
| 142 | 192 | ||
| 143 | if ((s64)msg_size != send(fd, msg, msg_size, MSG_DONTWAIT)) | 193 | Log("<- wl_registry.global: name=%u interface=%s version=%u\n", name, interface, version); |
| 144 | exit(1); | ||
| 145 | 194 | ||
| 146 | Log("-> wl_display@%u.get_registry: wl_registry=%u\n", | 195 | if (CstringIsMatch(interface, wl_compositor_interface.name)) { |
| 147 | wayland_display_object_id, wayland_current_id); | 196 | state->compositor = (struct wl_compositor *) |
| 197 | wl_registry_bind(registry, name, &wl_compositor_interface, Min(version, 4)); | ||
| 198 | } else if (CstringIsMatch(interface, wl_shm_interface.name)) { | ||
| 199 | state->shm = (struct wl_shm *) | ||
| 200 | wl_registry_bind(registry, name, &wl_shm_interface, 1); | ||
| 201 | } else if(CstringIsMatch(interface, xdg_wm_base_interface.name)) { | ||
| 202 | state->xdg_wm_base = (struct xdg_wm_base *) | ||
| 203 | wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); | ||
| 204 | xdg_wm_base_add_listener(state->xdg_wm_base, &wayland_xdg_wm_base_listener, state); | ||
| 205 | } else if (CstringIsMatch(interface, wl_seat_interface.name)) { | ||
| 206 | state->seat = (struct wl_seat *) | ||
| 207 | wl_registry_bind(registry, name, &wl_seat_interface, Min(version, 5)); | ||
| 208 | wl_seat_add_listener(state->seat, &wayland_seat_listener, state); // has the keyboard handler | ||
| 209 | } | ||
| 210 | } | ||
| 148 | 211 | ||
| 149 | return wayland_current_id; | 212 | internal void wayland_registry_global_remove(void *data, struct wl_registry *registry, u32 name) { |
| 150 | } | 213 | } |
| 151 | 214 | ||
| 215 | global_variable const struct wl_registry_listener wayland_registry_listener = { | ||
| 216 | wayland_registry_global, | ||
| 217 | wayland_registry_global_remove, | ||
| 218 | }; | ||
| 152 | 219 | ||
| 220 | //- entrypoint | ||
| 153 | 221 | ||
| 154 | internal void create_shared_memory_file(u64 size, state_t *state) { | 222 | internal void wayland_entrypoint(MemArena *arena, LinuxWindowParams *params) { |
| 155 | char name[255] = "/"; | 223 | |
| 156 | for (u64 index = 1; index < cstring8_length((u8 *)name); ++index) { | 224 | WaylandState state = {}; |
| 157 | name[index] = ((double)rand()) / (double)RAND_MAX * 26 + 'a'; | 225 | |
| 158 | } | 226 | state.display = wl_display_connect(NULL); |
| 159 | 227 | AssertLog((state.display != NULL), "failed to connect to wayland display\n"); | |
| 160 | int fd = shm_open(name, O_RDWR | O_EXCL | O_CREAT, 0600); | 228 | |
| 161 | AssertLog((fd != -1), "Failed to open buffer for shared memory."); | 229 | // ask for the registry and listen to its global events |
| 162 | 230 | state.registry = wl_display_get_registry(state.display); | |
| 163 | Assert(shm_unlink(name) != -1); | 231 | wl_registry_add_listener(state.registry, &wayland_registry_listener, &state); |
| 164 | 232 | ||
| 165 | AssertLog((ftruncate(fd, size) != -1), "Failed to truncate buffer."); | 233 | wl_display_roundtrip(state.display); |
| 166 | 234 | ||
| 167 | state->shm_pool_data = (u8 *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); | 235 | AssertLog((state.compositor != NULL), "compositor has no wl_compositor\n"); |
| 168 | Assert((void*)-1 != state->shm_pool_data); | 236 | AssertLog((state.shm != NULL), "compositor has no wl_shm\n"); |
| 169 | Assert(state->shm_pool_data != NULL); | 237 | AssertLog((state.xdg_wm_base != NULL), "compositor has no xdg_wm_base\n"); |
| 170 | state->shm_fd = fd; | 238 | if (state.seat == NULL) { |
| 239 | Log("no wl_seat: running without keyboard input\n"); | ||
| 240 | } | ||
| 241 | |||
| 242 | // build the window: surface -> give it the xdg role -> make it a toplevel | ||
| 243 | state.surface = wl_compositor_create_surface(state.compositor); | ||
| 244 | |||
| 245 | state.xdg_surface = xdg_wm_base_get_xdg_surface(state.xdg_wm_base, state.surface); | ||
| 246 | xdg_surface_add_listener(state.xdg_surface, &wayland_xdg_surface_listener, &state); | ||
| 247 | |||
| 248 | state.xdg_toplevel = xdg_surface_get_toplevel(state.xdg_surface); | ||
| 249 | xdg_toplevel_add_listener(state.xdg_toplevel, &wayland_xdg_toplevel_listener, &state); | ||
| 250 | xdg_toplevel_set_title(state.xdg_toplevel, params->name); | ||
| 251 | xdg_toplevel_set_app_id(state.xdg_toplevel, params->name); | ||
| 252 | |||
| 253 | wl_surface_commit(state.surface); | ||
| 254 | |||
| 255 | // | ||
| 256 | // TODO: blocking is fine while the picture is static. for a game loop that renders | ||
| 257 | // every frame we'll instead use wl_display_dispatch_pending + a non blocking read | ||
| 258 | // (wl_display_prepare_read / wl_display_read_events with poll on wl_display_get_fd). | ||
| 259 | while (!state.should_close) { | ||
| 260 | |||
| 261 | if (wl_display_dispatch(state.display) == -1) { | ||
| 262 | Log("wayland connection lost\n"); | ||
| 263 | goto regular_exit; | ||
| 264 | } | ||
| 265 | |||
| 266 | if(state.keys_down[KEY_W]) { Log("Move Forward"); } | ||
| 267 | if(state.keys_down[KEY_A]) { Log("Move Left"); } | ||
| 268 | if(state.keys_down[KEY_D]) { Log("Move Right"); } | ||
| 269 | if(state.keys_down[KEY_S]) { Log("Move Back"); } | ||
| 270 | if(state.keys_down[KEY_Q]) { | ||
| 271 | goto cleanup_exit; | ||
| 171 | } | 272 | } |
| 172 | 273 | ||
| 173 | internal void wayland_handle_message(int fd, state_t *state, char **msg, | 274 | } |
| 174 | uint64_t *msg_len) { | 275 | |
| 175 | Assert(*msg_len >= 8); | 276 | |
| 176 | 277 | cleanup_exit: | |
| 177 | u32 object_id = buf_read_u32(msg, msg_len); | ||
| 178 | Assert(object_id <= wayland_current_id); | ||
| 179 | |||
| 180 | u16 opcode = buf_read_u16(msg, msg_len); | ||
| 181 | |||
| 182 | u16 announced_size = buf_read_u16(msg, msg_len); | ||
| 183 | Assert(roundup_4(announced_size) <= announced_size); | ||
| 184 | |||
| 185 | u32 header_size = sizeof(object_id) + sizeof(opcode) + sizeof(announced_size); | ||
| 186 | Assert(announced_size <= header_size + *msg_len); | ||
| 187 | 278 | ||
| 188 | if (object_id == state->wl_registry && | 279 | if (state.keyboard) wl_keyboard_destroy(state.keyboard); |
| 189 | opcode == wayland_wl_registry_event_global) { | 280 | if (state.buffer) wl_buffer_destroy(state.buffer); |
| 190 | // TODO | 281 | if (state.pixels) munmap(state.pixels, state.size); |
| 191 | } | 282 | xdg_toplevel_destroy(state.xdg_toplevel); |
| 192 | // Following: Lots of `if (opcode == ...) {... } else if (opcode = ...) { ... } [...]` | 283 | xdg_surface_destroy(state.xdg_surface); |
| 284 | wl_surface_destroy(state.surface); | ||
| 285 | if (state.seat) wl_seat_destroy(state.seat); | ||
| 286 | xdg_wm_base_destroy(state.xdg_wm_base); | ||
| 287 | wl_shm_destroy(state.shm); | ||
| 288 | wl_compositor_destroy(state.compositor); | ||
| 289 | wl_registry_destroy(state.registry); | ||
| 290 | return; | ||
| 291 | |||
| 292 | regular_exit: | ||
| 293 | return; | ||
| 193 | } | 294 | } |
