@@ -1,7 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
project(waylight LANGUAGES C CXX)
|
project(waylight LANGUAGES C CXX)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@ pkg_check_modules(EGL REQUIRED IMPORTED_TARGET egl)
|
|||||||
pkg_check_modules(GLES2 REQUIRED IMPORTED_TARGET glesv2)
|
pkg_check_modules(GLES2 REQUIRED IMPORTED_TARGET glesv2)
|
||||||
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
|
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
|
||||||
pkg_check_modules(LIBPORTAL REQUIRED IMPORTED_TARGET libportal)
|
pkg_check_modules(LIBPORTAL REQUIRED IMPORTED_TARGET libportal)
|
||||||
|
pkg_check_modules(XKBCOMMON REQUIRED IMPORTED_TARGET xkbcommon)
|
||||||
pkg_check_modules(WAYLAND_PROTOCOLS REQUIRED wayland-protocols)
|
pkg_check_modules(WAYLAND_PROTOCOLS REQUIRED wayland-protocols)
|
||||||
pkg_check_modules(WLR_PROTOCOLS REQUIRED wlr-protocols)
|
pkg_check_modules(WLR_PROTOCOLS REQUIRED wlr-protocols)
|
||||||
|
|
||||||
@@ -130,6 +131,7 @@ target_link_libraries(waylight PRIVATE
|
|||||||
PkgConfig::GLES2
|
PkgConfig::GLES2
|
||||||
PkgConfig::GLIB
|
PkgConfig::GLIB
|
||||||
PkgConfig::LIBPORTAL
|
PkgConfig::LIBPORTAL
|
||||||
|
PkgConfig::XKBCOMMON
|
||||||
|
|
||||||
raylib
|
raylib
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
doxygen
|
doxygen
|
||||||
gtest
|
gtest
|
||||||
cppcheck
|
cppcheck
|
||||||
|
inotify-tools
|
||||||
|
|
||||||
pkg-config
|
pkg-config
|
||||||
wayland
|
wayland
|
||||||
@@ -42,6 +43,7 @@
|
|||||||
libGL
|
libGL
|
||||||
libportal
|
libportal
|
||||||
glib
|
glib
|
||||||
|
libxkbcommon
|
||||||
]
|
]
|
||||||
++ buildInputs
|
++ buildInputs
|
||||||
++ nativeBuildInputs
|
++ nativeBuildInputs
|
||||||
|
|||||||
36
src/common.hpp
Normal file
36
src/common.hpp
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
using u8 = std::uint8_t;
|
||||||
|
using i8 = std::int8_t;
|
||||||
|
using u16 = std::uint16_t;
|
||||||
|
using i16 = std::int16_t;
|
||||||
|
using u32 = std::uint32_t;
|
||||||
|
using i32 = std::int32_t;
|
||||||
|
using u64 = std::uint64_t;
|
||||||
|
using i64 = std::int64_t;
|
||||||
|
using usize = std::uintptr_t;
|
||||||
|
using isize = std::intptr_t;
|
||||||
|
|
||||||
|
inline auto rune_to_string(uint32_t cp) -> char const * {
|
||||||
|
static char utf8[5] = {0};
|
||||||
|
for (auto &c : utf8)
|
||||||
|
c = 0;
|
||||||
|
|
||||||
|
if (cp < 0x80) {
|
||||||
|
utf8[0] = cp;
|
||||||
|
} else if (cp < 0x800) {
|
||||||
|
utf8[0] = 0xC0 | (cp >> 6);
|
||||||
|
utf8[1] = 0x80 | (cp & 0x3F);
|
||||||
|
} else if (cp < 0x10000) {
|
||||||
|
utf8[0] = 0xE0 | (cp >> 12);
|
||||||
|
utf8[1] = 0x80 | ((cp >> 6) & 0x3F);
|
||||||
|
utf8[2] = 0x80 | (cp & 0x3F);
|
||||||
|
} else {
|
||||||
|
utf8[0] = 0xF0 | (cp >> 18);
|
||||||
|
utf8[1] = 0x80 | ((cp >> 12) & 0x3F);
|
||||||
|
utf8[2] = 0x80 | ((cp >> 6) & 0x3F);
|
||||||
|
utf8[3] = 0x80 | (cp & 0x3F);
|
||||||
|
}
|
||||||
|
|
||||||
|
return utf8;
|
||||||
|
}
|
||||||
224
src/main.cpp
224
src/main.cpp
@@ -5,9 +5,13 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
|
#include <print>
|
||||||
#include <sys/file.h>
|
#include <sys/file.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
#include <sys/signalfd.h>
|
#include <sys/signalfd.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <EGL/egl.h>
|
#include <EGL/egl.h>
|
||||||
#include <GLES3/gl3.h>
|
#include <GLES3/gl3.h>
|
||||||
@@ -16,6 +20,7 @@
|
|||||||
#include <raylib.h>
|
#include <raylib.h>
|
||||||
#include <rlgl.h>
|
#include <rlgl.h>
|
||||||
#include <wayland-egl.h>
|
#include <wayland-egl.h>
|
||||||
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
|
||||||
#define namespace namespace_
|
#define namespace namespace_
|
||||||
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||||
@@ -30,6 +35,32 @@ extern "C" {
|
|||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
|
|
||||||
#include "Theme.hpp"
|
#include "Theme.hpp"
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
|
struct TypingBuffer : std::pmr::vector<u32> {
|
||||||
|
void push_utf8(const char *s) {
|
||||||
|
for (const unsigned char *p = (const unsigned char *)s; *p;) {
|
||||||
|
u32 cp = 0;
|
||||||
|
int len = 0;
|
||||||
|
if (*p < 0x80) {
|
||||||
|
cp = *p++;
|
||||||
|
len = 1;
|
||||||
|
} else if ((*p & 0xE0) == 0xC0) {
|
||||||
|
cp = *p++ & 0x1F;
|
||||||
|
len = 2;
|
||||||
|
} else if ((*p & 0xF0) == 0xE0) {
|
||||||
|
cp = *p++ & 0x0F;
|
||||||
|
len = 3;
|
||||||
|
} else {
|
||||||
|
cp = *p++ & 0x07;
|
||||||
|
len = 4;
|
||||||
|
}
|
||||||
|
for (int i = 1; i < len; i++)
|
||||||
|
cp = (cp << 6) | ((*p++) & 0x3F);
|
||||||
|
push_back(cp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct App {
|
struct App {
|
||||||
App() {
|
App() {
|
||||||
@@ -74,6 +105,8 @@ private:
|
|||||||
wl_display *display{};
|
wl_display *display{};
|
||||||
wl_registry *registry{};
|
wl_registry *registry{};
|
||||||
wl_compositor *compositor{};
|
wl_compositor *compositor{};
|
||||||
|
wl_seat *seat{};
|
||||||
|
wl_keyboard *kbd{};
|
||||||
wl_surface *wl_surface{};
|
wl_surface *wl_surface{};
|
||||||
zwlr_layer_shell_v1 *layer_shell{};
|
zwlr_layer_shell_v1 *layer_shell{};
|
||||||
zwlr_layer_surface_v1 *layer_surface{};
|
zwlr_layer_surface_v1 *layer_surface{};
|
||||||
@@ -96,6 +129,53 @@ private:
|
|||||||
XdpSettings *settings{};
|
XdpSettings *settings{};
|
||||||
} m_xdp;
|
} m_xdp;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
TypingBuffer typing{};
|
||||||
|
|
||||||
|
xkb_context *xkb_ctx{};
|
||||||
|
xkb_keymap *xkb_keymap{};
|
||||||
|
xkb_state *xkb_state{};
|
||||||
|
|
||||||
|
std::unordered_set<u32> held;
|
||||||
|
std::unordered_set<u32> pressed_syms;
|
||||||
|
std::unordered_set<u32> released_syms;
|
||||||
|
|
||||||
|
auto is_down_evdev(u32 evdev) const -> bool {
|
||||||
|
return held.find(evdev) != held.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto is_down_sym(xkb_keysym_t sym) const -> bool {
|
||||||
|
if (!xkb_state)
|
||||||
|
return false;
|
||||||
|
for (auto k : held) {
|
||||||
|
if (xkb_state_key_get_one_sym(xkb_state, (xkb_keycode_t)(k + 8)) == sym)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto is_sym_pressed(xkb_keysym_t sym) const -> bool {
|
||||||
|
return pressed_syms.find(sym) != pressed_syms.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto is_sym_released(xkb_keysym_t sym) const -> bool {
|
||||||
|
return released_syms.find(sym) != released_syms.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mod_active(const char *name) const -> bool {
|
||||||
|
return xkb_state && xkb_state_mod_name_is_active(
|
||||||
|
xkb_state, name, XKB_STATE_MODS_EFFECTIVE) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ctrl() const -> bool { return mod_active("Control"); }
|
||||||
|
auto shift() const -> bool { return mod_active("Shift"); }
|
||||||
|
|
||||||
|
void clear_transients() {
|
||||||
|
pressed_syms.clear();
|
||||||
|
released_syms.clear();
|
||||||
|
}
|
||||||
|
} m_kbd;
|
||||||
|
|
||||||
enum_array<Theme, ColorScheme> m_themes{make_default_themes()};
|
enum_array<Theme, ColorScheme> m_themes{make_default_themes()};
|
||||||
Theme m_active_theme{Theme::Light};
|
Theme m_active_theme{Theme::Light};
|
||||||
|
|
||||||
@@ -119,6 +199,16 @@ App::~App() {
|
|||||||
eglTerminate(m_gl.edpy);
|
eglTerminate(m_gl.edpy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_kbd.xkb_state)
|
||||||
|
xkb_state_unref(m_kbd.xkb_state);
|
||||||
|
if (m_kbd.xkb_keymap)
|
||||||
|
xkb_keymap_unref(m_kbd.xkb_keymap);
|
||||||
|
if (m_kbd.xkb_ctx)
|
||||||
|
xkb_context_unref(m_kbd.xkb_ctx);
|
||||||
|
if (m_wayland.kbd)
|
||||||
|
wl_keyboard_destroy(m_wayland.kbd);
|
||||||
|
if (m_wayland.seat)
|
||||||
|
wl_seat_destroy(m_wayland.seat);
|
||||||
if (m_wayland.compositor)
|
if (m_wayland.compositor)
|
||||||
wl_compositor_destroy(m_wayland.compositor);
|
wl_compositor_destroy(m_wayland.compositor);
|
||||||
if (m_wayland.registry)
|
if (m_wayland.registry)
|
||||||
@@ -139,13 +229,116 @@ auto App::init_wayland() -> void {
|
|||||||
std::exit(EXIT_FAILURE);
|
std::exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto handle_registry_global = [](void *data, wl_registry *registry,
|
static wl_keyboard_listener keyboard_listener{};
|
||||||
uint32_t name, const char *interface,
|
{
|
||||||
uint32_t version) -> void {
|
auto kb_keymap = [](void *data, wl_keyboard *, u32 format, i32 fd,
|
||||||
|
u32 size) -> void {
|
||||||
|
auto *app = (App *)data;
|
||||||
|
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
|
||||||
|
close(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
void *map = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||||
|
if (map == MAP_FAILED) {
|
||||||
|
close(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (app->m_kbd.xkb_keymap)
|
||||||
|
xkb_keymap_unref(app->m_kbd.xkb_keymap);
|
||||||
|
if (app->m_kbd.xkb_state) {
|
||||||
|
xkb_state_unref(app->m_kbd.xkb_state);
|
||||||
|
app->m_kbd.xkb_state = nullptr;
|
||||||
|
}
|
||||||
|
app->m_kbd.xkb_keymap = xkb_keymap_new_from_string(
|
||||||
|
app->m_kbd.xkb_ctx, (const char *)map, XKB_KEYMAP_FORMAT_TEXT_V1,
|
||||||
|
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||||
|
app->m_kbd.xkb_state = app->m_kbd.xkb_keymap
|
||||||
|
? xkb_state_new(app->m_kbd.xkb_keymap)
|
||||||
|
: nullptr;
|
||||||
|
munmap(map, size);
|
||||||
|
close(fd);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto kb_enter = [](void *, wl_keyboard *, u32, wl_surface *,
|
||||||
|
wl_array *) -> void {};
|
||||||
|
auto kb_leave = [](void *data, wl_keyboard *, u32, wl_surface *) -> void {
|
||||||
|
auto *app = (App *)data;
|
||||||
|
app->m_kbd.held.clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto kb_key = [](void *data, wl_keyboard *, u32, u32, u32 key,
|
||||||
|
u32 state) -> void {
|
||||||
|
auto *app = (App *)data;
|
||||||
|
if (!app->m_kbd.xkb_state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
xkb_keycode_t kc = key + 8;
|
||||||
|
|
||||||
|
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
|
||||||
|
app->m_kbd.held.insert(key);
|
||||||
|
|
||||||
|
xkb_state_update_key(app->m_kbd.xkb_state, kc, XKB_KEY_DOWN);
|
||||||
|
xkb_keysym_t sym = xkb_state_key_get_one_sym(app->m_kbd.xkb_state, kc);
|
||||||
|
app->m_kbd.pressed_syms.insert(sym);
|
||||||
|
|
||||||
|
bool ctrl = app->m_kbd.mod_active("Control");
|
||||||
|
bool alt = app->m_kbd.mod_active("Mod1");
|
||||||
|
bool meta = app->m_kbd.mod_active("Mod4");
|
||||||
|
if (!(ctrl || alt || meta)) {
|
||||||
|
u32 cp = xkb_keysym_to_utf32(sym);
|
||||||
|
if (cp >= 0x20) {
|
||||||
|
char buf[8];
|
||||||
|
int n = xkb_keysym_to_utf8(sym, buf, sizeof(buf));
|
||||||
|
if (n > 0)
|
||||||
|
app->m_kbd.typing.push_utf8(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
xkb_keysym_t sym = xkb_state_key_get_one_sym(app->m_kbd.xkb_state, kc);
|
||||||
|
app->m_kbd.released_syms.insert(sym);
|
||||||
|
|
||||||
|
app->m_kbd.held.erase(key);
|
||||||
|
xkb_state_update_key(app->m_kbd.xkb_state, kc, XKB_KEY_UP);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto kb_mods = [](void *data, wl_keyboard *, u32, u32 depressed,
|
||||||
|
u32 latched, u32 locked, u32 group) -> void {
|
||||||
|
auto *app = (App *)data;
|
||||||
|
if (!app->m_kbd.xkb_state)
|
||||||
|
return;
|
||||||
|
xkb_state_update_mask(app->m_kbd.xkb_state, depressed, latched, locked, 0,
|
||||||
|
0, group);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto kb_repeat_info = [](void *, wl_keyboard *, i32, i32) -> void {};
|
||||||
|
|
||||||
|
keyboard_listener = {kb_keymap, kb_enter, kb_leave,
|
||||||
|
kb_key, kb_mods, kb_repeat_info};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto handle_registry_global = [](void *data, wl_registry *registry, u32 name,
|
||||||
|
const char *interface, u32 version) -> void {
|
||||||
auto *app = static_cast<App *>(data);
|
auto *app = static_cast<App *>(data);
|
||||||
if (std::strcmp(interface, wl_compositor_interface.name) == 0) {
|
if (std::strcmp(interface, wl_compositor_interface.name) == 0) {
|
||||||
app->m_wayland.compositor = static_cast<wl_compositor *>(
|
app->m_wayland.compositor = static_cast<wl_compositor *>(
|
||||||
wl_registry_bind(registry, name, &wl_compositor_interface, 4));
|
wl_registry_bind(registry, name, &wl_compositor_interface, 4));
|
||||||
|
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
|
||||||
|
app->m_wayland.seat = static_cast<wl_seat *>(
|
||||||
|
wl_registry_bind(registry, name, &wl_seat_interface, 9));
|
||||||
|
static struct wl_seat_listener const seat_listener = {
|
||||||
|
.capabilities =
|
||||||
|
[](void *data, struct wl_seat *seat, u32 caps) {
|
||||||
|
auto *app = static_cast<App *>(data);
|
||||||
|
if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
|
||||||
|
app->m_wayland.kbd = wl_seat_get_keyboard(seat);
|
||||||
|
wl_keyboard_add_listener(app->m_wayland.kbd,
|
||||||
|
&keyboard_listener, data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.name = [](void *data, struct wl_seat *wl_seat, const char *name) {},
|
||||||
|
};
|
||||||
|
wl_seat_add_listener(app->m_wayland.seat, &seat_listener, data);
|
||||||
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
|
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
|
||||||
app->m_wayland.layer_shell = (zwlr_layer_shell_v1 *)wl_registry_bind(
|
app->m_wayland.layer_shell = (zwlr_layer_shell_v1 *)wl_registry_bind(
|
||||||
registry, name, &zwlr_layer_shell_v1_interface,
|
registry, name, &zwlr_layer_shell_v1_interface,
|
||||||
@@ -163,12 +356,14 @@ auto App::init_wayland() -> void {
|
|||||||
|
|
||||||
static wl_registry_listener const registry_listener{
|
static wl_registry_listener const registry_listener{
|
||||||
.global = handle_registry_global,
|
.global = handle_registry_global,
|
||||||
.global_remove = [](void *, wl_registry *, uint32_t) {},
|
.global_remove = [](void *, wl_registry *, u32) {},
|
||||||
};
|
};
|
||||||
|
|
||||||
m_wayland.registry = wl_display_get_registry(m_wayland.display);
|
m_wayland.registry = wl_display_get_registry(m_wayland.display);
|
||||||
wl_registry_add_listener(m_wayland.registry, ®istry_listener, this);
|
wl_registry_add_listener(m_wayland.registry, ®istry_listener, this);
|
||||||
|
|
||||||
|
m_kbd.xkb_ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
|
|
||||||
wl_display_roundtrip(m_wayland.display);
|
wl_display_roundtrip(m_wayland.display);
|
||||||
|
|
||||||
create_layer_surface();
|
create_layer_surface();
|
||||||
@@ -276,15 +471,29 @@ auto App::render_frame() -> void {
|
|||||||
|
|
||||||
glViewport(0, 0, m_win_w, m_win_h);
|
glViewport(0, 0, m_win_w, m_win_h);
|
||||||
|
|
||||||
|
for (auto const cp : m_kbd.typing) {
|
||||||
|
std::println("Char typed: {} ({}) shift={} ctrl={}", rune_to_string(cp),
|
||||||
|
cp, m_kbd.shift() ? 'y' : 'n', m_kbd.ctrl() ? 'y' : 'n');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_kbd.is_sym_pressed(XKB_KEY_Escape)) {
|
||||||
|
set_visible(!visible());
|
||||||
|
if (m_kbd.ctrl() && m_kbd.shift()) {
|
||||||
|
m_running = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BeginDrawing();
|
BeginDrawing();
|
||||||
|
|
||||||
ClearBackground(theme().window.background);
|
ClearBackground(BLANK);
|
||||||
|
|
||||||
DrawFPS(10, 10);
|
DrawFPS(10, 10);
|
||||||
|
|
||||||
EndDrawing();
|
EndDrawing();
|
||||||
|
|
||||||
eglSwapBuffers(m_gl.edpy, m_gl.esurf);
|
eglSwapBuffers(m_gl.edpy, m_gl.esurf);
|
||||||
|
m_kbd.typing.clear();
|
||||||
|
m_kbd.clear_transients();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto App::create_layer_surface() -> void {
|
auto App::create_layer_surface() -> void {
|
||||||
@@ -336,8 +545,7 @@ auto App::create_layer_surface() -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto handle_layer_configure = [](void *data, zwlr_layer_surface_v1 *ls,
|
auto handle_layer_configure = [](void *data, zwlr_layer_surface_v1 *ls,
|
||||||
uint32_t serial, uint32_t w,
|
u32 serial, u32 w, u32 h) -> void {
|
||||||
uint32_t h) -> void {
|
|
||||||
auto *app = static_cast<App *>(data);
|
auto *app = static_cast<App *>(data);
|
||||||
if (w)
|
if (w)
|
||||||
app->m_win_w = static_cast<int>(w);
|
app->m_win_w = static_cast<int>(w);
|
||||||
@@ -459,7 +667,7 @@ auto App::update_blur_region() -> void {
|
|||||||
if (!region)
|
if (!region)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
wl_region_add(region, 0, 0, m_win_w, m_win_h);
|
wl_region_add(region, 0, 0, m_win_w - 50, m_win_h);
|
||||||
|
|
||||||
if (m_wayland.eff)
|
if (m_wayland.eff)
|
||||||
ext_background_effect_surface_v1_set_blur_region(m_wayland.eff, region);
|
ext_background_effect_surface_v1_set_blur_region(m_wayland.eff, region);
|
||||||
|
|||||||
6
tools/continuous_build.sh
Executable file
6
tools/continuous_build.sh
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
while inotifywait -r -e modify,create,delete src; do
|
||||||
|
cmake --build build
|
||||||
|
done
|
||||||
|
|
||||||
Reference in New Issue
Block a user