#include "../../../include/mgl/window/wayland.h" #include "../../../include/mgl/mgl.h" #include #include #include #include #include #include #include "xdg-shell-client-protocol.h" static void mgl_window_wayland_deinit(mgl_window *self); typedef struct { struct wl_egl_window *window; struct wl_registry *registry; struct wl_surface *surface; struct wl_compositor *compositor; struct wl_shell *shell; struct wl_shell_surface *shell_surface; struct wl_seat *seat; struct wl_pointer *pointer; struct wl_keyboard *keyboard; struct xdg_wm_base *xdg_wm_base; struct xdg_surface *xdg_surface; struct xdg_toplevel *xdg_toplevel; bool mouse_moved; mgl_vec2i mouse_position; mgl_graphics graphics; } mgl_window_wayland; static void mgl_window_wayland_on_resize(mgl_window *self, int width, int height) { mgl_window_wayland *impl = self->impl; self->size.x = width; self->size.y = height; wl_egl_window_resize(impl->window, self->size.x, self->size.y, 0, 0); mgl_view view; view.position = (mgl_vec2i){ 0, 0 }; view.size = self->size; mgl_window_set_view(self, &view); mgl_window_set_scissor(self, &(mgl_scissor){ .position = { 0, 0 }, .size = self->size }); } static void registry_add_object(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { (void)version; mgl_window *self = data; mgl_window_wayland *impl = self->impl; if(strcmp(interface, wl_compositor_interface.name) == 0) { if(impl->compositor) return; impl->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1); } else if(strcmp(interface, wl_output_interface.name) == 0) { if(version < 4) { fprintf(stderr, "mgl warning: wl output interface version is < 4, expected >= 4\n"); return; } if(self->num_monitors == MGL_MAX_MONITORS) { fprintf(stderr, "mgl warning: reached maximum outputs (%d), ignoring output %u\n", MGL_MAX_MONITORS, name); return; } // gsr_wayland_output *gsr_output = &window_wayland->outputs[window_wayland->num_outputs]; // ++self->num_monitors; // *gsr_output = (gsr_wayland_output) { // .wl_name = name, // .output = wl_registry_bind(registry, name, &wl_output_interface, 4), // .pos = { .x = 0, .y = 0 }, // .size = { .x = 0, .y = 0 }, // .transform = 0, // .name = NULL, // }; // wl_output_add_listener(gsr_output->output, &output_listener, gsr_output); } else if(strcmp(interface, wl_shell_interface.name) == 0) { if(impl->shell) return; impl->shell = wl_registry_bind(registry, name, &wl_shell_interface, 1); } else if(strcmp(interface, xdg_wm_base_interface.name) == 0) { if(impl->xdg_wm_base) return; impl->xdg_wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); } else if(strcmp(interface, wl_seat_interface.name) == 0) { if(version < 5) { fprintf(stderr, "mgl warning: wl seat interface version is < 5, expected >= 5\n"); return; } if(impl->seat) return; impl->seat = wl_registry_bind(registry, name, &wl_seat_interface, 5); } } static void registry_remove_object(void *data, struct wl_registry *registry, uint32_t name) { (void)data; (void)registry; (void)name; // TODO: Remove output } static struct wl_registry_listener registry_listener = { .global = registry_add_object, .global_remove = registry_remove_object, }; static void shell_surface_ping(void *data, struct wl_shell_surface *shell_surface, uint32_t serial) { wl_shell_surface_pong(shell_surface, serial); } static void shell_surface_configure(void *data, struct wl_shell_surface *shell_surface, uint32_t edges, int32_t width, int32_t height) { mgl_window *self = data; mgl_window_wayland *impl = self->impl; wl_egl_window_resize(impl->window, width, height, 0, 0); } void shell_surface_popup_done(void *data, struct wl_shell_surface *shell_surface) { } static struct wl_shell_surface_listener shell_surface_listener = { .ping = shell_surface_ping, .configure = shell_surface_configure, .popup_done = shell_surface_popup_done, }; static void xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial) { xdg_wm_base_pong(shell, serial); } static const struct xdg_wm_base_listener wm_base_listener = { .ping = xdg_wm_base_ping, }; static void xdg_surface_configure(void *data, struct xdg_surface *surface, uint32_t serial) { xdg_surface_ack_configure(surface, serial); // TODO: //window->wait_for_configure = false; } static const struct xdg_surface_listener xdg_surface_listener = { .configure = xdg_surface_configure, }; static void xdg_toplevel_configure(void *data, struct xdg_toplevel *toplevel, int32_t width, int32_t height, struct wl_array *states) { mgl_window *self = data; mgl_window_wayland *impl = self->impl; // TODO: mgl_window_wayland_on_resize(self, width, height); // struct window *window = data; // uint32_t *p; // window->fullscreen = 0; // window->maximized = 0; // wl_array_for_each(p, states) { // uint32_t state = *p; // switch (state) { // case XDG_TOPLEVEL_STATE_FULLSCREEN: // window->fullscreen = 1; // break; // case XDG_TOPLEVEL_STATE_MAXIMIZED: // window->maximized = 1; // break; // } // } // if (width > 0 && height > 0) { // if (!window->fullscreen && !window->maximized) { // window->window_size.width = width; // window->window_size.height = height; // } // window->geometry.width = width; // window->geometry.height = height; // } else if (!window->fullscreen && !window->maximized) { // window->geometry = window->window_size; // } // if (window->native) // wl_egl_window_resize(window->native, // window->geometry.width, // window->geometry.height, 0, 0); } static void xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel) { // TODO: //running = 0; } static const struct xdg_toplevel_listener xdg_toplevel_listener = { .configure = xdg_toplevel_configure, .close = xdg_toplevel_close, }; static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { // // Set our pointer // wl_pointer_set_cursor(wl_pointer, serial, cursor_surface, // cursor_image->hotspot_x, cursor_image->hotspot_y); } static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface) { } static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { mgl_window *self = data; mgl_window_wayland *impl = self->impl; impl->mouse_moved = true; impl->mouse_position = (mgl_vec2i){ .x = wl_fixed_to_int(surface_x), .y = wl_fixed_to_int(surface_y) }; } static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { } static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { } static void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t axis_source) { } static void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) { } static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) { } static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) { mgl_window *self = data; mgl_window_wayland *impl = self->impl; if(impl->mouse_moved) { impl->mouse_moved = false; self->cursor_position = impl->mouse_position; } } static const struct wl_pointer_listener wl_pointer_listener = { .enter = wl_pointer_enter, .leave = wl_pointer_leave, .motion = wl_pointer_motion, .button = wl_pointer_button, .axis = wl_pointer_axis, .frame = wl_pointer_frame, .axis_source = wl_pointer_axis_source, .axis_stop = wl_pointer_axis_stop, .axis_discrete = wl_pointer_axis_discrete, }; static void wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size) { // struct client_state *client_state = data; // assert(format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1); // char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); // assert(map_shm != MAP_FAILED); // struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_string( // client_state->xkb_context, map_shm, // XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); // munmap(map_shm, size); // close(fd); // struct xkb_state *xkb_state = xkb_state_new(xkb_keymap); // xkb_keymap_unref(client_state->xkb_keymap); // xkb_state_unref(client_state->xkb_state); // client_state->xkb_keymap = xkb_keymap; // client_state->xkb_state = xkb_state; } static void wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { // struct client_state *client_state = data; // fprintf(stderr, "keyboard enter; keys pressed are:\n"); // uint32_t *key; // wl_array_for_each(key, keys) { // char buf[128]; // xkb_keysym_t sym = xkb_state_key_get_one_sym( // client_state->xkb_state, *key + 8); // xkb_keysym_get_name(sym, buf, sizeof(buf)); // fprintf(stderr, "sym: %-12s (%d), ", buf, sym); // xkb_state_key_get_utf8(client_state->xkb_state, // *key + 8, buf, sizeof(buf)); // fprintf(stderr, "utf8: '%s'\n", buf); // } } static void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface) { //fprintf(stderr, "keyboard leave\n"); } static void wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { // struct client_state *client_state = data; // xkb_state_update_mask(client_state->xkb_state, // mods_depressed, mods_latched, mods_locked, 0, 0, group); } static void wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay) { /* Left as an exercise for the reader */ } static void wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { // struct client_state *client_state = data; // char buf[128]; // uint32_t keycode = key + 8; // xkb_keysym_t sym = xkb_state_key_get_one_sym( // client_state->xkb_state, keycode); // xkb_keysym_get_name(sym, buf, sizeof(buf)); // const char *action = // state == WL_KEYBOARD_KEY_STATE_PRESSED ? "press" : "release"; // fprintf(stderr, "key %s: sym: %-12s (%d), ", action, buf, sym); // xkb_state_key_get_utf8(client_state->xkb_state, keycode, // buf, sizeof(buf)); // fprintf(stderr, "utf8: '%s'\n", buf); } static const struct wl_keyboard_listener wl_keyboard_listener = { .keymap = wl_keyboard_keymap, .enter = wl_keyboard_enter, .leave = wl_keyboard_leave, .key = wl_keyboard_key, .modifiers = wl_keyboard_modifiers, .repeat_info = wl_keyboard_repeat_info, }; static void wl_seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities) { struct mgl_window *self = data; mgl_window_wayland *impl = self->impl; const bool have_pointer = capabilities & WL_SEAT_CAPABILITY_POINTER; const bool have_keyboard = capabilities & WL_SEAT_CAPABILITY_KEYBOARD; if(have_pointer && impl->pointer == NULL) { impl->pointer = wl_seat_get_pointer(impl->seat); wl_pointer_add_listener(impl->pointer, &wl_pointer_listener, self); } else if(!have_pointer && impl->pointer != NULL) { wl_pointer_release(impl->pointer); impl->pointer = NULL; } if(have_keyboard && impl->keyboard == NULL) { impl->keyboard = wl_seat_get_keyboard(impl->seat); wl_keyboard_add_listener(impl->keyboard, &wl_keyboard_listener, self); } else if(!have_keyboard && impl->keyboard != NULL) { wl_keyboard_release(impl->keyboard); impl->keyboard = NULL; } } static void wl_seat_name(void *data, struct wl_seat *wl_seat, const char *name) { fprintf(stderr, "seat name: %s\n", name); } static const struct wl_seat_listener wl_seat_listener = { .capabilities = wl_seat_capabilities, .name = wl_seat_name, }; // TODO: params and existing_window bool mgl_wayland_setup_window(mgl_window *self, const char *title, const mgl_window_create_params *params, mgl_window_handle existing_window) { mgl_window_wayland *impl = self->impl; mgl_context *context = mgl_get_context(); mgl_vec2i window_size = params ? params->size : (mgl_vec2i){ 0, 0 }; if(window_size.x <= 0 || window_size.y <= 0) { window_size.x = 640; window_size.y = 480; } self->size = window_size; impl->registry = wl_display_get_registry(context->connection); /* TODO: Error checking */ wl_registry_add_listener(impl->registry, ®istry_listener, self); /* TODO: Error checking */ /* Fetch globals */ wl_display_roundtrip(context->connection); /* Fetch wl_output */ //wl_display_roundtrip(context->connection); if(!impl->compositor) { fprintf(stderr, "mgl error: mgl_wayland_setup_window: failed to find compositor\n"); return false; } if(!impl->xdg_wm_base && !impl->shell) { fprintf(stderr, "mgl error: mgl_wayland_setup_window: failed to find shell\n"); return false; } if(!impl->seat) { fprintf(stderr, "mgl error: mgl_wayland_setup_window: failed to find seat\n"); return false; } impl->surface = wl_compositor_create_surface(impl->compositor); if(!impl->surface) { fprintf(stderr, "mgl error: mgl_wayland_setup_window: failed to create surface\n"); return false; } wl_seat_add_listener(impl->seat, &wl_seat_listener, self); if(impl->xdg_wm_base) { // TODO: Error check, cleanup xdg_wm_base_add_listener(impl->xdg_wm_base, &wm_base_listener, self); impl->xdg_surface = xdg_wm_base_get_xdg_surface(impl->xdg_wm_base, impl->surface); xdg_surface_add_listener(impl->xdg_surface, &xdg_surface_listener, self); impl->xdg_toplevel = xdg_surface_get_toplevel(impl->xdg_surface); xdg_toplevel_add_listener(impl->xdg_toplevel, &xdg_toplevel_listener, self); xdg_toplevel_set_title(impl->xdg_toplevel, title); if(params && params->class_name) xdg_toplevel_set_app_id(impl->xdg_toplevel, params->class_name); } else { // TODO: Error check, cleanup impl->shell_surface = wl_shell_get_shell_surface(impl->shell, impl->surface); wl_shell_surface_add_listener(impl->shell_surface, &shell_surface_listener, self); wl_shell_surface_set_toplevel(impl->shell_surface); wl_shell_surface_set_title(impl->shell_surface, title); if(params && params->class_name) wl_shell_surface_set_class(impl->shell_surface, params->class_name); } wl_surface_commit(impl->surface); // TODO: Error check struct wl_region *region = wl_compositor_create_region(impl->compositor); wl_region_add(region, 0, 0, self->size.x, self->size.y); wl_surface_set_opaque_region(impl->surface, region); wl_region_destroy(region); impl->window = wl_egl_window_create(impl->surface, self->size.x, self->size.y); if(!impl->window) { fprintf(stderr, "mgl error: mgl_wayland_setup_window: failed to create the Wayland window\n"); return false; } //mgl_window_x11_on_move(self, window_pos.x, window_pos.y); mgl_window_wayland_on_resize(self, self->size.x, self->size.y); self->open = true; self->focused = false; return true; } static mgl_window_handle mgl_window_wayland_get_system_handle(const mgl_window *self) { const mgl_window_wayland *impl = self->impl; return (mgl_window_handle)impl->window; } static void mgl_window_wayland_close(mgl_window *self) { mgl_window_wayland *impl = self->impl; if(impl->window) { wl_egl_window_destroy(impl->window); impl->window = NULL; } if(impl->surface) { wl_surface_destroy(impl->surface); impl->surface = NULL; } self->open = false; } static bool mgl_window_wayland_poll_event(mgl_window *self, mgl_event *event) { mgl_context *context = mgl_get_context(); struct wl_display *display = context->connection; const bool events_available = wl_display_dispatch_pending(display) > 0; //wl_display_flush(display); /* TODO: Return event here, pop from circular buffer */ return events_available; } static void mgl_window_wayland_swap_buffers(mgl_window *self) { mgl_window_wayland *impl = self->impl; mgl_graphics_swap_buffers(&impl->graphics, (mgl_window_handle)impl->window); } static void mgl_window_wayland_set_visible(mgl_window *self, bool visible) { fprintf(stderr, "TODO: Implement\n"); } static bool mgl_window_wayland_is_key_pressed(const mgl_window *self, mgl_key key) { (void)self; if(key < 0 || key >= __MGL_NUM_KEYS__) return false; fprintf(stderr, "TODO: Implement\n"); return false; } static bool mgl_window_wayland_is_mouse_button_pressed(const mgl_window *self, mgl_mouse_button button) { (void)self; if(button < 0 || button >= __MGL_NUM_MOUSE_BUTTONS__) return false; fprintf(stderr, "TODO: Implement\n"); return false; } static void mgl_window_wayland_set_title(mgl_window *self, const char *title) { mgl_window_wayland *impl = self->impl; // TODO: Check if wl_display_flush is needed after these calls if(impl->xdg_toplevel) xdg_toplevel_set_title(impl->xdg_toplevel, title); else wl_shell_surface_set_title(impl->shell_surface, title); } static void mgl_window_wayland_set_cursor_visible(mgl_window *self, bool visible) { fprintf(stderr, "TODO: Implement\n"); } static void mgl_window_wayland_set_vsync_enabled(mgl_window *self, bool enabled) { mgl_window_wayland *impl = self->impl; self->vsync_enabled = enabled; if(!mgl_graphics_set_swap_interval(&impl->graphics, (mgl_window_handle)impl->window, self->vsync_enabled)) fprintf(stderr, "mgl warning: mgl_window_wayland_set_vsync_enabled: failed to enable vsync\n"); } static bool mgl_window_wayland_is_vsync_enabled(const mgl_window *self) { return self->vsync_enabled; } static void mgl_window_wayland_set_fullscreen(mgl_window *self, bool fullscreen) { mgl_window_wayland *impl = self->impl; // TODO: The last argument is the monitor we want to fullscreen on. Use this! if(impl->xdg_toplevel) { if(fullscreen) xdg_toplevel_set_fullscreen(impl->xdg_toplevel, NULL); else xdg_toplevel_unset_fullscreen(impl->xdg_toplevel); } else { if(fullscreen) wl_shell_surface_set_fullscreen(impl->shell_surface, WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0, NULL); else fprintf(stderr, "TODO: Implement\n"); } } static bool mgl_window_wayland_is_fullscreen(const mgl_window *self) { fprintf(stderr, "TODO: Implement\n"); return false; } static void mgl_window_wayland_set_position(mgl_window *self, mgl_vec2i position) { fprintf(stderr, "TODO: Implement\n"); } static void mgl_window_wayland_set_size(mgl_window *self, mgl_vec2i size) { fprintf(stderr, "TODO: Implement\n"); } static void mgl_window_wayland_set_size_limits(mgl_window *self, mgl_vec2i minimum, mgl_vec2i maximum) { mgl_window_wayland *impl = self->impl; if(impl->xdg_toplevel) { xdg_toplevel_set_min_size(impl->xdg_toplevel, minimum.x, minimum.y); xdg_toplevel_set_max_size(impl->xdg_toplevel, maximum.x, maximum.y); } else { fprintf(stderr, "TODO: Implement\n"); } } static void mgl_window_wayland_set_clipboard(mgl_window *self, const char *str, size_t size) { fprintf(stderr, "TODO: Implement\n"); } static bool mgl_window_wayland_get_clipboard(mgl_window *self, mgl_clipboard_callback callback, void *userdata, uint32_t clipboard_types) { fprintf(stderr, "TODO: Implement\n"); return false; } static void mgl_window_wayland_set_key_repeat_enabled(mgl_window *self, bool enabled) { self->key_repeat_enabled = enabled; // TODO: Implement } static void mgl_window_wayland_flush(mgl_window *self) { (void)self; mgl_context *context = mgl_get_context(); struct wl_display *display = context->connection; wl_display_flush(display); } static void* mgl_window_wayland_get_egl_display(mgl_window *self) { mgl_window_wayland *impl = self->impl; if(impl->graphics.graphics_api == MGL_GRAPHICS_API_EGL) return mgl_graphics_get_display(&impl->graphics); else return NULL; } static void* mgl_window_wayland_get_egl_context(mgl_window *self) { mgl_window_wayland *impl = self->impl; if(impl->graphics.graphics_api == MGL_GRAPHICS_API_EGL) return mgl_graphics_get_context(&impl->graphics); else return NULL; } static void mgl_window_wayland_for_each_active_monitor_output(mgl_window *self, mgl_active_monitor_callback callback, void *userdata) { fprintf(stderr, "TODO: Implement\n"); } bool mgl_window_wayland_init(mgl_window *self, const char *title, const mgl_window_create_params *params, mgl_window_handle existing_window) { mgl_window_wayland *impl = calloc(1, sizeof(mgl_window_wayland)); if(!impl) return false; self->get_system_handle = mgl_window_wayland_get_system_handle; self->deinit = mgl_window_wayland_deinit; self->close = mgl_window_wayland_close; self->poll_event = mgl_window_wayland_poll_event; self->swap_buffers = mgl_window_wayland_swap_buffers; self->set_visible = mgl_window_wayland_set_visible; self->is_key_pressed = mgl_window_wayland_is_key_pressed; self->is_mouse_button_pressed = mgl_window_wayland_is_mouse_button_pressed; self->set_title = mgl_window_wayland_set_title; self->set_cursor_visible = mgl_window_wayland_set_cursor_visible; self->set_vsync_enabled = mgl_window_wayland_set_vsync_enabled; self->is_vsync_enabled = mgl_window_wayland_is_vsync_enabled; self->set_fullscreen = mgl_window_wayland_set_fullscreen; self->is_fullscreen = mgl_window_wayland_is_fullscreen; self->set_position = mgl_window_wayland_set_position; self->set_size = mgl_window_wayland_set_size; self->set_size_limits = mgl_window_wayland_set_size_limits; self->set_clipboard = mgl_window_wayland_set_clipboard; self->get_clipboard = mgl_window_wayland_get_clipboard; self->set_key_repeat_enabled = mgl_window_wayland_set_key_repeat_enabled; self->flush = mgl_window_wayland_flush; self->get_egl_display = mgl_window_wayland_get_egl_display; self->get_egl_context = mgl_window_wayland_get_egl_context; self->for_each_active_monitor_output = mgl_window_wayland_for_each_active_monitor_output; self->impl = impl; if(!mgl_wayland_setup_window(self, title, params, existing_window)) { mgl_window_wayland_deinit(self); return false; } assert(!params || params->graphics_api == MGL_GRAPHICS_API_EGL); const bool alpha = params && params->support_alpha; if(!mgl_graphics_init(&impl->graphics, &(mgl_graphics_create_params){ .graphics_api = MGL_GRAPHICS_API_EGL, .alpha = alpha })) { mgl_window_wayland_deinit(self); return false; } if(!mgl_graphics_make_context_current(&impl->graphics, impl->window)) { fprintf(stderr, "mgl error: mgl_window_wayland_init: failed to make window context current\n"); mgl_window_wayland_deinit(self); return false; } self->vsync_enabled = true; mgl_graphics_set_swap_interval(&impl->graphics, (mgl_window_handle)impl->window, self->vsync_enabled); mgl_context *context = mgl_get_context(); context->current_window = self; return true; } void mgl_window_wayland_deinit(mgl_window *self) { mgl_window_wayland *impl = self->impl; if(!impl) return; mgl_graphics_deinit(&impl->graphics); mgl_window_wayland_close(self); if(impl->xdg_toplevel) { xdg_toplevel_destroy(impl->xdg_toplevel); impl->xdg_toplevel = NULL; } if(impl->xdg_surface) { xdg_surface_destroy(impl->xdg_surface); impl->xdg_surface = NULL; } if(impl->shell_surface) { wl_shell_surface_destroy(impl->shell_surface); impl->shell_surface = NULL; } if(impl->pointer) { wl_pointer_release(impl->pointer); impl->pointer = NULL; } if(impl->keyboard) { wl_keyboard_release(impl->keyboard); impl->keyboard = NULL; } if(impl->seat) { wl_seat_destroy(impl->seat); impl->seat = NULL; } if(impl->shell) { wl_shell_destroy(impl->shell); impl->shell = NULL; } if(impl->xdg_wm_base) { xdg_wm_base_destroy(impl->xdg_wm_base); impl->xdg_wm_base = NULL; } if(impl->compositor) { wl_compositor_destroy(impl->compositor); impl->compositor = NULL; } if(impl->registry) { wl_registry_destroy(impl->registry); impl->registry = NULL; } mgl_context *context = mgl_get_context(); if(context->current_window == self) context->current_window = NULL; free(self->impl); self->impl = NULL; }