Toggleable window

Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
2025-10-05 01:25:05 +03:00
parent 6e86ff41db
commit 7397917c61

View File

@@ -3,6 +3,7 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <optional>
#include <poll.h>
#include <sys/file.h>
#include <sys/signalfd.h>
@@ -50,6 +51,8 @@ struct App {
auto set_visible(bool visible) -> void;
auto visible() const -> bool { return m_visible; }
auto stop() -> void { m_running = false; }
private:
auto init_wayland() -> void;
auto init_egl() -> void;
@@ -57,6 +60,10 @@ private:
auto init_theme_portal() -> void;
auto pump_events() -> void;
auto render_frame() -> void;
auto create_layer_surface() -> void;
auto destroy_layer_surface() -> void;
auto ensure_egl_surface() -> void;
auto update_blur_region() -> void;
auto theme() const -> ColorScheme const & { return m_themes[m_active_theme]; }
static void on_settings_changed(XdpSettings * /*self*/, const char *ns,
@@ -104,19 +111,14 @@ App::~App() {
if (m_sfd != -1)
close(m_sfd);
destroy_layer_surface();
if (m_gl.edpy != EGL_NO_DISPLAY) {
eglMakeCurrent(m_gl.edpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (m_gl.esurf != EGL_NO_SURFACE)
eglDestroySurface(m_gl.edpy, m_gl.esurf);
if (m_gl.wegl)
wl_egl_window_destroy(m_gl.wegl);
if (m_gl.ectx != EGL_NO_CONTEXT)
eglDestroyContext(m_gl.edpy, m_gl.ectx);
eglTerminate(m_gl.edpy);
}
if (m_wayland.wl_surface)
wl_surface_destroy(m_wayland.wl_surface);
if (m_wayland.compositor)
wl_compositor_destroy(m_wayland.compositor);
if (m_wayland.registry)
@@ -169,99 +171,22 @@ auto App::init_wayland() -> void {
wl_display_roundtrip(m_wayland.display);
m_wayland.wl_surface = wl_compositor_create_surface(m_wayland.compositor);
if (m_wayland.mgr && !m_wayland.eff) {
m_wayland.eff = ext_background_effect_manager_v1_get_background_effect(
m_wayland.mgr, m_wayland.wl_surface);
}
if (m_wayland.kde_blur_mgr && !m_wayland.kde_blur) {
m_wayland.kde_blur = org_kde_kwin_blur_manager_create(
m_wayland.kde_blur_mgr, m_wayland.wl_surface);
}
auto apply_blur_full_surface = [&] {
wl_region *r = wl_compositor_create_region(m_wayland.compositor);
wl_region_add(r, 0, 0, m_win_w, m_win_h);
if (m_wayland.eff) {
ext_background_effect_surface_v1_set_blur_region(m_wayland.eff, r);
}
if (m_wayland.kde_blur) {
org_kde_kwin_blur_set_region(m_wayland.kde_blur, r);
}
wl_region_destroy(r);
};
m_wayland.layer_surface = zwlr_layer_shell_v1_get_layer_surface(
m_wayland.layer_shell, m_wayland.wl_surface, /*output*/ nullptr,
ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "waylight-overlay");
zwlr_layer_surface_v1_set_anchor(m_wayland.layer_surface, 0);
zwlr_layer_surface_v1_set_size(m_wayland.layer_surface, m_win_w, m_win_h);
zwlr_layer_surface_v1_set_exclusive_zone(m_wayland.layer_surface, 0);
if (zwlr_layer_shell_v1_get_version(m_wayland.layer_shell) >= 3)
zwlr_layer_surface_v1_set_keyboard_interactivity(
m_wayland.layer_surface,
ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND);
auto handle_layer_configure = [](void *data, zwlr_layer_surface_v1 *ls,
uint32_t serial, uint32_t w,
uint32_t h) -> void {
auto *app = static_cast<App *>(data);
app->m_win_w = (int)(w ? w : app->m_win_w);
app->m_win_h = (int)(h ? h : app->m_win_h);
zwlr_layer_surface_v1_ack_configure(ls, serial);
if (!app->m_gl.wegl) {
app->m_gl.wegl = wl_egl_window_create(app->m_wayland.wl_surface,
app->m_win_w, app->m_win_h);
app->m_gl.esurf =
eglCreateWindowSurface(app->m_gl.edpy, app->m_gl.ecfg,
(EGLNativeWindowType)app->m_gl.wegl, nullptr);
eglMakeCurrent(app->m_gl.edpy, app->m_gl.esurf, app->m_gl.esurf,
app->m_gl.ectx);
eglSwapInterval(app->m_gl.edpy, 1);
} else {
wl_egl_window_resize(app->m_gl.wegl, app->m_win_w, app->m_win_h, 0, 0);
}
if (app->m_wayland.wl_surface)
wl_surface_commit(app->m_wayland.wl_surface);
};
auto handle_layer_closed = [](void *data, zwlr_layer_surface_v1 *) -> void {
static_cast<App *>(data)->m_running = false;
};
static const zwlr_layer_surface_v1_listener lsl = {
.configure = handle_layer_configure,
.closed = handle_layer_closed,
};
zwlr_layer_surface_v1_add_listener(m_wayland.layer_surface, &lsl, this);
apply_blur_full_surface();
wl_surface_commit(m_wayland.wl_surface);
wl_display_roundtrip(m_wayland.display);
create_layer_surface();
}
auto App::set_visible(bool visible) -> void {
if (!m_wayland.layer_surface)
if (visible == m_visible)
return;
if (visible) {
zwlr_layer_surface_v1_set_size(m_wayland.layer_surface, m_win_w, m_win_h);
wl_surface_commit(m_wayland.wl_surface);
create_layer_surface();
ensure_egl_surface();
} else {
wl_surface_attach(m_wayland.wl_surface, nullptr, 0, 0);
wl_surface_commit(m_wayland.wl_surface);
destroy_layer_surface();
}
wl_display_flush(m_wayland.display);
m_visible = visible;
if (m_wayland.display)
wl_display_flush(m_wayland.display);
}
auto App::init_egl() -> void {
@@ -291,13 +216,7 @@ auto App::init_egl() -> void {
m_gl.ectx =
eglCreateContext(m_gl.edpy, m_gl.ecfg, EGL_NO_CONTEXT, ctxAttribs);
m_gl.wegl = wl_egl_window_create(m_wayland.wl_surface, m_win_w, m_win_h);
m_gl.esurf = eglCreateWindowSurface(
m_gl.edpy, m_gl.ecfg, reinterpret_cast<EGLNativeWindowType>(m_gl.wegl),
nullptr);
eglMakeCurrent(m_gl.edpy, m_gl.esurf, m_gl.esurf, m_gl.ectx);
eglSwapInterval(m_gl.edpy, 1);
ensure_egl_surface();
InitWindow(m_win_w, m_win_h, "");
}
@@ -352,6 +271,9 @@ auto App::init_theme_portal() -> void {
}
auto App::render_frame() -> void {
if (!m_visible || m_gl.edpy == EGL_NO_DISPLAY || m_gl.esurf == EGL_NO_SURFACE)
return;
glViewport(0, 0, m_win_w, m_win_h);
BeginDrawing();
@@ -365,6 +287,188 @@ auto App::render_frame() -> void {
eglSwapBuffers(m_gl.edpy, m_gl.esurf);
}
auto App::create_layer_surface() -> void {
if (m_wayland.layer_surface)
return;
if (!m_wayland.compositor || !m_wayland.layer_shell)
return;
m_wayland.wl_surface = wl_compositor_create_surface(m_wayland.compositor);
if (m_wayland.mgr) {
m_wayland.eff = ext_background_effect_manager_v1_get_background_effect(
m_wayland.mgr, m_wayland.wl_surface);
}
if (m_wayland.kde_blur_mgr) {
m_wayland.kde_blur = org_kde_kwin_blur_manager_create(
m_wayland.kde_blur_mgr, m_wayland.wl_surface);
}
m_wayland.layer_surface = zwlr_layer_shell_v1_get_layer_surface(
m_wayland.layer_shell, m_wayland.wl_surface, nullptr,
ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "waylight-overlay");
if (!m_wayland.layer_surface) {
if (m_wayland.eff) {
ext_background_effect_surface_v1_destroy(m_wayland.eff);
m_wayland.eff = nullptr;
}
if (m_wayland.kde_blur) {
org_kde_kwin_blur_destroy(m_wayland.kde_blur);
m_wayland.kde_blur = nullptr;
}
if (m_wayland.wl_surface) {
wl_surface_destroy(m_wayland.wl_surface);
m_wayland.wl_surface = nullptr;
}
return;
}
zwlr_layer_surface_v1_set_anchor(m_wayland.layer_surface, 0);
zwlr_layer_surface_v1_set_size(m_wayland.layer_surface, m_win_w, m_win_h);
zwlr_layer_surface_v1_set_exclusive_zone(m_wayland.layer_surface, 0);
if (zwlr_layer_shell_v1_get_version(m_wayland.layer_shell) >= 3) {
zwlr_layer_surface_v1_set_keyboard_interactivity(
m_wayland.layer_surface,
ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND);
}
auto handle_layer_configure = [](void *data, zwlr_layer_surface_v1 *ls,
uint32_t serial, uint32_t w,
uint32_t h) -> void {
auto *app = static_cast<App *>(data);
if (w)
app->m_win_w = static_cast<int>(w);
if (h)
app->m_win_h = static_cast<int>(h);
zwlr_layer_surface_v1_ack_configure(ls, serial);
if (app->m_gl.edpy != EGL_NO_DISPLAY) {
if (!app->m_gl.wegl || app->m_gl.esurf == EGL_NO_SURFACE) {
app->ensure_egl_surface();
} else {
wl_egl_window_resize(app->m_gl.wegl, app->m_win_w, app->m_win_h, 0, 0);
eglMakeCurrent(app->m_gl.edpy, app->m_gl.esurf, app->m_gl.esurf,
app->m_gl.ectx);
}
}
app->update_blur_region();
if (app->m_wayland.wl_surface)
wl_surface_commit(app->m_wayland.wl_surface);
};
auto handle_layer_closed = [](void *data, zwlr_layer_surface_v1 *) -> void {
static_cast<App *>(data)->m_running = false;
};
static const zwlr_layer_surface_v1_listener lsl = {
.configure = handle_layer_configure,
.closed = handle_layer_closed,
};
zwlr_layer_surface_v1_add_listener(m_wayland.layer_surface, &lsl, this);
update_blur_region();
if (m_wayland.wl_surface)
wl_surface_commit(m_wayland.wl_surface);
if (m_wayland.display)
wl_display_roundtrip(m_wayland.display);
ensure_egl_surface();
m_visible = true;
}
auto App::destroy_layer_surface() -> void {
if (m_gl.edpy != EGL_NO_DISPLAY && m_gl.esurf != EGL_NO_SURFACE) {
eglMakeCurrent(m_gl.edpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroySurface(m_gl.edpy, m_gl.esurf);
m_gl.esurf = EGL_NO_SURFACE;
}
if (m_gl.wegl) {
wl_egl_window_destroy(m_gl.wegl);
m_gl.wegl = nullptr;
}
if (m_wayland.eff) {
ext_background_effect_surface_v1_destroy(m_wayland.eff);
m_wayland.eff = nullptr;
}
if (m_wayland.kde_blur) {
org_kde_kwin_blur_destroy(m_wayland.kde_blur);
m_wayland.kde_blur = nullptr;
}
if (m_wayland.layer_surface) {
zwlr_layer_surface_v1_destroy(m_wayland.layer_surface);
m_wayland.layer_surface = nullptr;
}
if (m_wayland.wl_surface) {
wl_surface_destroy(m_wayland.wl_surface);
m_wayland.wl_surface = nullptr;
}
if (m_wayland.display)
wl_display_flush(m_wayland.display);
m_visible = false;
}
auto App::ensure_egl_surface() -> void {
if (m_gl.edpy == EGL_NO_DISPLAY || m_gl.ectx == EGL_NO_CONTEXT)
return;
if (!m_wayland.wl_surface)
return;
if (!m_gl.wegl)
m_gl.wegl = wl_egl_window_create(m_wayland.wl_surface, m_win_w, m_win_h);
if (!m_gl.wegl)
return;
if (m_gl.esurf == EGL_NO_SURFACE) {
m_gl.esurf = eglCreateWindowSurface(
m_gl.edpy, m_gl.ecfg, reinterpret_cast<EGLNativeWindowType>(m_gl.wegl),
nullptr);
}
if (m_gl.esurf == EGL_NO_SURFACE)
return;
eglMakeCurrent(m_gl.edpy, m_gl.esurf, m_gl.esurf, m_gl.ectx);
eglSwapInterval(m_gl.edpy, 1);
}
auto App::update_blur_region() -> void {
if (!m_wayland.compositor)
return;
if (!m_wayland.eff && !m_wayland.kde_blur)
return;
wl_region *region = wl_compositor_create_region(m_wayland.compositor);
if (!region)
return;
wl_region_add(region, 0, 0, m_win_w, m_win_h);
if (m_wayland.eff)
ext_background_effect_surface_v1_set_blur_region(m_wayland.eff, region);
if (m_wayland.kde_blur)
org_kde_kwin_blur_set_region(m_wayland.kde_blur, region);
wl_region_destroy(region);
}
auto App::pump_events() -> void {
while (g_main_context_iteration(nullptr, false))
;
@@ -422,11 +526,18 @@ bool check_or_signal_running() {
return false;
}
std::optional<App> g_app{};
auto main() -> int {
if (check_or_signal_running()) {
return 0;
}
App app;
app.run();
std::signal(SIGINT, [](int) {
if (g_app)
g_app->stop();
});
g_app.emplace();
g_app->run();
}