164
CMakeLists.txt
164
CMakeLists.txt
@@ -1,32 +1,37 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(skia_wayland_nodeco LANGUAGES C CXX)
|
||||
project(waylight LANGUAGES C CXX)
|
||||
|
||||
# --- toolchain & opts ---------------------------------------------------------
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
# --- deps (pkg-config) --------------------------------------------------------
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(WAYLAND_CLIENT REQUIRED wayland-client)
|
||||
pkg_check_modules(WAYLAND_EGL REQUIRED wayland-egl)
|
||||
pkg_check_modules(EGL REQUIRED egl)
|
||||
pkg_check_modules(GLES2 REQUIRED glesv2)
|
||||
# Skia package name varies; this works on many distros. Adjust if needed.
|
||||
pkg_check_modules(SKIA REQUIRED skia)
|
||||
# The protocol XMLs live here:
|
||||
pkg_check_modules(WAYLAND_CLIENT REQUIRED IMPORTED_TARGET wayland-client)
|
||||
pkg_check_modules(WAYLAND_EGL REQUIRED IMPORTED_TARGET wayland-egl)
|
||||
pkg_check_modules(EGL REQUIRED IMPORTED_TARGET egl)
|
||||
pkg_check_modules(GLES2 REQUIRED IMPORTED_TARGET glesv2)
|
||||
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
|
||||
pkg_check_modules(LIBPORTAL REQUIRED IMPORTED_TARGET libportal)
|
||||
pkg_check_modules(WAYLAND_PROTOCOLS REQUIRED wayland-protocols)
|
||||
pkg_check_modules(WLR_PROTOCOLS REQUIRED wlr-protocols)
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
FetchContent_Declare(
|
||||
raylib
|
||||
GIT_REPOSITORY https://github.com/slendidev/raylib.git
|
||||
GIT_TAG "lunar"
|
||||
GIT_SHALLOW 1
|
||||
)
|
||||
set(OPENGL_VERSION "ES 2.0")
|
||||
set(PLATFORM DRM)
|
||||
set(BUILD_EXAMPLES OFF)
|
||||
FetchContent_MakeAvailable(raylib)
|
||||
|
||||
# --- scanners -----------------------------------------------------------------
|
||||
find_program(WAYLAND_SCANNER wayland-scanner REQUIRED)
|
||||
# waylandpp's generator; provided by the waylandpp package
|
||||
# (typically installed as 'wayland-scanner++')
|
||||
find_program(WAYLAND_SCANNER_PP wayland-scanner++ REQUIRED)
|
||||
|
||||
# --- protocol XML paths -------------------------------------------------------
|
||||
pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir)
|
||||
|
||||
# fallback via datarootdir if needed
|
||||
if(NOT WAYLAND_PROTOCOLS_DIR OR NOT EXISTS "${WAYLAND_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml")
|
||||
pkg_get_variable(_WP_DATAROOT wayland-protocols datarootdir)
|
||||
if(_WP_DATAROOT AND EXISTS "${_WP_DATAROOT}/wayland-protocols")
|
||||
@@ -34,7 +39,6 @@ if(NOT WAYLAND_PROTOCOLS_DIR OR NOT EXISTS "${WAYLAND_PROTOCOLS_DIR}/stable/xdg-
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# last-resort search (covers odd layouts)
|
||||
if(NOT WAYLAND_PROTOCOLS_DIR OR NOT EXISTS "${WAYLAND_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml")
|
||||
find_path(WAYLAND_PROTOCOLS_DIR
|
||||
NAMES stable/xdg-shell/xdg-shell.xml
|
||||
@@ -50,114 +54,92 @@ endif()
|
||||
set(XDG_SHELL_XML
|
||||
"${WAYLAND_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml"
|
||||
)
|
||||
set(XDG_DECOR_XML
|
||||
"${WAYLAND_PROTOCOLS_DIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml"
|
||||
)
|
||||
|
||||
# --- generated outputs (into build dir) ---------------------------------------
|
||||
set(GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
|
||||
file(MAKE_DIRECTORY "${GEN_DIR}")
|
||||
|
||||
# waylandpp C++ headers + code (used by the app)
|
||||
set(GEN_CXX_HEADERS
|
||||
"${GEN_DIR}/xdg-shell-client-protocol.hpp"
|
||||
"${GEN_DIR}/xdg-decoration-unstable-v1-client-protocol.hpp"
|
||||
pkg_get_variable(WLR_PROTOCOLS_DIR wlr-protocols pkgdatadir)
|
||||
if(NOT WLR_PROTOCOLS_DIR OR NOT EXISTS "${WLR_PROTOCOLS_DIR}/unstable/wlr-layer-shell-unstable-v1.xml")
|
||||
find_path(WLR_PROTOCOLS_DIR
|
||||
NAMES unstable/wlr-layer-shell-unstable-v1.xml
|
||||
HINTS ${CMAKE_SYSTEM_PREFIX_PATH}
|
||||
PATH_SUFFIXES wlr-protocols share/wlr-protocols
|
||||
)
|
||||
set(GEN_CXX_SOURCES
|
||||
"${GEN_DIR}/xdg-shell-client-protocol.cpp"
|
||||
"${GEN_DIR}/xdg-decoration-unstable-v1-client-protocol.cpp"
|
||||
endif()
|
||||
if(NOT WLR_PROTOCOLS_DIR)
|
||||
message(FATAL_ERROR "Could not locate wlr-protocols datadir (install wlr-protocols?)")
|
||||
endif()
|
||||
|
||||
set(BACKGROUND_EFFECT_XML
|
||||
"${WAYLAND_PROTOCOLS_DIR}/staging/ext-background-effect/ext-background-effect-v1.xml"
|
||||
)
|
||||
|
||||
set(WLR_LAYER_SHELL_XML
|
||||
"${WLR_PROTOCOLS_DIR}/unstable/wlr-layer-shell-unstable-v1.xml"
|
||||
)
|
||||
|
||||
# (optional) C headers + private impls (not linked, but generated)
|
||||
set(GEN_C_HEADERS
|
||||
"${GEN_DIR}/xdg-shell-client-protocol.h"
|
||||
"${GEN_DIR}/xdg-decoration-unstable-v1-client-protocol.h"
|
||||
"${GEN_DIR}/wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||
"${GEN_DIR}/ext-background-effect-v1-client-protocol.h"
|
||||
"${GEN_DIR}/blur-client-protocol.h"
|
||||
)
|
||||
set(GEN_C_PRIVATES
|
||||
"${GEN_DIR}/xdg-shell-protocol.c"
|
||||
"${GEN_DIR}/xdg-decoration-unstable-v1-protocol.c"
|
||||
"${GEN_DIR}/wlr-layer-shell-unstable-v1-protocol.c"
|
||||
"${GEN_DIR}/ext-background-effect-v1-protocol.c"
|
||||
"${GEN_DIR}/blur-protocol.c"
|
||||
)
|
||||
|
||||
# --- custom commands: generate C++ (required) ---------------------------------
|
||||
add_custom_command(
|
||||
OUTPUT ${GEN_CXX_HEADERS} ${GEN_CXX_SOURCES}
|
||||
COMMAND "${WAYLAND_SCANNER_PP}" "${XDG_SHELL_XML}" "${GEN_DIR}/xdg-shell-client-protocol.hpp" "${GEN_DIR}/xdg-shell-client-protocol.cpp"
|
||||
COMMAND "${WAYLAND_SCANNER_PP}" "${XDG_DECOR_XML}" "${GEN_DIR}/xdg-decoration-unstable-v1-client-protocol.hpp" "${GEN_DIR}/xdg-decoration-unstable-v1-client-protocol.cpp"
|
||||
DEPENDS "${XDG_SHELL_XML}" "${XDG_DECOR_XML}"
|
||||
COMMENT "Generating waylandpp C++ protocol headers/sources with wayland-scanner++"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
# --- custom commands: generate C (optional) -----------------------------------
|
||||
add_custom_command(
|
||||
OUTPUT ${GEN_C_HEADERS} ${GEN_C_PRIVATES}
|
||||
COMMAND "${WAYLAND_SCANNER}" client-header "${XDG_SHELL_XML}" "${GEN_DIR}/xdg-shell-client-protocol.h"
|
||||
COMMAND "${WAYLAND_SCANNER}" private-code "${XDG_SHELL_XML}" "${GEN_DIR}/xdg-shell-protocol.c"
|
||||
COMMAND "${WAYLAND_SCANNER}" client-header "${XDG_DECOR_XML}" "${GEN_DIR}/xdg-decoration-unstable-v1-client-protocol.h"
|
||||
COMMAND "${WAYLAND_SCANNER}" private-code "${XDG_DECOR_XML}" "${GEN_DIR}/xdg-decoration-unstable-v1-protocol.c"
|
||||
DEPENDS "${XDG_SHELL_XML}" "${XDG_DECOR_XML}"
|
||||
COMMENT "Generating C client headers + private code with wayland-scanner (optional)"
|
||||
# ext-background-effect
|
||||
COMMAND "${WAYLAND_SCANNER}" client-header "${BACKGROUND_EFFECT_XML}" "${GEN_DIR}/ext-background-effect-v1-client-protocol.h"
|
||||
COMMAND "${WAYLAND_SCANNER}" private-code "${BACKGROUND_EFFECT_XML}" "${GEN_DIR}/ext-background-effect-v1-protocol.c"
|
||||
# wlr-layer-shell
|
||||
COMMAND "${WAYLAND_SCANNER}" client-header "${WLR_LAYER_SHELL_XML}" "${GEN_DIR}/wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||
COMMAND "${WAYLAND_SCANNER}" private-code "${WLR_LAYER_SHELL_XML}" "${GEN_DIR}/wlr-layer-shell-unstable-v1-protocol.c"
|
||||
# org-kde-win-blur
|
||||
COMMAND "${WAYLAND_SCANNER}" client-header "${CMAKE_CURRENT_SOURCE_DIR}/blur.xml" "${GEN_DIR}/blur-client-protocol.h"
|
||||
COMMAND "${WAYLAND_SCANNER}" private-code "${CMAKE_CURRENT_SOURCE_DIR}/blur.xml" "${GEN_DIR}/blur-protocol.c"
|
||||
DEPENDS "${XDG_SHELL_XML}" "${WLR_LAYER_SHELL_XML}"
|
||||
COMMENT "Generating Wayland + wlr-layer-shell client headers and private code"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
add_custom_target(generate_protocols ALL
|
||||
DEPENDS ${GEN_CXX_HEADERS} ${GEN_CXX_SOURCES} ${GEN_C_HEADERS} ${GEN_C_PRIVATES}
|
||||
DEPENDS ${GEN_C_HEADERS} ${GEN_C_PRIVATES}
|
||||
)
|
||||
|
||||
# --- executable ---------------------------------------------------------------
|
||||
add_executable(skia_wayland
|
||||
${GEN_CXX_HEADERS}
|
||||
${GEN_CXX_SOURCES}
|
||||
add_executable(waylight
|
||||
${GEN_C_PRIVATES}
|
||||
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp
|
||||
)
|
||||
add_dependencies(skia_wayland generate_protocols)
|
||||
add_dependencies(waylight generate_protocols)
|
||||
|
||||
# include dirs (system + generated)
|
||||
target_include_directories(skia_wayland PRIVATE
|
||||
${WAYLAND_CLIENT_INCLUDE_DIRS}
|
||||
${WAYLAND_EGL_INCLUDE_DIRS}
|
||||
${EGL_INCLUDE_DIRS}
|
||||
${GLES2_INCLUDE_DIRS}
|
||||
${SKIA_INCLUDE_DIRS}
|
||||
# waylandpp headers (wayland-client.hpp). If they aren't system-wide, add your path here:
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/external/waylandpp/include
|
||||
target_include_directories(waylight PRIVATE
|
||||
${GEN_DIR}
|
||||
)
|
||||
|
||||
# libs
|
||||
target_link_libraries(skia_wayland PRIVATE
|
||||
${WAYLAND_CLIENT_LIBRARIES}
|
||||
${WAYLAND_EGL_LIBRARIES}
|
||||
${EGL_LIBRARIES}
|
||||
${GLES2_LIBRARIES}
|
||||
${SKIA_LIBRARIES}
|
||||
# some distros need these as well:
|
||||
target_link_libraries(waylight PRIVATE
|
||||
PkgConfig::WAYLAND_CLIENT
|
||||
PkgConfig::WAYLAND_EGL
|
||||
PkgConfig::EGL
|
||||
PkgConfig::GLES2
|
||||
PkgConfig::GLIB
|
||||
PkgConfig::LIBPORTAL
|
||||
|
||||
raylib
|
||||
|
||||
m
|
||||
dl
|
||||
pthread
|
||||
)
|
||||
|
||||
# cflags/ldflags from pkg-config
|
||||
target_compile_options(skia_wayland PRIVATE
|
||||
${WAYLAND_CLIENT_CFLAGS_OTHER}
|
||||
${WAYLAND_EGL_CFLAGS_OTHER}
|
||||
${EGL_CFLAGS_OTHER}
|
||||
${GLES2_CFLAGS_OTHER}
|
||||
${SKIA_CFLAGS_OTHER}
|
||||
)
|
||||
target_link_options(skia_wayland PRIVATE
|
||||
${WAYLAND_CLIENT_LDFLAGS_OTHER}
|
||||
${WAYLAND_EGL_LDFLAGS_OTHER}
|
||||
${EGL_LDFLAGS_OTHER}
|
||||
${GLES2_LDFLAGS_OTHER}
|
||||
${SKIA_LDFLAGS_OTHER}
|
||||
)
|
||||
|
||||
# --- warnings -----------------------------------------------------------------
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
|
||||
target_compile_options(skia_wayland PRIVATE -Wall -Wextra -Wno-unused-parameter)
|
||||
target_compile_options(waylight PRIVATE -Wall -Wextra -Wno-unused-parameter)
|
||||
endif()
|
||||
|
||||
# --- install (optional) -------------------------------------------------------
|
||||
install(TARGETS skia_wayland RUNTIME DESTINATION bin)
|
||||
install(TARGETS waylight RUNTIME DESTINATION bin)
|
||||
|
||||
28
blur.xml
Normal file
28
blur.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="blur">
|
||||
<copyright><![CDATA[
|
||||
SPDX-FileCopyrightText: 2015 Martin Gräßlin
|
||||
SPDX-FileCopyrightText: 2015 Marco Martin
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
]]></copyright>
|
||||
<interface name="org_kde_kwin_blur_manager" version="1">
|
||||
<request name="create">
|
||||
<arg name="id" type="new_id" interface="org_kde_kwin_blur"/>
|
||||
<arg name="surface" type="object" interface="wl_surface"/>
|
||||
</request>
|
||||
<request name="unset">
|
||||
<arg name="surface" type="object" interface="wl_surface"/>
|
||||
</request>
|
||||
</interface>
|
||||
<interface name="org_kde_kwin_blur" version="1">
|
||||
<request name="commit">
|
||||
</request>
|
||||
<request name="set_region">
|
||||
<arg name="region" type="object" interface="wl_region" allow-null="true"/>
|
||||
</request>
|
||||
<request name="release" type="destructor">
|
||||
<description summary="release the blur object"/>
|
||||
</request>
|
||||
</interface>
|
||||
</protocol>
|
||||
@@ -37,10 +37,11 @@
|
||||
pkg-config
|
||||
wayland
|
||||
wayland-protocols
|
||||
wlr-protocols
|
||||
wayland-scanner
|
||||
waylandpp
|
||||
libGL
|
||||
skia
|
||||
libportal
|
||||
glib
|
||||
]
|
||||
++ buildInputs
|
||||
++ nativeBuildInputs
|
||||
|
||||
200
main.cpp
200
main.cpp
@@ -1,200 +0,0 @@
|
||||
#include "xdg-shell-client-protocol.hpp"
|
||||
|
||||
#include "xdg-decoration-unstable-v1-client-protocol.hpp"
|
||||
|
||||
#include <wayland-client-protocol.hpp>
|
||||
#include <wayland-client.hpp>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <GLES2/gl2.h>
|
||||
#include <wayland-egl.h>
|
||||
|
||||
#include <skia/core/SkCanvas.h>
|
||||
#include <skia/core/SkColor.h>
|
||||
#include <skia/core/SkFont.h>
|
||||
#include <skia/core/SkPaint.h>
|
||||
#include <skia/core/SkSurface.h>
|
||||
#include <skia/core/SkTextBlob.h>
|
||||
#include <skia/gpu/GrDirectContext.h>
|
||||
#include <skia/gpu/ganesh/SkSurfaceGanesh.h>
|
||||
#include <skia/gpu/gl/GrGLInterface.h>
|
||||
#include <skia/gpu/gl/GrGLTypes.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <thread>
|
||||
|
||||
struct App {
|
||||
wayland::display_t display;
|
||||
wayland::registry_t registry;
|
||||
wayland::compositor_t compositor;
|
||||
wayland::xdg_wm_base_t xdg_wm;
|
||||
wayland::zxdg_decoration_manager_v1_t dec_mgr;
|
||||
wayland::surface_t wl_surface;
|
||||
wayland::xdg_surface_t xdg_surface;
|
||||
wayland::xdg_toplevel_t xdg_toplevel;
|
||||
|
||||
EGLDisplay edpy = EGL_NO_DISPLAY;
|
||||
EGLConfig ecfg = nullptr;
|
||||
EGLContext ectx = EGL_NO_CONTEXT;
|
||||
EGLSurface esurf = EGL_NO_SURFACE;
|
||||
wl_egl_window *wegl = nullptr;
|
||||
|
||||
sk_sp<const GrGLInterface> gl_iface;
|
||||
sk_sp<GrDirectContext> gr_ctx;
|
||||
sk_sp<SkSurface> sk_surface;
|
||||
|
||||
int win_w = 800, win_h = 600;
|
||||
bool running = true;
|
||||
|
||||
void init_wayland() {
|
||||
registry = display.get_registry();
|
||||
registry.on_global() = [&](uint32_t name, const std::string &iface,
|
||||
uint32_t ver) {
|
||||
if (iface == wayland::compositor_t::interface_name)
|
||||
compositor = registry.bind<wayland::compositor_t>(
|
||||
name, std::min<uint32_t>(ver, 4));
|
||||
else if (iface == wayland::xdg_wm_base_t::interface_name)
|
||||
xdg_wm = registry.bind<xdg_wm_base_t>(name, 1);
|
||||
else if (iface == wayland::zxdg_decoration_manager_v1_t::interface_name)
|
||||
dec_mgr = registry.bind<wayland::zxdg_decoration_manager_v1_t>(name, 1);
|
||||
};
|
||||
xdg_wm.on_ping() = [&](uint32_t serial) { xdg_wm.pong(serial); };
|
||||
display.roundtrip();
|
||||
if (!compositor || !xdg_wm) {
|
||||
fprintf(stderr, "missing compositor\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
wl_surface = compositor.create_surface();
|
||||
xdg_surface = xdg_wm.get_xdg_surface(wl_surface);
|
||||
xdg_toplevel = xdg_surface.get_toplevel();
|
||||
|
||||
if (dec_mgr) {
|
||||
auto deco = dec_mgr.get_toplevel_decoration(xdg_toplevel);
|
||||
deco.set_mode(wayland::zxdg_toplevel_decoration_v1_mode::client_side);
|
||||
}
|
||||
xdg_toplevel.set_title("Skia Hello World");
|
||||
xdg_toplevel.on_close() = [&] { running = false; };
|
||||
|
||||
xdg_surface.on_configure() = [&](uint32_t serial) {
|
||||
xdg_surface.ack_configure(serial);
|
||||
wl_surface.commit();
|
||||
};
|
||||
xdg_toplevel.on_configure() = [&](int32_t w, int32_t h,
|
||||
std::vector<uint32_t>) {
|
||||
if (w > 0 && h > 0 && (w != win_w || h != win_h)) {
|
||||
win_w = w;
|
||||
win_h = h;
|
||||
if (wegl) {
|
||||
wl_egl_window_resize(wegl, win_w, win_h, 0, 0);
|
||||
recreate_skia_surface();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
wl_surface.commit();
|
||||
display.roundtrip();
|
||||
}
|
||||
|
||||
void init_egl() {
|
||||
edpy = eglGetDisplay((EGLNativeDisplayType)display.c_ptr());
|
||||
eglInitialize(edpy, nullptr, nullptr);
|
||||
|
||||
const EGLint cfgAttribs[] = {EGL_SURFACE_TYPE,
|
||||
EGL_WINDOW_BIT,
|
||||
EGL_RENDERABLE_TYPE,
|
||||
EGL_OPENGL_ES2_BIT,
|
||||
EGL_RED_SIZE,
|
||||
8,
|
||||
EGL_GREEN_SIZE,
|
||||
8,
|
||||
EGL_BLUE_SIZE,
|
||||
8,
|
||||
EGL_ALPHA_SIZE,
|
||||
8,
|
||||
EGL_NONE};
|
||||
EGLint n = 0;
|
||||
eglChooseConfig(edpy, cfgAttribs, &ecfg, 1, &n);
|
||||
const EGLint ctxAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
|
||||
ectx = eglCreateContext(edpy, ecfg, EGL_NO_CONTEXT, ctxAttribs);
|
||||
wegl = wl_egl_window_create(wl_surface, win_w, win_h);
|
||||
esurf =
|
||||
eglCreateWindowSurface(edpy, ecfg, (EGLNativeWindowType)wegl, nullptr);
|
||||
eglMakeCurrent(edpy, esurf, esurf, ectx);
|
||||
eglSwapInterval(edpy, 1);
|
||||
}
|
||||
|
||||
void init_skia() {
|
||||
gl_iface = GrGLMakeNativeInterface();
|
||||
gr_ctx = GrDirectContext::MakeGL(gl_iface);
|
||||
recreate_skia_surface();
|
||||
}
|
||||
|
||||
void recreate_skia_surface() {
|
||||
GrGLFramebufferInfo fbInfo{0, GL_RGBA8};
|
||||
GrBackendRenderTarget backendRT(win_w, win_h, 0, 8, fbInfo);
|
||||
SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
|
||||
sk_surface = SkSurfaces::WrapBackendRenderTarget(
|
||||
gr_ctx.get(), backendRT, kBottomLeft_GrSurfaceOrigin,
|
||||
kRGBA_8888_SkColorType, nullptr, &props);
|
||||
}
|
||||
|
||||
void draw_frame() {
|
||||
SkCanvas *c = sk_surface->getCanvas();
|
||||
c->clear(SkColorSetARGB(255, 20, 22, 26));
|
||||
|
||||
SkPaint paint;
|
||||
paint.setAntiAlias(true);
|
||||
paint.setColor(SkColorSetARGB(255, 240, 240, 255));
|
||||
|
||||
SkFont font;
|
||||
font.setSize(48);
|
||||
|
||||
const char *text = "Hello, world!";
|
||||
SkRect bounds;
|
||||
font.measureText(text, strlen(text), SkTextEncoding::kUTF8, &bounds);
|
||||
|
||||
float x = (win_w - bounds.width()) / 2 - bounds.left();
|
||||
float y = (win_h + bounds.height()) / 2 - bounds.bottom();
|
||||
|
||||
c->drawSimpleText(text, strlen(text), SkTextEncoding::kUTF8, x, y, font,
|
||||
paint);
|
||||
|
||||
sk_surface->flushAndSubmit();
|
||||
eglSwapBuffers(edpy, esurf);
|
||||
}
|
||||
|
||||
void run() {
|
||||
while (running) {
|
||||
display.dispatch_pending();
|
||||
display.flush();
|
||||
draw_frame();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(16));
|
||||
}
|
||||
}
|
||||
|
||||
void cleanup() {
|
||||
gr_ctx->flushAndSubmit();
|
||||
gr_ctx->releaseResourcesAndAbandonContext();
|
||||
sk_surface.reset();
|
||||
gl_iface.reset();
|
||||
eglMakeCurrent(edpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
eglDestroySurface(edpy, esurf);
|
||||
wl_egl_window_destroy(wegl);
|
||||
eglDestroyContext(edpy, ectx);
|
||||
eglTerminate(edpy);
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
App a;
|
||||
a.init_wayland();
|
||||
a.init_egl();
|
||||
a.init_skia();
|
||||
a.run();
|
||||
a.cleanup();
|
||||
return 0;
|
||||
}
|
||||
35
src/Theme.hpp
Normal file
35
src/Theme.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <raylib.h>
|
||||
|
||||
#include "enum_array.hpp"
|
||||
|
||||
struct ColorScheme {
|
||||
struct {
|
||||
Color background;
|
||||
} window;
|
||||
};
|
||||
|
||||
enum class Theme : int { Light = 0, Dark, _last = Dark };
|
||||
|
||||
template <> struct enum_traits<Theme> {
|
||||
static constexpr Theme first = Theme::Light;
|
||||
static constexpr Theme last = Theme::_last;
|
||||
};
|
||||
|
||||
constexpr auto make_default_themes() -> enum_array<Theme, ColorScheme> const {
|
||||
enum_array<Theme, ColorScheme> array;
|
||||
array[Theme::Light] = {
|
||||
.window =
|
||||
{
|
||||
.background = {255, 255, 255, 100},
|
||||
},
|
||||
};
|
||||
array[Theme::Dark] = {
|
||||
.window =
|
||||
{
|
||||
.background = {0, 0, 0, 100},
|
||||
},
|
||||
};
|
||||
return array;
|
||||
}
|
||||
73
src/enum_array.hpp
Normal file
73
src/enum_array.hpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
template <class E> struct enum_traits;
|
||||
|
||||
template <class E>
|
||||
concept EnumLike = std::is_enum_v<E>;
|
||||
|
||||
template <EnumLike E>
|
||||
constexpr std::size_t enum_count_v =
|
||||
static_cast<std::size_t>(enum_traits<E>::last) -
|
||||
static_cast<std::size_t>(enum_traits<E>::first) + 1;
|
||||
|
||||
template <EnumLike E, class T> struct enum_array {
|
||||
using value_type = T;
|
||||
using enum_type = E;
|
||||
using underlying_index_type = std::size_t;
|
||||
|
||||
static constexpr E first = enum_traits<E>::first;
|
||||
static constexpr E last = enum_traits<E>::last;
|
||||
static constexpr std::size_t size_value = enum_count_v<E>;
|
||||
|
||||
std::array<T, size_value> _data{};
|
||||
|
||||
static constexpr std::size_t size() noexcept { return size_value; }
|
||||
constexpr T *data() noexcept { return _data.data(); }
|
||||
constexpr const T *data() const noexcept { return _data.data(); }
|
||||
constexpr T *begin() noexcept { return _data.begin().operator->(); }
|
||||
constexpr const T *begin() const noexcept {
|
||||
return _data.begin().operator->();
|
||||
}
|
||||
constexpr T *end() noexcept { return _data.end().operator->(); }
|
||||
constexpr const T *end() const noexcept { return _data.end().operator->(); }
|
||||
|
||||
constexpr T &operator[](E e) noexcept { return _data[to_index(e)]; }
|
||||
constexpr const T &operator[](E e) const noexcept {
|
||||
return _data[to_index(e)];
|
||||
}
|
||||
|
||||
constexpr T &at(E e) {
|
||||
auto i = to_index(e);
|
||||
if (i >= size_value)
|
||||
throw std::out_of_range("enum_array::at");
|
||||
return _data[i];
|
||||
}
|
||||
constexpr const T &at(E e) const {
|
||||
auto i = to_index(e);
|
||||
if (i >= size_value)
|
||||
throw std::out_of_range("enum_array::at");
|
||||
return _data[i];
|
||||
}
|
||||
|
||||
constexpr void fill(const T &v) { _data.fill(v); }
|
||||
|
||||
private:
|
||||
static constexpr std::size_t to_index(E e) noexcept {
|
||||
return static_cast<std::size_t>(e) - static_cast<std::size_t>(first);
|
||||
}
|
||||
};
|
||||
|
||||
template <class E, class T, class... U>
|
||||
requires EnumLike<E> && (std::is_same_v<T, U> && ...)
|
||||
constexpr auto make_enum_array(T &&first_val, U &&...rest) {
|
||||
enum_array<E, std::decay_t<T>> arr;
|
||||
static_assert(sizeof...(rest) + 1 == enum_count_v<E>,
|
||||
"initializer count must match enum range");
|
||||
arr._data = {std::forward<T>(first_val), std::forward<U>(rest)...};
|
||||
return arr;
|
||||
}
|
||||
432
src/main.cpp
Normal file
432
src/main.cpp
Normal file
@@ -0,0 +1,432 @@
|
||||
#include <cassert>
|
||||
#include <csignal>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <poll.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/signalfd.h>
|
||||
#include <thread>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <GLES3/gl3.h>
|
||||
#include <glib.h>
|
||||
#include <libportal/portal.h>
|
||||
#include <raylib.h>
|
||||
#include <rlgl.h>
|
||||
#include <wayland-egl.h>
|
||||
|
||||
#define namespace namespace_
|
||||
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||
extern "C" {
|
||||
#include <libportal/settings.h>
|
||||
}
|
||||
#undef namespace
|
||||
|
||||
#include "blur-client-protocol.h"
|
||||
#include "ext-background-effect-v1-client-protocol.h"
|
||||
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "Theme.hpp"
|
||||
|
||||
struct App {
|
||||
App() {
|
||||
init_wayland();
|
||||
init_egl();
|
||||
init_signal();
|
||||
init_theme_portal();
|
||||
}
|
||||
~App();
|
||||
|
||||
auto run() -> void {
|
||||
SetWindowSize(m_win_w, m_win_h);
|
||||
while (m_running) {
|
||||
pump_events();
|
||||
render_frame();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(16));
|
||||
}
|
||||
}
|
||||
auto set_visible(bool visible) -> void;
|
||||
auto visible() const -> bool { return m_visible; }
|
||||
|
||||
private:
|
||||
auto init_wayland() -> void;
|
||||
auto init_egl() -> void;
|
||||
auto init_signal() -> void;
|
||||
auto init_theme_portal() -> void;
|
||||
auto pump_events() -> void;
|
||||
auto render_frame() -> void;
|
||||
auto theme() const -> ColorScheme const & { return m_themes[m_active_theme]; }
|
||||
|
||||
static void on_settings_changed(XdpSettings * /*self*/, const char *ns,
|
||||
const char *key, GVariant * /*value*/,
|
||||
gpointer data);
|
||||
|
||||
struct {
|
||||
wl_display *display{};
|
||||
wl_registry *registry{};
|
||||
wl_compositor *compositor{};
|
||||
wl_surface *wl_surface{};
|
||||
zwlr_layer_shell_v1 *layer_shell{};
|
||||
zwlr_layer_surface_v1 *layer_surface{};
|
||||
ext_background_effect_manager_v1 *mgr{};
|
||||
ext_background_effect_surface_v1 *eff{};
|
||||
org_kde_kwin_blur_manager *kde_blur_mgr{};
|
||||
org_kde_kwin_blur *kde_blur{};
|
||||
} m_wayland;
|
||||
|
||||
struct {
|
||||
EGLDisplay edpy{EGL_NO_DISPLAY};
|
||||
EGLConfig ecfg{};
|
||||
EGLContext ectx{EGL_NO_CONTEXT};
|
||||
EGLSurface esurf{EGL_NO_SURFACE};
|
||||
wl_egl_window *wegl{};
|
||||
} m_gl;
|
||||
|
||||
struct {
|
||||
XdpPortal *portal{};
|
||||
XdpSettings *settings{};
|
||||
} m_xdp;
|
||||
|
||||
enum_array<Theme, ColorScheme> m_themes{make_default_themes()};
|
||||
Theme m_active_theme{Theme::Light};
|
||||
|
||||
int m_win_w{800};
|
||||
int m_win_h{600};
|
||||
bool m_running{true};
|
||||
bool m_visible{true};
|
||||
|
||||
int m_sfd{-1}; // Signal fd for toggling visibility
|
||||
};
|
||||
|
||||
App::~App() {
|
||||
if (m_sfd != -1)
|
||||
close(m_sfd);
|
||||
|
||||
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)
|
||||
wl_registry_destroy(m_wayland.registry);
|
||||
if (m_wayland.display)
|
||||
wl_display_disconnect(m_wayland.display);
|
||||
|
||||
if (m_xdp.settings)
|
||||
g_object_unref(m_xdp.settings);
|
||||
if (m_xdp.portal)
|
||||
g_object_unref(m_xdp.portal);
|
||||
}
|
||||
|
||||
auto App::init_wayland() -> void {
|
||||
m_wayland.display = wl_display_connect(nullptr);
|
||||
if (!m_wayland.display) {
|
||||
std::fprintf(stderr, "failed to connect to Wayland display\n");
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
auto handle_registry_global = [](void *data, wl_registry *registry,
|
||||
uint32_t name, const char *interface,
|
||||
uint32_t version) -> void {
|
||||
auto *app = static_cast<App *>(data);
|
||||
if (std::strcmp(interface, wl_compositor_interface.name) == 0) {
|
||||
app->m_wayland.compositor = static_cast<wl_compositor *>(
|
||||
wl_registry_bind(registry, name, &wl_compositor_interface, 4));
|
||||
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
|
||||
app->m_wayland.layer_shell = (zwlr_layer_shell_v1 *)wl_registry_bind(
|
||||
registry, name, &zwlr_layer_shell_v1_interface,
|
||||
version >= 4 ? 4 : version);
|
||||
} else if (strcmp(interface,
|
||||
ext_background_effect_manager_v1_interface.name) == 0) {
|
||||
app->m_wayland.mgr = (ext_background_effect_manager_v1 *)wl_registry_bind(
|
||||
registry, name, &ext_background_effect_manager_v1_interface, 1);
|
||||
} else if (strcmp(interface, "org_kde_kwin_blur_manager") == 0) {
|
||||
app->m_wayland.kde_blur_mgr =
|
||||
(org_kde_kwin_blur_manager *)wl_registry_bind(
|
||||
registry, name, &org_kde_kwin_blur_manager_interface, 1);
|
||||
}
|
||||
};
|
||||
|
||||
static wl_registry_listener const registry_listener{
|
||||
.global = handle_registry_global,
|
||||
.global_remove = [](void *, wl_registry *, uint32_t) {},
|
||||
};
|
||||
|
||||
m_wayland.registry = wl_display_get_registry(m_wayland.display);
|
||||
wl_registry_add_listener(m_wayland.registry, ®istry_listener, this);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
auto App::set_visible(bool visible) -> void {
|
||||
if (!m_wayland.layer_surface)
|
||||
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);
|
||||
} else {
|
||||
wl_surface_attach(m_wayland.wl_surface, nullptr, 0, 0);
|
||||
wl_surface_commit(m_wayland.wl_surface);
|
||||
}
|
||||
wl_display_flush(m_wayland.display);
|
||||
|
||||
m_visible = visible;
|
||||
}
|
||||
|
||||
auto App::init_egl() -> void {
|
||||
m_gl.edpy =
|
||||
eglGetDisplay(reinterpret_cast<EGLNativeDisplayType>(m_wayland.display));
|
||||
eglInitialize(m_gl.edpy, nullptr, nullptr);
|
||||
|
||||
const EGLint cfgAttribs[]{
|
||||
EGL_SURFACE_TYPE,
|
||||
EGL_WINDOW_BIT,
|
||||
EGL_RENDERABLE_TYPE,
|
||||
EGL_OPENGL_ES2_BIT,
|
||||
EGL_RED_SIZE,
|
||||
8,
|
||||
EGL_GREEN_SIZE,
|
||||
8,
|
||||
EGL_BLUE_SIZE,
|
||||
8,
|
||||
EGL_ALPHA_SIZE,
|
||||
8,
|
||||
EGL_NONE,
|
||||
};
|
||||
EGLint n = 0;
|
||||
eglChooseConfig(m_gl.edpy, cfgAttribs, &m_gl.ecfg, 1, &n);
|
||||
|
||||
const EGLint ctxAttribs[] = {EGL_CONTEXT_MAJOR_VERSION, 2, EGL_NONE};
|
||||
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);
|
||||
|
||||
InitWindow(m_win_w, m_win_h, "");
|
||||
}
|
||||
|
||||
auto App::init_signal() -> void {
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGUSR1);
|
||||
|
||||
if (pthread_sigmask(SIG_BLOCK, &mask, nullptr) != 0) {
|
||||
std::perror("pthread_sigmask");
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
m_sfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
|
||||
if (m_sfd == -1) {
|
||||
std::perror("signalfd");
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void App::on_settings_changed(XdpSettings * /*self*/, const char *ns,
|
||||
const char *key, GVariant * /*value*/,
|
||||
gpointer data) {
|
||||
auto *app = static_cast<App *>(data);
|
||||
if (g_strcmp0(ns, "org.freedesktop.appearance") == 0 &&
|
||||
g_strcmp0(key, "color-scheme") == 0) {
|
||||
guint v = xdp_settings_read_uint(app->m_xdp.settings,
|
||||
"org.freedesktop.appearance",
|
||||
"color-scheme", NULL, NULL);
|
||||
|
||||
if (v == 1)
|
||||
app->m_active_theme = Theme::Dark;
|
||||
else
|
||||
app->m_active_theme = Theme::Light;
|
||||
}
|
||||
}
|
||||
|
||||
auto App::init_theme_portal() -> void {
|
||||
m_xdp.portal = xdp_portal_new();
|
||||
m_xdp.settings = xdp_portal_get_settings(m_xdp.portal);
|
||||
|
||||
guint v = xdp_settings_read_uint(m_xdp.settings, "org.freedesktop.appearance",
|
||||
"color-scheme", NULL, NULL);
|
||||
if (v == 1)
|
||||
m_active_theme = Theme::Dark;
|
||||
else
|
||||
m_active_theme = Theme::Light;
|
||||
|
||||
g_signal_connect(m_xdp.settings, "changed", G_CALLBACK(on_settings_changed),
|
||||
this);
|
||||
}
|
||||
|
||||
auto App::render_frame() -> void {
|
||||
glViewport(0, 0, m_win_w, m_win_h);
|
||||
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(theme().window.background);
|
||||
|
||||
DrawFPS(10, 10);
|
||||
|
||||
EndDrawing();
|
||||
|
||||
eglSwapBuffers(m_gl.edpy, m_gl.esurf);
|
||||
}
|
||||
|
||||
auto App::pump_events() -> void {
|
||||
while (g_main_context_iteration(nullptr, false))
|
||||
;
|
||||
|
||||
wl_display_dispatch_pending(m_wayland.display);
|
||||
wl_display_flush(m_wayland.display);
|
||||
|
||||
pollfd fds[2]{{wl_display_get_fd(m_wayland.display), POLLIN, 0},
|
||||
{m_sfd, POLLIN, 0}};
|
||||
|
||||
auto prepared = (wl_display_prepare_read(m_wayland.display) == 0);
|
||||
auto ret = poll(fds, 2, 0);
|
||||
|
||||
// Wayland
|
||||
if (ret > 0 && (fds[0].revents & POLLIN)) {
|
||||
if (prepared) {
|
||||
wl_display_read_events(m_wayland.display);
|
||||
prepared = false;
|
||||
}
|
||||
} else if (prepared) {
|
||||
wl_display_cancel_read(m_wayland.display);
|
||||
}
|
||||
|
||||
// Signals
|
||||
if (ret > 0 && (fds[1].revents & POLLIN)) {
|
||||
signalfd_siginfo si;
|
||||
while (read(m_sfd, &si, sizeof(si)) == sizeof(si)) {
|
||||
if (si.ssi_signo == SIGUSR1) {
|
||||
set_visible(!visible());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool check_or_signal_running() {
|
||||
const char *lock_path = "/tmp/waylight.lock";
|
||||
int fd = open(lock_path, O_CREAT | O_RDWR, 0666);
|
||||
if (fd == -1)
|
||||
return false;
|
||||
|
||||
if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
|
||||
FILE *f = fopen(lock_path, "r");
|
||||
if (f) {
|
||||
pid_t pid;
|
||||
if (fscanf(f, "%d", &pid) == 1)
|
||||
kill(pid, SIGUSR1);
|
||||
fclose(f);
|
||||
}
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
ftruncate(fd, 0);
|
||||
dprintf(fd, "%d\n", getpid());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto main() -> int {
|
||||
if (check_or_signal_running()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
App app;
|
||||
app.run();
|
||||
}
|
||||
Reference in New Issue
Block a user