// ===================================================================================================================== // fennec, a free and open source game engine // Copyright © 2025 Medusa Slockbower // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // ===================================================================================================================== #include #include #include #include #include #include #if FENNEC_HAS_LIBDECOR #include #endif using namespace fennec; wayland_window::wayland_window(display_server* server, uint32_t id, const config& cfg) : window_base(server, id, cfg) , surface(nullptr) , xdgsurface(nullptr) { } wayland_window::~wayland_window() { } void wayland_window::show() { static constexpr wl_surface_listener surface_listener = { .enter = _wl_surface_listen_enter, .leave = _wl_surface_listen_leave, .preferred_buffer_scale = _wl_surface_listen_preferred_buffer_scale, .preferred_buffer_transform = _wl_surface_listen_preferred_buffer_transform }; static constexpr xdg_surface_listener xdg_surface_listener = { .configure = _xdg_surface_listen_configure }; static constexpr xdg_toplevel_listener xdg_toplevel_listener = { .configure = _xdg_toplevel_listen_configure, .close = _xdg_toplevel_listen_close, .configure_bounds = _xdg_toplevel_listen_configure_bounds, .wm_capabilities = _xdg_toplevel_listen_wm_capabilities }; static constexpr wl_callback_listener frame_callback_listener = { .done = _wl_frame_listen_done }; if (is_visible()) { return; } if (is_running()) { // reshow window return; } wayland_window* root = this; while (root != nullptr and root->is_popup()) { root = static_cast(root->get_parent()); } assertf(root != nullptr, "Failed to find appropriate top-level window."); wayland_server* wl_server = static_cast(server); surface = wl_compositor_create_surface(wl_server->compositor); wl_surface_add_listener(surface, &surface_listener, this); #if FENNEC_HAS_LIBDECOR static libdecor_frame_interface libdecor_frame_listener = { .configure = _libdecor_frame_listen_configure, .close = _libdecor_frame_listen_close, .commit = _libdecor_frame_listen_commit, .dismiss_popup = _libdecor_frame_listen_dismiss_popup, .reserved0 = nullptr, .reserved1 = nullptr, .reserved2 = nullptr, .reserved3 = nullptr, .reserved4 = nullptr, .reserved5 = nullptr, .reserved6 = nullptr, .reserved7 = nullptr, .reserved8 = nullptr, .reserved9 = nullptr, }; if (wl_server->has_libdecor) { libdecorframe = libdecor_decorate(wl_server->libdecor, surface, &libdecor_frame_listener, this); libdecor_frame_map(libdecorframe); xdgsurface = libdecor_frame_get_xdg_surface(libdecorframe); xdgtoplevel = libdecor_frame_get_xdg_toplevel(libdecorframe); } else #endif { xdgsurface = xdg_wm_base_get_xdg_surface(wl_server->xdg, surface); xdg_surface_add_listener(xdgsurface, &xdg_surface_listener, this); xdgtoplevel = xdg_surface_get_toplevel(xdgsurface); xdg_toplevel_add_listener(xdgtoplevel, &xdg_toplevel_listener, this); } frame_callback = wl_surface_frame(surface); wl_callback_add_listener(frame_callback, &frame_callback_listener, this); wl_surface_commit(surface); wl_display_roundtrip(wl_server->display); cfg.flags.set(flag_visible); cfg.flags.set(flag_running); gfx_surface = wl_server->get_gfx_context()->create_surface(this); } void wayland_window::hide() { if (not is_running()) { return; } wayland_server* wl_server = static_cast(server); if (frame_callback) { wl_callback_destroy(frame_callback); frame_callback = nullptr; } #if FENNEC_HAS_LIBDECOR if (libdecorframe) { libdecor_frame_unref(libdecorframe); xdgtoplevel = nullptr; xdgsurface = nullptr; } #endif if (xdgtoplevel) { xdg_toplevel_destroy(xdgtoplevel); xdgtoplevel = nullptr; } if (xdgsurface) { xdg_surface_destroy(xdgsurface); xdgsurface = nullptr; } if (surface) { wl_surface_destroy(surface); surface = nullptr; } wl_display_roundtrip(wl_server->display); cfg.flags.clear(flag_visible); cfg.flags.clear(flag_running); } void* wayland_window::get_native_handle() { return surface; } bool wayland_window::set_flag(uint8_t, bool) { return false; } // Surface Listeners void wayland_window::_wl_surface_listen_enter(void*, wl_surface*, wl_output*) {} void wayland_window::_wl_surface_listen_leave(void*, wl_surface*, wl_output*) {} void wayland_window::_wl_surface_listen_preferred_buffer_scale(void*, wl_surface*, int32_t) {} void wayland_window::_wl_surface_listen_preferred_buffer_transform(void*, wl_surface*, uint32_t) {} // Frame Listeners void wayland_window::_wl_frame_listen_done(void*, wl_callback*, uint32_t) {} // XDG Listeners void wayland_window::_xdg_surface_listen_configure(void*, xdg_surface* xdg, uint32_t serial) { xdg_surface_ack_configure(xdg, serial); } void wayland_window::_xdg_toplevel_listen_configure(void*, xdg_toplevel*, int32_t, int32_t, wl_array*) {} void wayland_window::_xdg_toplevel_listen_configure_bounds(void*, xdg_toplevel*, int32_t, int32_t) {} void wayland_window::_xdg_toplevel_listen_close(void* data, xdg_toplevel*) { wayland_window* window = static_cast(data); window->hide(); } void wayland_window::_xdg_toplevel_listen_wm_capabilities(void*, xdg_toplevel*, wl_array*) {} // Libdecor Listeners #if FENNEC_HAS_LIBDECOR void wayland_window::_libdecor_frame_listen_configure(libdecor_frame*, libdecor_configuration*, void*) {} void wayland_window::_libdecor_frame_listen_close(libdecor_frame*, void* data) { wayland_window* window = static_cast(data); window->hide(); } void wayland_window::_libdecor_frame_listen_commit(libdecor_frame*, void*) {} void wayland_window::_libdecor_frame_listen_dismiss_popup(libdecor_frame*, const char*, void*) {} #endif