mirror of
https://github.com/hyprwm/Hyprland
synced 2026-05-11 07:18:09 +02:00
Merge branch 'main' into background-scale
This commit is contained in:
commit
6b3aafe46e
45 changed files with 800 additions and 191 deletions
|
|
@ -104,6 +104,8 @@ add_compile_options(
|
|||
-Wall
|
||||
-Wextra
|
||||
-Wpedantic
|
||||
-Wno-keyword-macro
|
||||
-Wno-unused-result
|
||||
-Wno-unused-parameter
|
||||
-Wno-unused-value
|
||||
-Wno-missing-field-initializers
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@
|
|||
#include <src/includes.hpp>
|
||||
#include <sstream>
|
||||
#include <any>
|
||||
#include <cmath>
|
||||
|
||||
#define private public
|
||||
#include <src/managers/input/InputManager.hpp>
|
||||
#include <src/managers/PointerManager.hpp>
|
||||
#include <src/managers/input/trackpad/TrackpadGestures.hpp>
|
||||
#include <src/helpers/Monitor.hpp>
|
||||
#include <src/desktop/rule/windowRule/WindowRuleEffectContainer.hpp>
|
||||
#include <src/desktop/rule/layerRule/LayerRuleEffectContainer.hpp>
|
||||
#include <src/desktop/rule/windowRule/WindowRuleApplicator.hpp>
|
||||
|
|
@ -17,6 +19,7 @@
|
|||
#undef private
|
||||
|
||||
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||
#include <hyprutils/string/Numeric.hpp>
|
||||
#include <hyprutils/string/VarList.hpp>
|
||||
using namespace Hyprutils::Utils;
|
||||
using namespace Hyprutils::String;
|
||||
|
|
@ -156,6 +159,96 @@ static SDispatchResult simulateGesture(std::string in) {
|
|||
return {.success = true};
|
||||
}
|
||||
|
||||
static SDispatchResult pinchUpdate(std::string in) {
|
||||
CVarList data(in);
|
||||
uint32_t fingers = 2;
|
||||
double scale = 1.0;
|
||||
Vector2D delta = {};
|
||||
double rotation{};
|
||||
|
||||
if (data.size() < 2)
|
||||
return {.success = false, .error = "invalid input"};
|
||||
|
||||
if (const auto n = strToNumber<uint32_t>(data[0]); n)
|
||||
fingers = n.value();
|
||||
else
|
||||
return {.success = false, .error = "invalid input"};
|
||||
|
||||
if (const auto n = strToNumber<double>(data[1]); n)
|
||||
scale = n.value();
|
||||
else
|
||||
return {.success = false, .error = "invalid input"};
|
||||
|
||||
if (data.size() > 2) {
|
||||
if (const auto n = strToNumber<double>(data[2]); n)
|
||||
delta.x = n.value();
|
||||
else
|
||||
return {.success = false, .error = "invalid input"};
|
||||
}
|
||||
|
||||
if (data.size() > 3) {
|
||||
if (const auto n = strToNumber<double>(data[3]); n)
|
||||
delta.y = n.value();
|
||||
else
|
||||
return {.success = false, .error = "invalid input"};
|
||||
}
|
||||
|
||||
if (data.size() > 4) {
|
||||
if (const auto n = strToNumber<double>(data[4]); n)
|
||||
rotation = n.value();
|
||||
else
|
||||
return {.success = false, .error = "invalid input"};
|
||||
}
|
||||
|
||||
g_pTrackpadGestures->gestureUpdate(IPointer::SPinchUpdateEvent{
|
||||
.fingers = fingers,
|
||||
.delta = delta,
|
||||
.scale = scale,
|
||||
.rotation = rotation,
|
||||
});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static SDispatchResult pinchEnd(std::string in) {
|
||||
g_pTrackpadGestures->gestureEnd(IPointer::SPinchEndEvent{});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static SDispatchResult expectCursorZoom(std::string in) {
|
||||
CVarList data(in);
|
||||
float expected = 1.F;
|
||||
float delta = 0.01F;
|
||||
|
||||
if (data.size() < 1)
|
||||
return {.success = false, .error = "invalid input"};
|
||||
|
||||
if (const auto n = strToNumber<float>(data[0]); n)
|
||||
expected = n.value();
|
||||
else
|
||||
return {.success = false, .error = "invalid input"};
|
||||
|
||||
if (data.size() > 1) {
|
||||
if (const auto n = strToNumber<float>(data[1]); n)
|
||||
delta = n.value();
|
||||
else
|
||||
return {.success = false, .error = "invalid input"};
|
||||
}
|
||||
|
||||
const auto PMONITOR = g_pCompositor->getMonitorFromVector(g_pInputManager->getMouseCoordsInternal());
|
||||
|
||||
if (!PMONITOR)
|
||||
return {.success = false, .error = "No monitor under cursor"};
|
||||
|
||||
const auto actual = PMONITOR->m_cursorZoom->value();
|
||||
|
||||
if (std::abs(actual - expected) > delta)
|
||||
return {.success = false, .error = std::format("Expected cursor zoom {} ± {}, got {}", expected, delta, actual)};
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static SDispatchResult vkb(std::string in) {
|
||||
auto tkb0 = CTestKeyboard::create(false);
|
||||
auto tkb1 = CTestKeyboard::create(false);
|
||||
|
|
@ -375,6 +468,32 @@ static int luaGesture(lua_State* L) {
|
|||
return luaResult(L, ::simulateGesture(std::format("{},{}", direction, fingers)));
|
||||
}
|
||||
|
||||
static int luaPinchUpdate(lua_State* L) {
|
||||
std::string in = std::format("{},{}", (int)luaL_checkinteger(L, 1), (double)luaL_checknumber(L, 2));
|
||||
|
||||
if (lua_gettop(L) > 2)
|
||||
in += std::format(",{}", (double)luaL_checknumber(L, 3));
|
||||
if (lua_gettop(L) > 3)
|
||||
in += std::format(",{}", (double)luaL_checknumber(L, 4));
|
||||
if (lua_gettop(L) > 4)
|
||||
in += std::format(",{}", (double)luaL_checknumber(L, 5));
|
||||
|
||||
return luaResult(L, ::pinchUpdate(in));
|
||||
}
|
||||
|
||||
static int luaPinchEnd(lua_State* L) {
|
||||
return luaResult(L, ::pinchEnd(""));
|
||||
}
|
||||
|
||||
static int luaExpectCursorZoom(lua_State* L) {
|
||||
const auto expected = (double)luaL_checknumber(L, 1);
|
||||
|
||||
if (lua_gettop(L) > 1)
|
||||
return luaResult(L, ::expectCursorZoom(std::format("{},{}", expected, (double)luaL_checknumber(L, 2))));
|
||||
|
||||
return luaResult(L, ::expectCursorZoom(std::format("{}", expected)));
|
||||
}
|
||||
|
||||
static int luaScroll(lua_State* L) {
|
||||
return luaResult(L, ::scroll(std::to_string((double)luaL_checknumber(L, 1))));
|
||||
}
|
||||
|
|
@ -425,6 +544,9 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
|||
addLuaFn("vkb", ::luaVkb);
|
||||
addLuaFn("alt", ::luaAlt);
|
||||
addLuaFn("gesture", ::luaGesture);
|
||||
addLuaFn("pinch_update", ::luaPinchUpdate);
|
||||
addLuaFn("pinch_end", ::luaPinchEnd);
|
||||
addLuaFn("expect_cursor_zoom", ::luaExpectCursorZoom);
|
||||
addLuaFn("scroll", ::luaScroll);
|
||||
addLuaFn("click", ::luaClick);
|
||||
addLuaFn("keybind", ::luaKeybind);
|
||||
|
|
|
|||
|
|
@ -126,6 +126,9 @@ static bool sendScroll(int delta) {
|
|||
}
|
||||
|
||||
TEST_CASE(pointerScroll) {
|
||||
NLog::log("{}Skipping pointerScroll test (unstable in CI / headless environments)", Colors::YELLOW);
|
||||
return;
|
||||
|
||||
std::optional<CClient> client;
|
||||
try {
|
||||
client.emplace();
|
||||
|
|
|
|||
|
|
@ -151,8 +151,10 @@ static bool isCursorPos(int x, int y) {
|
|||
}
|
||||
|
||||
TEST_CASE(pointerWarp) {
|
||||
std::optional<CClient> client;
|
||||
NLog::log("{}Skipping pointerWarp test (unstable in CI / headless environments)", Colors::YELLOW);
|
||||
return;
|
||||
|
||||
std::optional<CClient> client;
|
||||
try {
|
||||
client.emplace();
|
||||
} catch (...) { FAIL_TEST("Couldn't start the client"); }
|
||||
|
|
|
|||
|
|
@ -6,12 +6,14 @@
|
|||
#include <chrono>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <hyprutils/string/Numeric.hpp>
|
||||
#include <csignal>
|
||||
#include <cerrno>
|
||||
#include "../shared.hpp"
|
||||
|
||||
using namespace Hyprutils::OS;
|
||||
using namespace Hyprutils::Memory;
|
||||
using namespace Hyprutils::String;
|
||||
|
||||
#define UP CUniquePointer
|
||||
#define SP CSharedPointer
|
||||
|
|
@ -178,4 +180,40 @@ TEST_CASE(gestures) {
|
|||
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({ workspace = '1' })"));
|
||||
}
|
||||
const std::string cursorPosBeforePinch = getFromSocket("/cursorpos");
|
||||
|
||||
OK(getFromSocket("/dispatch hl.dsp.cursor.move({ x = 500, y = 500 })"));
|
||||
OK(getFromSocket("/eval hl.config({ cursor = { zoom_factor = 1 } })"));
|
||||
OK(getFromSocket("/eval hl.plugin.test.expect_cursor_zoom(1, 0.01)"));
|
||||
|
||||
OK(getFromSocket("/eval hl.plugin.test.pinch_update(2, 1.2)"));
|
||||
OK(getFromSocket("/eval hl.plugin.test.expect_cursor_zoom(1.2, 0.01)"));
|
||||
OK(getFromSocket("/eval hl.plugin.test.pinch_update(2, 1.6)"));
|
||||
OK(getFromSocket("/eval hl.plugin.test.expect_cursor_zoom(1.6, 0.01)"));
|
||||
OK(getFromSocket("/eval hl.plugin.test.pinch_end()"));
|
||||
OK(getFromSocket("/eval hl.plugin.test.expect_cursor_zoom(1.6, 0.01)"));
|
||||
|
||||
OK(getFromSocket("/eval hl.plugin.test.pinch_update(2, 0.64)"));
|
||||
OK(getFromSocket("/eval hl.plugin.test.expect_cursor_zoom(1, 0.01)"));
|
||||
OK(getFromSocket("/eval hl.plugin.test.pinch_end()"));
|
||||
OK(getFromSocket("/eval hl.plugin.test.expect_cursor_zoom(1, 0.01)"));
|
||||
|
||||
const auto comma = cursorPosBeforePinch.find(',');
|
||||
|
||||
if (comma != std::string::npos) {
|
||||
auto xSv = std::string_view(cursorPosBeforePinch).substr(0, comma);
|
||||
auto ySv = std::string_view(cursorPosBeforePinch).substr(comma + 1);
|
||||
while (!xSv.empty() && xSv.front() == ' ')
|
||||
xSv.remove_prefix(1);
|
||||
while (!ySv.empty() && ySv.front() == ' ')
|
||||
ySv.remove_prefix(1);
|
||||
|
||||
const auto x = strToNumber<int>(xSv);
|
||||
const auto y = strToNumber<int>(ySv);
|
||||
|
||||
if (!x || !y)
|
||||
FAIL_TEST("Failed to restore cursor pos");
|
||||
|
||||
OK(getFromSocket(std::format("/dispatch hl.dsp.cursor.move({{ x = {}, y = {} }})", x.value(), y.value())));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -192,6 +192,7 @@ TEST_CASE(groups) {
|
|||
|
||||
NLog::log("{}Disable autogrouping", Colors::YELLOW);
|
||||
OK(getFromSocket("/eval hl.config({ group = { auto_group = false } })"));
|
||||
OK(getFromSocket("/eval hl.config({ dwindle = { force_split = 2 } })"));
|
||||
|
||||
NLog::log("{}Spawn kittyProcC", Colors::YELLOW);
|
||||
auto kittyProcC = Tests::spawnKitty();
|
||||
|
|
@ -206,6 +207,7 @@ TEST_CASE(groups) {
|
|||
EXPECT_COUNT_STRING(str, "at: 22,22", 2);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/eval hl.config({ dwindle = { force_split = 0 } })"));
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({ direction = 'left' })"));
|
||||
OK(getFromSocket("/dispatch hl.dsp.group.active({ index = 1 })"));
|
||||
OK(getFromSocket("/eval hl.config({ group = { auto_group = true } })"));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "../shared.hpp"
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include <algorithm>
|
||||
#include "tests.hpp"
|
||||
|
||||
TEST_CASE(focusMasterPrevious) {
|
||||
|
|
@ -141,3 +142,71 @@ TEST_CASE(fsBehavior) {
|
|||
EXPECT_CONTAINS(str, "fullscreen: 0");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(rollFocus) {
|
||||
// test rollnext/rollprev dispatchers
|
||||
|
||||
OK(getFromSocket("r/eval hl.config({ general = { layout = 'master' } })"));
|
||||
|
||||
// set up windows
|
||||
std::vector<std::string> windows = {"slave1", "slave2", "slave3", "master"};
|
||||
|
||||
// helper lambda thing
|
||||
auto roll = [&](const std::string& dir) {
|
||||
auto pivot = (dir == "rollnext") ? windows.begin() + 1 : windows.end() - 1;
|
||||
|
||||
// rotate the windows vector along with the actual windows
|
||||
// the rolling behavior of the window focus should follow the
|
||||
// rotating behavior of std::ranges::rotate
|
||||
OK(getFromSocket("/dispatch hl.dsp.layout('" + dir + "')"));
|
||||
std::ranges::rotate(windows.begin(), pivot, windows.end());
|
||||
ASSERT_CONTAINS(getFromSocket("/activewindow"), "class: " + windows.back());
|
||||
};
|
||||
|
||||
for (auto const& win : windows) {
|
||||
if (!Tests::spawnKitty(win)) {
|
||||
FAIL_TEST("Could not spawn kitty with win class `{}`", win);
|
||||
}
|
||||
}
|
||||
|
||||
// focus master
|
||||
OK(getFromSocket("/dispatch hl.dsp.layout('focusmaster master')"));
|
||||
ASSERT_CONTAINS(getFromSocket("/activewindow"), "class: master");
|
||||
|
||||
// put the windows in the washing machine
|
||||
NLog::log("{}Testing rollnext", Colors::YELLOW);
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
roll("rollnext");
|
||||
}
|
||||
|
||||
NLog::log("{}Testing rollprev", Colors::YELLOW);
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
roll("rollprev");
|
||||
}
|
||||
|
||||
NLog::log("{}Testing rollnext with rollprev", Colors::YELLOW);
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
for (int j = 0; j < 5; ++j) {
|
||||
roll("rollnext");
|
||||
}
|
||||
roll("rollprev");
|
||||
}
|
||||
|
||||
NLog::log("{}Testing rollnext/rollprev alternation", Colors::YELLOW);
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
if (i % 2 == 0) {
|
||||
roll("rollnext");
|
||||
} else {
|
||||
roll("rollprev");
|
||||
}
|
||||
}
|
||||
|
||||
NLog::log("{}Testing rollnext/rollprev burst calls", Colors::YELLOW);
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
if (i / 5 % 2 == 0) {
|
||||
roll("rollnext");
|
||||
} else {
|
||||
roll("rollprev");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -290,5 +290,6 @@ hl.gesture({ fingers = 5, direction = "left", action = function() hl.dispatch(hl
|
|||
hl.gesture({ fingers = 5, direction = "right", action = function() hl.dispatch(hl.dsp.send_shortcut({ mods = "", key = "t", window = "activewindow" })) end })
|
||||
hl.gesture({ fingers = 4, direction = "right", action = function() hl.dispatch(hl.dsp.send_shortcut({ mods = "", key = "return", window = "activewindow" })) end })
|
||||
hl.gesture({ fingers = 4, direction = "left", action = function() hl.dispatch(hl.dsp.cursor.move_to_corner({ corner = 1, window = "activewindow" })) end })
|
||||
hl.gesture({ fingers = 2, direction = "pinch", action = "cursorZoom", zoom_level = "1", mode = "live" })
|
||||
|
||||
hl.gesture({ fingers = 2, direction = "right", action = "float", disable_inhibit = true })
|
||||
|
|
|
|||
|
|
@ -469,7 +469,8 @@ def generate_stub(root: Path) -> str:
|
|||
|
||||
api_signatures: dict[str, str] = {
|
||||
"hl.on": "fun(event: HL.EventName, cb: fun(...)): HL.EventSubscription",
|
||||
"hl.bind": "fun(keys: string, dispatcher: function, opts?: HL.BindOptions): HL.Keybind",
|
||||
"hl.bind": "fun(keys: string, dispatcher: HL.Dispatcher|function, opts?: HL.BindOptions): HL.Keybind",
|
||||
"hl.dispatch": "fun(dispatcher: HL.Dispatcher|function): any",
|
||||
"hl.define_submap": "fun(name: string, reset_or_fn: string|function, fn?: function): nil",
|
||||
"hl.timer": "fun(callback: function, opts: HL.TimerOptions): HL.Timer",
|
||||
"hl.config": "fun(config: table): nil",
|
||||
|
|
@ -523,6 +524,9 @@ def generate_stub(root: Path) -> str:
|
|||
lines.append("---@alias HL.CssGap integer|{top?:integer, right?:integer, bottom?:integer, left?:integer}")
|
||||
lines.append("---@alias HL.Gradient string|{colors:string[], angle?:number}")
|
||||
lines.append("")
|
||||
lines.append("---@class HL.Dispatcher")
|
||||
lines.append("local __HL_Dispatcher = {}")
|
||||
lines.append("")
|
||||
|
||||
lines.extend(
|
||||
emit_class_block(
|
||||
|
|
@ -651,7 +655,8 @@ def generate_stub(root: Path) -> str:
|
|||
|
||||
for method in sorted(node.methods):
|
||||
full_name = f"{full_prefix}.{method}"
|
||||
method_type = api_signatures.get(full_name, "fun(...): any")
|
||||
default_method_type = "fun(...): HL.Dispatcher" if path and path[0] == "dsp" else "fun(...): any"
|
||||
method_type = api_signatures.get(full_name, default_method_type)
|
||||
fields.append((method, method_type, False))
|
||||
|
||||
for child_name in sorted(node.children.keys()):
|
||||
|
|
|
|||
104
meta/hl.meta.lua
104
meta/hl.meta.lua
|
|
@ -378,6 +378,9 @@
|
|||
---@alias HL.CssGap integer|{top?:integer, right?:integer, bottom?:integer, left?:integer}
|
||||
---@alias HL.Gradient string|{colors:string[], angle?:number}
|
||||
|
||||
---@class HL.Dispatcher
|
||||
local __HL_Dispatcher = {}
|
||||
|
||||
---@class HL.Vec2
|
||||
---@field x number
|
||||
---@field y number
|
||||
|
|
@ -739,12 +742,12 @@ local __HL_Workspace = {}
|
|||
|
||||
---@class HL.API
|
||||
---@field animation fun(...): any
|
||||
---@field bind fun(keys: string, dispatcher: function, opts?: HL.BindOptions): HL.Keybind
|
||||
---@field bind fun(keys: string, dispatcher: HL.Dispatcher|function, opts?: HL.BindOptions): HL.Keybind
|
||||
---@field config fun(config: table): nil
|
||||
---@field curve fun(...): any
|
||||
---@field define_submap fun(name: string, reset_or_fn: string|function, fn?: function): nil
|
||||
---@field device fun(spec: HL.DeviceSpec): nil
|
||||
---@field dispatch fun(...): any
|
||||
---@field dispatch fun(dispatcher: HL.Dispatcher|function): any
|
||||
---@field env fun(...): any
|
||||
---@field exec_cmd fun(cmd: string, rules?: table<string, string|number|boolean>): nil
|
||||
---@field gesture fun(spec: HL.GestureSpec): nil
|
||||
|
|
@ -783,21 +786,21 @@ local __HL_Workspace = {}
|
|||
local __HL_API = {}
|
||||
|
||||
---@class HL.DspNamespace
|
||||
---@field dpms fun(...): any
|
||||
---@field event fun(...): any
|
||||
---@field exec_cmd fun(...): any
|
||||
---@field exec_raw fun(...): any
|
||||
---@field exit fun(...): any
|
||||
---@field focus fun(...): any
|
||||
---@field force_idle fun(...): any
|
||||
---@field force_renderer_reload fun(...): any
|
||||
---@field global fun(...): any
|
||||
---@field layout fun(...): any
|
||||
---@field no_op fun(...): any
|
||||
---@field pass fun(...): any
|
||||
---@field send_key_state fun(...): any
|
||||
---@field send_shortcut fun(...): any
|
||||
---@field submap fun(...): any
|
||||
---@field dpms fun(...): HL.Dispatcher
|
||||
---@field event fun(...): HL.Dispatcher
|
||||
---@field exec_cmd fun(...): HL.Dispatcher
|
||||
---@field exec_raw fun(...): HL.Dispatcher
|
||||
---@field exit fun(...): HL.Dispatcher
|
||||
---@field focus fun(...): HL.Dispatcher
|
||||
---@field force_idle fun(...): HL.Dispatcher
|
||||
---@field force_renderer_reload fun(...): HL.Dispatcher
|
||||
---@field global fun(...): HL.Dispatcher
|
||||
---@field layout fun(...): HL.Dispatcher
|
||||
---@field no_op fun(...): HL.Dispatcher
|
||||
---@field pass fun(...): HL.Dispatcher
|
||||
---@field send_key_state fun(...): HL.Dispatcher
|
||||
---@field send_shortcut fun(...): HL.Dispatcher
|
||||
---@field submap fun(...): HL.Dispatcher
|
||||
---@field cursor HL.DspCursorNamespace
|
||||
---@field group HL.DspGroupNamespace
|
||||
---@field window HL.DspWindowNamespace
|
||||
|
|
@ -805,48 +808,49 @@ local __HL_API = {}
|
|||
local __HL_DspNamespace = {}
|
||||
|
||||
---@class HL.DspCursorNamespace
|
||||
---@field move fun(...): any
|
||||
---@field move_to_corner fun(...): any
|
||||
---@field move fun(...): HL.Dispatcher
|
||||
---@field move_to_corner fun(...): HL.Dispatcher
|
||||
local __HL_DspCursorNamespace = {}
|
||||
|
||||
---@class HL.DspGroupNamespace
|
||||
---@field active fun(...): any
|
||||
---@field lock fun(...): any
|
||||
---@field lock_active fun(...): any
|
||||
---@field move_window fun(...): any
|
||||
---@field next fun(...): any
|
||||
---@field prev fun(...): any
|
||||
---@field toggle fun(...): any
|
||||
---@field active fun(...): HL.Dispatcher
|
||||
---@field lock fun(...): HL.Dispatcher
|
||||
---@field lock_active fun(...): HL.Dispatcher
|
||||
---@field move_window fun(...): HL.Dispatcher
|
||||
---@field next fun(...): HL.Dispatcher
|
||||
---@field prev fun(...): HL.Dispatcher
|
||||
---@field toggle fun(...): HL.Dispatcher
|
||||
local __HL_DspGroupNamespace = {}
|
||||
|
||||
---@class HL.DspWindowNamespace
|
||||
---@field alter_zorder fun(...): any
|
||||
---@field bring_to_top fun(...): any
|
||||
---@field center fun(...): any
|
||||
---@field close fun(...): any
|
||||
---@field cycle_next fun(...): any
|
||||
---@field deny_from_group fun(...): any
|
||||
---@field drag fun(...): any
|
||||
---@field float fun(...): any
|
||||
---@field fullscreen fun(...): any
|
||||
---@field fullscreen_state fun(...): any
|
||||
---@field kill fun(...): any
|
||||
---@field move fun(...): any
|
||||
---@field pin fun(...): any
|
||||
---@field pseudo fun(...): any
|
||||
---@field resize fun(...): any
|
||||
---@field set_prop fun(...): any
|
||||
---@field signal fun(...): any
|
||||
---@field swap fun(...): any
|
||||
---@field tag fun(...): any
|
||||
---@field toggle_swallow fun(...): any
|
||||
---@field alter_zorder fun(...): HL.Dispatcher
|
||||
---@field bring_to_top fun(...): HL.Dispatcher
|
||||
---@field center fun(...): HL.Dispatcher
|
||||
---@field clear_tags fun(...): HL.Dispatcher
|
||||
---@field close fun(...): HL.Dispatcher
|
||||
---@field cycle_next fun(...): HL.Dispatcher
|
||||
---@field deny_from_group fun(...): HL.Dispatcher
|
||||
---@field drag fun(...): HL.Dispatcher
|
||||
---@field float fun(...): HL.Dispatcher
|
||||
---@field fullscreen fun(...): HL.Dispatcher
|
||||
---@field fullscreen_state fun(...): HL.Dispatcher
|
||||
---@field kill fun(...): HL.Dispatcher
|
||||
---@field move fun(...): HL.Dispatcher
|
||||
---@field pin fun(...): HL.Dispatcher
|
||||
---@field pseudo fun(...): HL.Dispatcher
|
||||
---@field resize fun(...): HL.Dispatcher
|
||||
---@field set_prop fun(...): HL.Dispatcher
|
||||
---@field signal fun(...): HL.Dispatcher
|
||||
---@field swap fun(...): HL.Dispatcher
|
||||
---@field tag fun(...): HL.Dispatcher
|
||||
---@field toggle_swallow fun(...): HL.Dispatcher
|
||||
local __HL_DspWindowNamespace = {}
|
||||
|
||||
---@class HL.DspWorkspaceNamespace
|
||||
---@field move fun(...): any
|
||||
---@field rename fun(...): any
|
||||
---@field swap_monitors fun(...): any
|
||||
---@field toggle_special fun(...): any
|
||||
---@field move fun(...): HL.Dispatcher
|
||||
---@field rename fun(...): HL.Dispatcher
|
||||
---@field swap_monitors fun(...): HL.Dispatcher
|
||||
---@field toggle_special fun(...): HL.Dispatcher
|
||||
local __HL_DspWorkspaceNamespace = {}
|
||||
|
||||
---@class HL.NotificationNamespace
|
||||
|
|
|
|||
|
|
@ -673,6 +673,9 @@ void CCompositor::initManagers(eManagersInitStage stage) {
|
|||
Log::logger->log(Log::DEBUG, "Creating the SeatManager!");
|
||||
g_pSeatManager = makeUnique<CSeatManager>();
|
||||
|
||||
Log::logger->log(Log::DEBUG, "Creating the SessionLockManager!");
|
||||
g_pSessionLockManager = makeUnique<CSessionLockManager>();
|
||||
|
||||
// init focus state els
|
||||
Desktop::History::windowTracker();
|
||||
Desktop::History::workspaceTracker();
|
||||
|
|
@ -688,9 +691,6 @@ void CCompositor::initManagers(eManagersInitStage stage) {
|
|||
Log::logger->log(Log::DEBUG, "Creating the XWaylandManager!");
|
||||
g_pXWaylandManager = makeUnique<CHyprXWaylandManager>();
|
||||
|
||||
Log::logger->log(Log::DEBUG, "Creating the SessionLockManager!");
|
||||
g_pSessionLockManager = makeUnique<CSessionLockManager>();
|
||||
|
||||
Log::logger->log(Log::DEBUG, "Creating the Debug Overlay!");
|
||||
Debug::overlay();
|
||||
|
||||
|
|
|
|||
|
|
@ -1538,15 +1538,15 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
|
|||
else if ((ARGS.size() > sc<size_t>(4) + DESCR_OFFSET + DEVICE_OFFSET && !mouse) || (ARGS.size() > sc<size_t>(3) + DESCR_OFFSET + DEVICE_OFFSET && mouse))
|
||||
return "bind: too many args";
|
||||
|
||||
std::vector<xkb_keysym_t> KEYSYMS;
|
||||
std::vector<xkb_keysym_t> MODS;
|
||||
std::vector<KeybindKey> KEYSYMS;
|
||||
std::vector<KeybindKey> MODS;
|
||||
|
||||
if (multiKey) {
|
||||
for (const auto& splitKey : CVarList(ARGS[1], 8, '&')) {
|
||||
KEYSYMS.emplace_back(xkb_keysym_from_name(splitKey.c_str(), XKB_KEYSYM_CASE_INSENSITIVE));
|
||||
KEYSYMS.emplace_back(xkb_keysym_from_name(splitKey.c_str(), XKB_KEYSYM_CASE_INSENSITIVE), 0);
|
||||
}
|
||||
for (const auto& splitMod : CVarList(ARGS[0], 8, '&')) {
|
||||
MODS.emplace_back(xkb_keysym_from_name(splitMod.c_str(), XKB_KEYSYM_CASE_INSENSITIVE));
|
||||
MODS.emplace_back(xkb_keysym_from_name(splitMod.c_str(), XKB_KEYSYM_CASE_INSENSITIVE), 0);
|
||||
}
|
||||
}
|
||||
const auto MOD = g_pKeybindManager->stringToModMask(ARGS[0]);
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ static int safeLuaRequire(lua_State* L) {
|
|||
|
||||
WP<CConfigManager> Lua::mgr() {
|
||||
auto& mgr = Config::mgr();
|
||||
if (mgr->type() != CONFIG_LUA)
|
||||
if (!mgr || mgr->type() != CONFIG_LUA)
|
||||
return nullptr;
|
||||
|
||||
return dynamicPointerCast<Lua::CConfigManager>(WP<IConfigManager>(mgr));
|
||||
|
|
|
|||
|
|
@ -61,15 +61,18 @@ void CLuaEventHandler::dispatch(const std::string& name, int nargs, const std::f
|
|||
lua_rawgeti(m_lua, LUA_REGISTRYINDEX, sub->second.luaRef);
|
||||
pushArgs();
|
||||
|
||||
int status = LUA_OK;
|
||||
if (auto* mgr = CConfigManager::fromLuaState(m_lua); mgr)
|
||||
auto* mgr = CConfigManager::fromLuaState(m_lua);
|
||||
|
||||
int status = LUA_OK;
|
||||
if (mgr)
|
||||
status = mgr->guardedPCall(nargs, 0, 0, CConfigManager::LUA_TIMEOUT_EVENT_CALLBACK_MS, std::format("hl.on(\"{}\") callback", name));
|
||||
else
|
||||
status = lua_pcall(m_lua, nargs, 0, 0);
|
||||
|
||||
if (status != LUA_OK) {
|
||||
const char* err = lua_tostring(m_lua, -1);
|
||||
Config::Lua::mgr()->addError(std::format("hl.on(\"{}\") callback: {}", name, err ? err : "(unknown)"));
|
||||
if (mgr)
|
||||
mgr->addError(std::format("hl.on(\"{}\") callback: {}", name, err ? err : "(unknown)"));
|
||||
lua_pop(m_lua, 1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
144
src/config/lua/bindings/LuaBindingsDispatcherUtils.cpp
Normal file
144
src/config/lua/bindings/LuaBindingsDispatcherUtils.cpp
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
#include "LuaBindingsInternal.hpp"
|
||||
|
||||
using namespace Config::Lua::Bindings;
|
||||
|
||||
static constexpr const char* DISPATCHER_MT = "HL.Dispatcher";
|
||||
static char DISPATCHER_TABLES_REGISTRY_KEY;
|
||||
|
||||
namespace {
|
||||
struct SDispatcherRef {
|
||||
int ref = LUA_NOREF;
|
||||
};
|
||||
}
|
||||
|
||||
static int dispatcherGc(lua_State* L) {
|
||||
auto* dispatcher = sc<SDispatcherRef*>(luaL_checkudata(L, 1, DISPATCHER_MT));
|
||||
if (dispatcher->ref != LUA_NOREF) {
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, dispatcher->ref);
|
||||
dispatcher->ref = LUA_NOREF;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dispatcherCall(lua_State* L) {
|
||||
return Internal::configError(L, "dispatcher objects cannot be called directly; use hl.dispatch(dispatcher)");
|
||||
}
|
||||
|
||||
static int dispatcherToString(lua_State* L) {
|
||||
lua_pushstring(L, "HL.Dispatcher");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void ensureDispatcherMetatable(lua_State* L) {
|
||||
if (luaL_newmetatable(L, DISPATCHER_MT)) {
|
||||
lua_pushcfunction(L, dispatcherGc);
|
||||
lua_setfield(L, -2, "__gc");
|
||||
lua_pushcfunction(L, dispatcherCall);
|
||||
lua_setfield(L, -2, "__call");
|
||||
lua_pushcfunction(L, dispatcherToString);
|
||||
lua_setfield(L, -2, "__tostring");
|
||||
|
||||
lua_pushstring(L, DISPATCHER_MT);
|
||||
lua_setfield(L, -2, "__metatable");
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
static bool isDispatcherTable(lua_State* L, int idx) {
|
||||
if (!lua_istable(L, idx))
|
||||
return false;
|
||||
|
||||
idx = lua_absindex(L, idx);
|
||||
lua_pushlightuserdata(L, &DISPATCHER_TABLES_REGISTRY_KEY);
|
||||
lua_rawget(L, LUA_REGISTRYINDEX);
|
||||
|
||||
if (!lua_istable(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
lua_pushvalue(L, idx);
|
||||
lua_rawget(L, -2);
|
||||
const bool result = lua_toboolean(L, -1);
|
||||
lua_pop(L, 2);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int dispatcherFactory(lua_State* L) {
|
||||
const int nargs = lua_gettop(L);
|
||||
|
||||
lua_pushvalue(L, lua_upvalueindex(1));
|
||||
lua_insert(L, 1);
|
||||
lua_call(L, nargs, LUA_MULTRET);
|
||||
|
||||
const int nresults = lua_gettop(L);
|
||||
if (nresults == 1 && lua_isfunction(L, -1))
|
||||
return Internal::wrapDispatcher(L);
|
||||
|
||||
return nresults;
|
||||
}
|
||||
|
||||
void Internal::setFn(lua_State* L, const char* name, lua_CFunction fn) {
|
||||
if (isDispatcherTable(L, -1)) {
|
||||
lua_pushcfunction(L, fn);
|
||||
lua_pushcclosure(L, dispatcherFactory, 1);
|
||||
} else
|
||||
lua_pushcfunction(L, fn);
|
||||
|
||||
lua_setfield(L, -2, name);
|
||||
}
|
||||
|
||||
void Internal::markDispatcherTable(lua_State* L) {
|
||||
if (!lua_istable(L, -1))
|
||||
return;
|
||||
|
||||
lua_pushlightuserdata(L, &DISPATCHER_TABLES_REGISTRY_KEY);
|
||||
lua_rawget(L, LUA_REGISTRYINDEX);
|
||||
|
||||
if (!lua_istable(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
lua_newtable(L);
|
||||
lua_pushlightuserdata(L, &DISPATCHER_TABLES_REGISTRY_KEY);
|
||||
lua_pushvalue(L, -2);
|
||||
lua_rawset(L, LUA_REGISTRYINDEX);
|
||||
}
|
||||
|
||||
lua_pushvalue(L, -2);
|
||||
lua_pushboolean(L, true);
|
||||
lua_rawset(L, -3);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
int Internal::wrapDispatcher(lua_State* L) {
|
||||
luaL_checktype(L, -1, LUA_TFUNCTION);
|
||||
|
||||
const int ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
|
||||
new (lua_newuserdata(L, sizeof(SDispatcherRef))) SDispatcherRef{.ref = ref};
|
||||
|
||||
ensureDispatcherMetatable(L);
|
||||
luaL_getmetatable(L, DISPATCHER_MT);
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool Internal::pushDispatcherFunction(lua_State* L, int idx) {
|
||||
if (lua_isfunction(L, idx)) {
|
||||
lua_pushvalue(L, idx);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto* dispatcher = sc<SDispatcherRef*>(luaL_testudata(L, idx, DISPATCHER_MT));
|
||||
if (!dispatcher || dispatcher->ref == LUA_NOREF)
|
||||
return false;
|
||||
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, dispatcher->ref);
|
||||
if (lua_isfunction(L, -1))
|
||||
return true;
|
||||
|
||||
lua_pop(L, 1);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -563,6 +563,10 @@ static int dsp_tagWindow(lua_State* L) {
|
|||
return Internal::checkResult(L, CA::tag(lua_tostring(L, lua_upvalueindex(1)), Internal::windowFromUpval(L, 2)));
|
||||
}
|
||||
|
||||
static int dsp_clearTags(lua_State* L) {
|
||||
return Internal::checkResult(L, CA::clearTags(Internal::windowFromUpval(L, 1)));
|
||||
}
|
||||
|
||||
static int dsp_toggleSwallow(lua_State* L) {
|
||||
return Internal::checkResult(L, CA::toggleSwallow());
|
||||
}
|
||||
|
|
@ -915,6 +919,12 @@ static int hlWindowTag(lua_State* L) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int hlWindowClearTags(lua_State* L) {
|
||||
Internal::pushWindowUpval(L, 1);
|
||||
lua_pushcclosure(L, dsp_clearTags, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int hlWindowToggleSwallow(lua_State* L) {
|
||||
lua_pushcclosure(L, dsp_toggleSwallow, 0);
|
||||
return 1;
|
||||
|
|
@ -1167,9 +1177,9 @@ static int hlWorkspaceToggleSpecial(lua_State* L) {
|
|||
|
||||
static int hlWorkspaceRename(lua_State* L) {
|
||||
if (!lua_istable(L, 1))
|
||||
return Internal::configError(L, "hl.workspace.rename: expected a table { id, name? }");
|
||||
return Internal::configError(L, "hl.workspace.rename: expected a table { workspace, name? }");
|
||||
|
||||
const auto id = Internal::requireTableFieldWorkspaceSelector(L, 1, "id", "hl.workspace.rename");
|
||||
const auto id = Internal::requireTableFieldWorkspaceSelector(L, 1, "workspace", "hl.workspace.rename");
|
||||
auto name = Internal::tableOptStr(L, 1, "name");
|
||||
|
||||
lua_pushstring(L, id.c_str());
|
||||
|
|
@ -1187,7 +1197,7 @@ static int hlWorkspaceMove(lua_State* L) {
|
|||
|
||||
const auto mon = Internal::requireTableFieldMonitorSelector(L, 1, "monitor", "hl.workspace.move");
|
||||
|
||||
auto id = Internal::tableOptWorkspaceSelector(L, 1, "id", "hl.workspace.move");
|
||||
auto id = Internal::tableOptWorkspaceSelector(L, 1, "workspace", "hl.workspace.move");
|
||||
if (id) {
|
||||
lua_pushstring(L, id->c_str());
|
||||
lua_pushstring(L, mon.c_str());
|
||||
|
|
@ -1214,14 +1224,17 @@ static int hlWorkspaceSwapMonitors(lua_State* L) {
|
|||
|
||||
void Internal::registerDispatcherBindings(lua_State* L) {
|
||||
lua_newtable(L);
|
||||
Internal::markDispatcherTable(L);
|
||||
|
||||
{
|
||||
lua_newtable(L);
|
||||
Internal::markDispatcherTable(L);
|
||||
Internal::setFn(L, "move_to_corner", hlCursorMoveToCorner);
|
||||
Internal::setFn(L, "move", hlCursorMove);
|
||||
lua_setfield(L, -2, "cursor");
|
||||
|
||||
lua_newtable(L);
|
||||
Internal::markDispatcherTable(L);
|
||||
Internal::setFn(L, "toggle", hlGroupToggle);
|
||||
Internal::setFn(L, "next", hlGroupNext);
|
||||
Internal::setFn(L, "prev", hlGroupPrev);
|
||||
|
|
@ -1232,6 +1245,7 @@ void Internal::registerDispatcherBindings(lua_State* L) {
|
|||
lua_setfield(L, -2, "group");
|
||||
|
||||
lua_newtable(L);
|
||||
Internal::markDispatcherTable(L);
|
||||
Internal::setFn(L, "close", hlWindowClose);
|
||||
Internal::setFn(L, "kill", hlWindowKill);
|
||||
Internal::setFn(L, "signal", hlWindowSignal);
|
||||
|
|
@ -1244,6 +1258,7 @@ void Internal::registerDispatcherBindings(lua_State* L) {
|
|||
Internal::setFn(L, "center", hlWindowCenter);
|
||||
Internal::setFn(L, "cycle_next", hlWindowCycleNext);
|
||||
Internal::setFn(L, "tag", hlWindowTag);
|
||||
Internal::setFn(L, "clear_tags", hlWindowClearTags);
|
||||
Internal::setFn(L, "toggle_swallow", hlWindowToggleSwallow);
|
||||
Internal::setFn(L, "pin", hlWindowPin);
|
||||
Internal::setFn(L, "bring_to_top", hlWindowBringToTop);
|
||||
|
|
@ -1255,6 +1270,7 @@ void Internal::registerDispatcherBindings(lua_State* L) {
|
|||
lua_setfield(L, -2, "window");
|
||||
|
||||
lua_newtable(L);
|
||||
Internal::markDispatcherTable(L);
|
||||
Internal::setFn(L, "rename", hlWorkspaceRename);
|
||||
Internal::setFn(L, "move", hlWorkspaceMove);
|
||||
Internal::setFn(L, "swap_monitors", hlWorkspaceSwapMonitors);
|
||||
|
|
|
|||
|
|
@ -449,11 +449,6 @@ CA::eTogglableAction Internal::tableToggleAction(lua_State* L, int idx, const ch
|
|||
return CA::TOGGLE_ACTION_TOGGLE;
|
||||
}
|
||||
|
||||
void Internal::setFn(lua_State* L, const char* name, lua_CFunction fn) {
|
||||
lua_pushcfunction(L, fn);
|
||||
lua_setfield(L, -2, name);
|
||||
}
|
||||
|
||||
void Internal::setMgrFn(lua_State* L, CConfigManager* mgr, const char* name, lua_CFunction fn) {
|
||||
lua_pushlightuserdata(L, mgr);
|
||||
lua_pushcclosure(L, fn, 1);
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ namespace Config::Lua::Bindings::Internal {
|
|||
{"no_screen_share", []() -> ILuaConfigValue* { return new CLuaConfigBool(false); }, WE::WINDOW_RULE_EFFECT_NO_SCREEN_SHARE},
|
||||
{"no_vrr", []() -> ILuaConfigValue* { return new CLuaConfigBool(false); }, WE::WINDOW_RULE_EFFECT_NO_VRR},
|
||||
{"stay_focused", []() -> ILuaConfigValue* { return new CLuaConfigBool(false); }, WE::WINDOW_RULE_EFFECT_STAY_FOCUSED},
|
||||
{"confine_pointer", []() -> ILuaConfigValue* { return new CLuaConfigBool(false); }, WE::WINDOW_RULE_EFFECT_CONFINE_POINTER},
|
||||
};
|
||||
|
||||
std::string argStr(lua_State* L, int idx);
|
||||
|
|
@ -180,6 +181,9 @@ namespace Config::Lua::Bindings::Internal {
|
|||
|
||||
void setFn(lua_State* L, const char* name, lua_CFunction fn);
|
||||
void setMgrFn(lua_State* L, CConfigManager* mgr, const char* name, lua_CFunction fn);
|
||||
void markDispatcherTable(lua_State* L);
|
||||
int wrapDispatcher(lua_State* L);
|
||||
bool pushDispatcherFunction(lua_State* L, int idx);
|
||||
|
||||
template <typename T>
|
||||
SParseError parseTableField(lua_State* L, int tableIdx, const char* field, T& parser) {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include "../../../devices/IKeyboard.hpp"
|
||||
#include "../../../managers/eventLoop/EventLoopManager.hpp"
|
||||
|
||||
#include <hyprutils/string/Numeric.hpp>
|
||||
#include <hyprutils/string/String.hpp>
|
||||
#include <hyprutils/string/VarList.hpp>
|
||||
|
||||
|
|
@ -46,12 +47,12 @@ static bool isSymSpecial(std::string_view sv) {
|
|||
}
|
||||
|
||||
static std::expected<void, std::string> parseKeyString(SKeybind& kb, std::string_view sv) {
|
||||
bool modsEnded = false, specialSym = false;
|
||||
CVarList2 vl(sv, 0, '+', true);
|
||||
bool modsEnded = false, specialSym = false;
|
||||
CVarList2 vl(sv, 0, '+', true);
|
||||
|
||||
uint32_t modMask = 0;
|
||||
std::vector<xkb_keysym_t> keysyms;
|
||||
std::string lastKeyArg;
|
||||
uint32_t modMask = 0;
|
||||
std::vector<std::pair<xkb_keysym_t, xkb_keycode_t>> keysyms;
|
||||
std::string lastKeyArg;
|
||||
|
||||
if (sv == "catchall") {
|
||||
kb.catchAll = true;
|
||||
|
|
@ -86,6 +87,16 @@ static std::expected<void, std::string> parseKeyString(SKeybind& kb, std::string
|
|||
continue;
|
||||
}
|
||||
|
||||
if (arg.starts_with("code:") && isNumber(std::string{arg.substr(5)})) {
|
||||
auto res = strToNumber<uint32_t>(arg.substr(5));
|
||||
|
||||
if (!res)
|
||||
return std::unexpected(std::format("Invalid keycode: \"{}\".", arg));
|
||||
|
||||
keysyms.emplace_back(XKB_KEY_NoSymbol, xkb_keycode_t{*res});
|
||||
continue;
|
||||
}
|
||||
|
||||
auto sym = xkb_keysym_from_name(std::string{arg}.c_str(), XKB_KEYSYM_CASE_INSENSITIVE);
|
||||
|
||||
if (sym == XKB_KEY_NoSymbol) {
|
||||
|
|
@ -99,7 +110,7 @@ static std::expected<void, std::string> parseKeyString(SKeybind& kb, std::string
|
|||
}
|
||||
|
||||
lastKeyArg = arg;
|
||||
keysyms.emplace_back(sym);
|
||||
keysyms.emplace_back(sym, 0);
|
||||
}
|
||||
|
||||
kb.modmask = modMask;
|
||||
|
|
@ -121,13 +132,12 @@ static int hlBind(lua_State* L) {
|
|||
if (auto res = parseKeyString(kb, keys); !res)
|
||||
return Internal::configError(L, std::format("hl.bind: failed to parse key string: {}", res.error()));
|
||||
|
||||
if (!lua_isfunction(L, 2))
|
||||
if (!Internal::pushDispatcherFunction(L, 2))
|
||||
return Internal::configError(L, "hl.bind: dispatcher must be a dispatcher (e.g. hl.dsp.window.close()) or a lua function");
|
||||
|
||||
if (kb.catchAll && mgr->m_currentSubmap.empty())
|
||||
return Internal::configError(L, "hl.bind: catchall keybinds are only allowed in submaps.");
|
||||
|
||||
lua_pushvalue(L, 2);
|
||||
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
kb.handler = "__lua";
|
||||
kb.arg = std::to_string(ref);
|
||||
|
|
@ -282,10 +292,9 @@ static int hlExecCmd(lua_State* L) {
|
|||
}
|
||||
|
||||
static int hlDispatch(lua_State* L) {
|
||||
if (!lua_isfunction(L, 1))
|
||||
return Internal::configError(L, "hl.dispatch: expected a dispatcher function (e.g. hl.dsp.window.close())");
|
||||
if (!Internal::pushDispatcherFunction(L, 1))
|
||||
return Internal::configError(L, "hl.dispatch: expected a dispatcher (e.g. hl.dsp.window.close())");
|
||||
|
||||
lua_pushvalue(L, 1);
|
||||
int status = LUA_OK;
|
||||
if (auto* mgr = CConfigManager::fromLuaState(L); mgr)
|
||||
status = mgr->guardedPCall(0, 1, 0, CConfigManager::LUA_TIMEOUT_DISPATCH_MS, "hl.dispatch");
|
||||
|
|
|
|||
|
|
@ -605,6 +605,19 @@ ActionResult Actions::tag(const std::string& tagStr, std::optional<PHLWINDOW> w)
|
|||
return {};
|
||||
}
|
||||
|
||||
ActionResult Actions::clearTags(std::optional<PHLWINDOW> w) {
|
||||
auto window = xtract(w);
|
||||
if (!window)
|
||||
return {};
|
||||
|
||||
if (window->m_ruleApplicator->m_tagKeeper.clearTags()) {
|
||||
window->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_TAG);
|
||||
window->updateDecorationValues();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ActionResult Actions::swapNext(const bool next, std::optional<PHLWINDOW> w) {
|
||||
auto window = xtract(w);
|
||||
if (!window)
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ namespace Config::Actions {
|
|||
ActionResult move(const Vector2D& pos, bool relative = false, std::optional<PHLWINDOW> window = std::nullopt /* Active */);
|
||||
ActionResult cycleNext(const bool next, std::optional<bool> onlyTiled, std::optional<bool> onlyFloating, std::optional<PHLWINDOW> window = std::nullopt /* Active */);
|
||||
ActionResult tag(const std::string& tag, std::optional<PHLWINDOW> window = std::nullopt /* Active */);
|
||||
ActionResult clearTags(std::optional<PHLWINDOW> w = std::nullopt);
|
||||
ActionResult pass(std::optional<PHLWINDOW> window = std::nullopt /* Active */);
|
||||
ActionResult pass(uint32_t modMask, uint32_t key, std::optional<PHLWINDOW> window = std::nullopt /* Active */);
|
||||
ActionResult sendKeyState(uint32_t modMask, uint32_t key, uint32_t state, std::optional<PHLWINDOW> window = std::nullopt /* Active */);
|
||||
|
|
|
|||
|
|
@ -1632,6 +1632,8 @@ static std::string dispatchGetOption(eHyprCtlOutputFormat format, std::string re
|
|||
if (format == FORMAT_NORMAL) {
|
||||
if (TYPE == typeid(Config::INTEGER))
|
||||
return std::format("int: {}\nset: {}", **rc<Config::INTEGER* const*>(VAL), VAR.setByUser);
|
||||
else if (TYPE == typeid(Config::BOOL))
|
||||
return std::format("bool: {}\nset: {}", **rc<Config::BOOL* const*>(VAL), VAR.setByUser);
|
||||
else if (TYPE == typeid(Config::FLOAT))
|
||||
return std::format("float: {:2f}\nset: {}", **rc<Config::FLOAT* const*>(VAL), VAR.setByUser);
|
||||
else if (TYPE == typeid(Config::VEC2))
|
||||
|
|
@ -1644,9 +1646,19 @@ static std::string dispatchGetOption(eHyprCtlOutputFormat format, std::string re
|
|||
return std::format("str: {}\nset: {}", **rc<Config::STRING* const*>(VAL), VAR.setByUser);
|
||||
else if (TYPE == typeid(void*))
|
||||
return std::format("custom type: {}\nset: {}", rc<Config::IComplexConfigValue*>((*rc<Hyprlang::CUSTOMTYPE* const*>(VAL))->getData())->toString(), VAR.setByUser);
|
||||
else if (TYPE == typeid(Config::IComplexConfigValue))
|
||||
return std::format("custom type: {}\nset: {}", (*rc<Config::IComplexConfigValue* const*>(VAL))->toString(), VAR.setByUser);
|
||||
else if (TYPE == typeid(Config::CCssGapData))
|
||||
return std::format("css gap data: {}\nset: {}", (*rc<Config::CCssGapData* const*>(VAL))->toString(), VAR.setByUser);
|
||||
else if (TYPE == typeid(Config::CGradientValueData))
|
||||
return std::format("gradient data: {}\nset: {}", (*rc<Config::CGradientValueData* const*>(VAL))->toString(), VAR.setByUser);
|
||||
else if (TYPE == typeid(Config::CFontWeightConfigValueData))
|
||||
return std::format("font weight data: {}\nset: {}", (*rc<Config::CFontWeightConfigValueData* const*>(VAL))->toString(), VAR.setByUser);
|
||||
} else {
|
||||
if (TYPE == typeid(Config::INTEGER))
|
||||
return std::format(R"({{"option": "{}", "int": {}, "set": {} }})", curitem, **rc<Config::INTEGER* const*>(VAL), VAR.setByUser);
|
||||
else if (TYPE == typeid(Config::BOOL))
|
||||
return std::format(R"({{"option": "{}", "bool": {}, "set": {} }})", curitem, (**rc<Config::BOOL* const*>(VAL)) ? "true" : "false", VAR.setByUser);
|
||||
else if (TYPE == typeid(Config::FLOAT))
|
||||
return std::format(R"({{"option": "{}", "float": {:2f}, "set": {} }})", curitem, **rc<Config::FLOAT* const*>(VAL), VAR.setByUser);
|
||||
else if (TYPE == typeid(Config::VEC2))
|
||||
|
|
@ -1662,6 +1674,15 @@ static std::string dispatchGetOption(eHyprCtlOutputFormat format, std::string re
|
|||
else if (TYPE == typeid(void*))
|
||||
return std::format(R"({{"option": "{}", "custom": "{}", "set": {} }})", curitem,
|
||||
rc<Config::IComplexConfigValue*>((*rc<Hyprlang::CUSTOMTYPE* const*>(VAL))->getData())->toString(), VAR.setByUser);
|
||||
else if (TYPE == typeid(Config::IComplexConfigValue))
|
||||
return std::format(R"({{"option": "{}", "custom": "{}", "set": {} }})", curitem, (*rc<Config::IComplexConfigValue* const*>(VAL))->toString(), VAR.setByUser);
|
||||
else if (TYPE == typeid(Config::CCssGapData))
|
||||
return std::format(R"({{"option": "{}", "css": "{}", "set": {} }})", curitem, (*rc<Config::CCssGapData* const*>(VAL))->toString(), VAR.setByUser);
|
||||
else if (TYPE == typeid(Config::CGradientValueData))
|
||||
return std::format(R"({{"option": "{}", "gradient": "{}", "set": {} }})", curitem, (*rc<Config::CGradientValueData* const*>(VAL))->toString(), VAR.setByUser);
|
||||
else if (TYPE == typeid(Config::CFontWeightConfigValueData))
|
||||
return std::format(R"({{"option": "{}", "font_weight": "{}", "set": {} }})", curitem, (*rc<Config::CFontWeightConfigValueData* const*>(VAL))->toString(),
|
||||
VAR.setByUser);
|
||||
}
|
||||
|
||||
return "invalid type (internal error)";
|
||||
|
|
|
|||
|
|
@ -248,6 +248,7 @@ static std::expected<WindowRuleEffectValue, std::string> parseWindowRuleEffect(C
|
|||
case WINDOW_RULE_EFFECT_RENDER_UNFOCUSED:
|
||||
case WINDOW_RULE_EFFECT_NO_SCREEN_SHARE:
|
||||
case WINDOW_RULE_EFFECT_NO_VRR:
|
||||
case WINDOW_RULE_EFFECT_CONFINE_POINTER:
|
||||
case WINDOW_RULE_EFFECT_STAY_FOCUSED: return truthy(raw);
|
||||
|
||||
case WINDOW_RULE_EFFECT_FULLSCREENSTATE: {
|
||||
|
|
|
|||
|
|
@ -52,9 +52,9 @@ std::unordered_set<CWindowRuleEffectContainer::storageType> CWindowRuleApplicato
|
|||
std::pair{std::ref(m_noFollowMouse), [this] { return noFollowMouseEffect(); }}, std::pair{std::ref(m_noScreenShare), [this] { return noScreenShareEffect(); }},
|
||||
std::pair{std::ref(m_noVRR), [this] { return noVRREffect(); }}, std::pair{std::ref(m_persistentSize), [this] { return persistentSizeEffect(); }},
|
||||
std::pair{std::ref(m_stayFocused), [this] { return stayFocusedEffect(); }}, std::pair{std::ref(m_idleInhibitMode), [this] { return idleInhibitModeEffect(); }},
|
||||
std::pair{std::ref(m_borderSize), [this] { return borderSizeEffect(); }}, std::pair{std::ref(m_rounding), [this] { return roundingEffect(); }},
|
||||
std::pair{std::ref(m_roundingPower), [this] { return roundingPowerEffect(); }}, std::pair{std::ref(m_scrollMouse), [this] { return scrollMouseEffect(); }},
|
||||
std::pair{std::ref(m_scrollTouchpad), [this] { return scrollTouchpadEffect(); }},
|
||||
std::pair{std::ref(m_confinePointer), [this] { return confinePointerEffect(); }}, std::pair{std::ref(m_borderSize), [this] { return borderSizeEffect(); }},
|
||||
std::pair{std::ref(m_rounding), [this] { return roundingEffect(); }}, std::pair{std::ref(m_roundingPower), [this] { return roundingPowerEffect(); }},
|
||||
std::pair{std::ref(m_scrollMouse), [this] { return scrollMouseEffect(); }}, std::pair{std::ref(m_scrollTouchpad), [this] { return scrollTouchpadEffect(); }},
|
||||
std::pair{std::ref(m_animationStyle), [this] { return animationStyleEffect(); }}, std::pair{std::ref(m_maxSize), [this] { return maxSizeEffect(); }},
|
||||
std::pair{std::ref(m_minSize), [this] { return minSizeEffect(); }}, std::pair{std::ref(m_activeBorderColor), [this] { return activeBorderColorEffect(); }},
|
||||
std::pair{std::ref(m_inactiveBorderColor), [this] { return inactiveBorderColorEffect(); }}));
|
||||
|
|
@ -342,6 +342,11 @@ CWindowRuleApplicator::SRuleResult CWindowRuleApplicator::applyDynamicRule(const
|
|||
m_stayFocused.second |= rule->getPropertiesMask();
|
||||
break;
|
||||
}
|
||||
case WINDOW_RULE_EFFECT_CONFINE_POINTER: {
|
||||
m_confinePointer.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
|
||||
m_confinePointer.second |= rule->getPropertiesMask();
|
||||
break;
|
||||
}
|
||||
case WINDOW_RULE_EFFECT_SCROLL_MOUSE: {
|
||||
m_scrollMouse.first.set(std::get<float>(value), Types::PRIORITY_WINDOW_RULE);
|
||||
m_scrollMouse.second |= rule->getPropertiesMask();
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ namespace Desktop::Rule {
|
|||
DEFINE_PROP(bool, noVRR, false, WINDOW_RULE_EFFECT_NO_VRR)
|
||||
DEFINE_PROP(bool, persistentSize, false, WINDOW_RULE_EFFECT_PERSISTENT_SIZE)
|
||||
DEFINE_PROP(bool, stayFocused, false, WINDOW_RULE_EFFECT_STAY_FOCUSED)
|
||||
DEFINE_PROP(bool, confinePointer, false, WINDOW_RULE_EFFECT_CONFINE_POINTER)
|
||||
|
||||
DEFINE_PROP(int, idleInhibitMode, false, WINDOW_RULE_EFFECT_IDLE_INHIBIT)
|
||||
|
||||
|
|
|
|||
|
|
@ -65,12 +65,13 @@ static const std::vector<std::string> EFFECT_STRINGS = {
|
|||
"scroll_mouse", //
|
||||
"scroll_touchpad", //
|
||||
"stay_focused", //
|
||||
"confine_pointer", //
|
||||
"__internal_last_static", //
|
||||
};
|
||||
|
||||
// This is here so that if we change the rules, we get reminded to update
|
||||
// the strings.
|
||||
static_assert(WINDOW_RULE_EFFECT_LAST_STATIC == 55);
|
||||
static_assert(WINDOW_RULE_EFFECT_LAST_STATIC == 56);
|
||||
|
||||
CWindowRuleEffectContainer::CWindowRuleEffectContainer() : IEffectContainer<eWindowRuleEffect>(std::vector<std::string>{EFFECT_STRINGS}) {
|
||||
;
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ namespace Desktop::Rule {
|
|||
WINDOW_RULE_EFFECT_SCROLL_MOUSE,
|
||||
WINDOW_RULE_EFFECT_SCROLL_TOUCHPAD,
|
||||
WINDOW_RULE_EFFECT_STAY_FOCUSED,
|
||||
WINDOW_RULE_EFFECT_CONFINE_POINTER,
|
||||
|
||||
WINDOW_RULE_EFFECT_LAST_STATIC,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,11 +7,27 @@
|
|||
#include "desktop/DesktopTypes.hpp"
|
||||
#include "render/Renderer.hpp"
|
||||
|
||||
void CMonitorZoomController::pinAnchor(const Vector2D& anchor) {
|
||||
m_pinnedAnchor = anchor;
|
||||
m_anchorPinned = true;
|
||||
}
|
||||
|
||||
void CMonitorZoomController::clearAnchor() {
|
||||
m_anchorPinned = false;
|
||||
}
|
||||
|
||||
Vector2D CMonitorZoomController::getAnchor(const PHLMONITORREF& monitor) {
|
||||
if (m_anchorPinned)
|
||||
return m_pinnedAnchor;
|
||||
|
||||
return g_pInputManager->getMouseCoordsInternal() - monitor->m_position;
|
||||
}
|
||||
|
||||
void CMonitorZoomController::zoomWithDetachedCamera(CBox& result, const Render::SRenderData& m_renderData) {
|
||||
const auto m = m_renderData.pMonitor;
|
||||
auto monbox = CBox(0, 0, m->m_size.x, m->m_size.y);
|
||||
const auto ZOOM = g_pHyprRenderer->m_renderData.mouseZoomFactor;
|
||||
const auto MOUSE = g_pInputManager->getMouseCoordsInternal() - m->m_position;
|
||||
const auto MOUSE = getAnchor(m);
|
||||
|
||||
if (m_lastZoomLevel != ZOOM) {
|
||||
if (m_resetCameraState) {
|
||||
|
|
@ -83,8 +99,7 @@ void CMonitorZoomController::applyZoomTransform(CBox& monbox, const Render::SRen
|
|||
if (*PZOOMDETACHEDCAMERA && !INITANIM)
|
||||
zoomWithDetachedCamera(monbox, m_renderData);
|
||||
else {
|
||||
const auto ZOOMCENTER =
|
||||
g_pHyprRenderer->m_renderData.mouseZoomUseMouse ? (g_pInputManager->getMouseCoordsInternal() - m->m_position) * m->m_scale : m->m_transformedSize / 2.f;
|
||||
const auto ZOOMCENTER = g_pHyprRenderer->m_renderData.mouseZoomUseMouse ? getAnchor(m) * m->m_scale : m->m_transformedSize / 2.f;
|
||||
|
||||
monbox.translate(-ZOOMCENTER).scale(ZOOM).translate(*PZOOMRIGID ? m->m_transformedSize / 2.0 : ZOOMCENTER);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "./math/Math.hpp"
|
||||
#include "../desktop/DesktopTypes.hpp"
|
||||
|
||||
namespace Render {
|
||||
struct SRenderData;
|
||||
|
|
@ -10,12 +11,18 @@ class CMonitorZoomController {
|
|||
public:
|
||||
bool m_resetCameraState = true;
|
||||
|
||||
void pinAnchor(const Vector2D& anchor);
|
||||
void clearAnchor();
|
||||
|
||||
void applyZoomTransform(CBox& monbox, const Render::SRenderData& m_renderData);
|
||||
|
||||
private:
|
||||
void zoomWithDetachedCamera(CBox& result, const Render::SRenderData& m_renderData);
|
||||
void zoomWithDetachedCamera(CBox& result, const Render::SRenderData& m_renderData);
|
||||
Vector2D getAnchor(const PHLMONITORREF& monitor);
|
||||
|
||||
CBox m_camera;
|
||||
float m_lastZoomLevel = 1.0f;
|
||||
bool m_padCamEdges = true;
|
||||
CBox m_camera;
|
||||
Vector2D m_pinnedAnchor = {};
|
||||
float m_lastZoomLevel = 1.0f;
|
||||
bool m_padCamEdges = true;
|
||||
bool m_anchorPinned = false;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -38,6 +38,15 @@ bool CTagKeeper::applyTag(const std::string& tag, bool dynamic) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CTagKeeper::clearTags() {
|
||||
if (!m_tags.empty()) {
|
||||
m_tags.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CTagKeeper::removeDynamicTag(const std::string& s) {
|
||||
return std::erase_if(m_tags, [&s](const auto& tag) { return tag == s + "*"; });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ class CTagKeeper {
|
|||
bool isTagged(const std::string& tag, bool strict = false) const;
|
||||
bool applyTag(const std::string& tag, bool dynamic = false);
|
||||
bool removeDynamicTag(const std::string& tag);
|
||||
bool clearTags();
|
||||
|
||||
const auto& getTags() const {
|
||||
return m_tags;
|
||||
|
|
|
|||
|
|
@ -729,7 +729,8 @@ Config::ErrorResult CDwindleAlgorithm::layoutMsg(const std::string_view& sv) {
|
|||
CURRENT_NODE->pParent->splitRatio = std::clamp(newRatio, 0.1F, 1.9F);
|
||||
|
||||
CURRENT_NODE->pParent->recalcSizePosRecursive();
|
||||
}
|
||||
} else
|
||||
return Config::configError(std::format("Unknown dwindle layoutmsg: {}", sv), Config::eConfigErrorLevel::ERROR, Config::eConfigErrorCode::INVALID_ARGUMENT);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -715,12 +715,15 @@ Config::ErrorResult CMasterAlgorithm::layoutMsg(const std::string_view& sv) {
|
|||
if (!OLDMASTER)
|
||||
return stateErr("no old master");
|
||||
|
||||
auto oldMasterIt = std::ranges::find(m_masterNodesData, OLDMASTER);
|
||||
auto oldMasterIt = std::ranges::find(m_masterNodesData, OLDMASTER);
|
||||
|
||||
SP<ITarget> newFocus;
|
||||
|
||||
for (auto& nd : m_masterNodesData) {
|
||||
if (!nd->isMaster) {
|
||||
const auto& newMaster = nd;
|
||||
newMaster->isMaster = true;
|
||||
newFocus = newMaster->pTarget.lock();
|
||||
|
||||
auto newMasterIt = std::ranges::find(m_masterNodesData, newMaster);
|
||||
|
||||
|
|
@ -729,7 +732,6 @@ Config::ErrorResult CMasterAlgorithm::layoutMsg(const std::string_view& sv) {
|
|||
else if (newMasterIt > oldMasterIt)
|
||||
std::ranges::rotate(oldMasterIt, newMasterIt, std::next(newMasterIt));
|
||||
|
||||
switchToWindow(newMaster->pTarget.lock());
|
||||
OLDMASTER->isMaster = false;
|
||||
|
||||
oldMasterIt = std::ranges::find(m_masterNodesData, OLDMASTER);
|
||||
|
|
@ -741,6 +743,8 @@ Config::ErrorResult CMasterAlgorithm::layoutMsg(const std::string_view& sv) {
|
|||
}
|
||||
|
||||
calculateWorkspace();
|
||||
if (newFocus)
|
||||
switchToWindow(newFocus);
|
||||
} else if (command == "rollprev") {
|
||||
const auto PNODE = getNodeFromWindow(PWINDOW);
|
||||
|
||||
|
|
@ -751,12 +755,15 @@ Config::ErrorResult CMasterAlgorithm::layoutMsg(const std::string_view& sv) {
|
|||
if (!OLDMASTER)
|
||||
return stateErr("no old master");
|
||||
|
||||
auto oldMasterIt = std::ranges::find(m_masterNodesData, OLDMASTER);
|
||||
auto oldMasterIt = std::ranges::find(m_masterNodesData, OLDMASTER);
|
||||
|
||||
SP<ITarget> newFocus;
|
||||
|
||||
for (auto& nd : m_masterNodesData | std::views::reverse) {
|
||||
if (!nd->isMaster) {
|
||||
const auto& newMaster = nd;
|
||||
newMaster->isMaster = true;
|
||||
newFocus = newMaster->pTarget.lock();
|
||||
|
||||
auto newMasterIt = std::ranges::find(m_masterNodesData, newMaster);
|
||||
|
||||
|
|
@ -765,7 +772,6 @@ Config::ErrorResult CMasterAlgorithm::layoutMsg(const std::string_view& sv) {
|
|||
else if (newMasterIt > oldMasterIt)
|
||||
std::ranges::rotate(oldMasterIt, newMasterIt, std::next(newMasterIt));
|
||||
|
||||
switchToWindow(newMaster->pTarget.lock());
|
||||
OLDMASTER->isMaster = false;
|
||||
|
||||
oldMasterIt = std::ranges::find(m_masterNodesData, OLDMASTER);
|
||||
|
|
@ -777,7 +783,10 @@ Config::ErrorResult CMasterAlgorithm::layoutMsg(const std::string_view& sv) {
|
|||
}
|
||||
|
||||
calculateWorkspace();
|
||||
}
|
||||
if (newFocus)
|
||||
switchToWindow(newFocus);
|
||||
} else
|
||||
return Config::configError(std::format("Unknown master layoutmsg: {}", sv), Config::eConfigErrorLevel::ERROR, Config::eConfigErrorCode::INVALID_ARGUMENT);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ CCursorManager::CCursorManager() {
|
|||
if (SIZE) {
|
||||
try {
|
||||
m_size = std::stoi(SIZE);
|
||||
} catch (...) { ; }
|
||||
} catch (...) { Log::logger->log(Log::WARN, "Invalid HYPRCURSOR_SIZE value \"{}\"", SIZE); }
|
||||
}
|
||||
|
||||
if (m_size <= 0) {
|
||||
|
|
@ -93,7 +93,7 @@ CCursorManager::CCursorManager() {
|
|||
if (SIZE) {
|
||||
try {
|
||||
m_size = std::stoi(SIZE);
|
||||
} catch (...) { ; }
|
||||
} catch (...) { Log::logger->log(Log::WARN, "Invalid XCURSOR_SIZE value \"{}\"", SIZE); }
|
||||
}
|
||||
|
||||
if (m_size <= 0) {
|
||||
|
|
|
|||
|
|
@ -505,25 +505,44 @@ void CKeybindManager::onSwitchOffEvent(const std::string& switchName) {
|
|||
handleKeybinds(0, SPressedKeyWithMods{.keyName = "switch:off:" + switchName}, true, nullptr, nullptr);
|
||||
}
|
||||
|
||||
eMultiKeyCase CKeybindManager::mkKeysymSetMatches(const std::vector<xkb_keysym_t> keybindKeysyms, const std::set<xkb_keysym_t> pressedKeysyms) {
|
||||
// Returns whether two sets of keysyms are equal, partially equal, or not
|
||||
// matching. (Partially matching means that pressed is a subset of bound)
|
||||
eMultiKeyCase CKeybindManager::mkKeysymSetMatches(const std::vector<KeybindKey>& keybindKeysyms, const std::set<KeybindKey>& pressedKeysyms) {
|
||||
// Returns whether the bound and pressed keys match fully, partially, or not at all.
|
||||
// KeybindKey stores {keysym, keycode}; either non-zero field matching is enough.
|
||||
|
||||
std::set<xkb_keysym_t> boundKeysNotPressed;
|
||||
std::set<xkb_keysym_t> pressedKeysNotBound;
|
||||
const auto MATCHES = [](const KeybindKey& lhs, const KeybindKey& rhs) {
|
||||
return (lhs.first != 0 && rhs.first != 0 && lhs.first == rhs.first) || (lhs.second != 0 && rhs.second != 0 && lhs.second == rhs.second);
|
||||
};
|
||||
|
||||
std::set<xkb_keysym_t> symsKb;
|
||||
for (const auto& k : keybindKeysyms) {
|
||||
symsKb.emplace(k);
|
||||
std::vector<KeybindKey> pressed{pressedKeysyms.begin(), pressedKeysyms.end()};
|
||||
std::vector<int> boundForPressed(pressed.size(), -1);
|
||||
|
||||
const auto tryMatch = [&](auto&& self, const size_t boundIdx, std::vector<uint8_t>& seen) -> bool {
|
||||
for (size_t pressedIdx = 0; pressedIdx < pressed.size(); ++pressedIdx) {
|
||||
if (seen[pressedIdx] || !MATCHES(keybindKeysyms[boundIdx], pressed[pressedIdx]))
|
||||
continue;
|
||||
|
||||
seen[pressedIdx] = true;
|
||||
|
||||
if (boundForPressed[pressedIdx] == -1 || self(self, static_cast<size_t>(boundForPressed[pressedIdx]), seen)) {
|
||||
boundForPressed[pressedIdx] = static_cast<int>(boundIdx);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
size_t matches = 0;
|
||||
for (size_t boundIdx = 0; boundIdx < keybindKeysyms.size(); ++boundIdx) {
|
||||
std::vector<uint8_t> seen(pressed.size(), false);
|
||||
if (tryMatch(tryMatch, boundIdx, seen))
|
||||
++matches;
|
||||
}
|
||||
|
||||
std::ranges::set_difference(symsKb, pressedKeysyms, std::inserter(boundKeysNotPressed, boundKeysNotPressed.begin()));
|
||||
std::ranges::set_difference(pressedKeysyms, symsKb, std::inserter(pressedKeysNotBound, pressedKeysNotBound.begin()));
|
||||
|
||||
if (boundKeysNotPressed.empty() && pressedKeysNotBound.empty())
|
||||
if (matches == keybindKeysyms.size() && matches == pressed.size())
|
||||
return MK_FULL_MATCH;
|
||||
|
||||
if (!boundKeysNotPressed.empty() && pressedKeysNotBound.empty())
|
||||
if (matches > 0 || (pressed.empty() && !keybindKeysyms.empty()))
|
||||
return MK_PARTIAL_MATCH;
|
||||
|
||||
return MK_NO_MATCH;
|
||||
|
|
@ -556,14 +575,14 @@ SDispatchResult CKeybindManager::handleKeybinds(const uint32_t modmask, const SP
|
|||
if (key.keysym != 0) {
|
||||
if (pressed) {
|
||||
if (keycodeToModifier(key.keycode))
|
||||
m_mkMods.insert(key.keysym);
|
||||
m_mkMods.emplace(key.keysym, key.keycode);
|
||||
else
|
||||
m_mkKeys.insert(key.keysym);
|
||||
m_mkKeys.emplace(key.keysym, key.keycode);
|
||||
} else {
|
||||
if (keycodeToModifier(key.keycode))
|
||||
m_mkMods.erase(key.keysym);
|
||||
std::erase_if(m_mkMods, [&key](const auto& e) { return e.first == key.keysym || e.second == key.keycode; });
|
||||
else
|
||||
m_mkKeys.erase(key.keysym);
|
||||
std::erase_if(m_mkKeys, [&key](const auto& e) { return e.first == key.keysym || e.second == key.keycode; });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -615,7 +634,8 @@ SDispatchResult CKeybindManager::handleKeybinds(const uint32_t modmask, const SP
|
|||
// check for just the one match
|
||||
// this is also needed for multi-key binds so that SUPER + A + K can't
|
||||
// be actuated by SUPER + K + A
|
||||
if (key.keysym != k->sMkKeys.back())
|
||||
auto& back = k->sMkKeys.back();
|
||||
if (key.keysym != back.first && key.keycode != back.second)
|
||||
continue;
|
||||
} else if (!key.keyName.empty()) {
|
||||
if (key.keyName != k->key)
|
||||
|
|
|
|||
|
|
@ -25,13 +25,15 @@ struct SSubmap {
|
|||
}
|
||||
};
|
||||
|
||||
using KeybindKey = std::pair<xkb_keysym_t, xkb_keycode_t>;
|
||||
|
||||
struct SKeybind {
|
||||
std::string key = "";
|
||||
std::vector<xkb_keysym_t> sMkKeys = {};
|
||||
std::vector<KeybindKey> sMkKeys = {};
|
||||
uint32_t keycode = 0;
|
||||
bool catchAll = false;
|
||||
uint32_t modmask = 0;
|
||||
std::vector<xkb_keysym_t> sMkMods = {};
|
||||
std::vector<KeybindKey> sMkMods = {};
|
||||
std::string handler = "";
|
||||
std::string arg = "";
|
||||
bool locked = false;
|
||||
|
|
@ -156,10 +158,10 @@ class CKeybindManager {
|
|||
|
||||
SDispatchResult handleKeybinds(const uint32_t, const SPressedKeyWithMods&, bool, SP<IKeyboard>, SP<IHID>);
|
||||
|
||||
std::set<xkb_keysym_t> m_mkKeys = {};
|
||||
std::set<xkb_keysym_t> m_mkMods = {};
|
||||
std::set<KeybindKey> m_mkKeys = {};
|
||||
std::set<KeybindKey> m_mkMods = {};
|
||||
eMultiKeyCase mkBindMatches(const SP<SKeybind>);
|
||||
eMultiKeyCase mkKeysymSetMatches(const std::vector<xkb_keysym_t>, const std::set<xkb_keysym_t>);
|
||||
eMultiKeyCase mkKeysymSetMatches(const std::vector<KeybindKey>&, const std::set<KeybindKey>&);
|
||||
|
||||
bool handleInternalKeybinds(xkb_keysym_t);
|
||||
bool handleVT(xkb_keysym_t);
|
||||
|
|
|
|||
|
|
@ -105,8 +105,10 @@ void CSessionLockManager::onNewSessionLock(SP<CSessionLock> pLock) {
|
|||
return;
|
||||
}
|
||||
|
||||
m_sessionLock->sendDeniedTimer = makeShared<CEventLoopTimer>(
|
||||
// Within this arbitrary amount of time, a session-lock client is expected to create and commit a lock surface for each output. If the client fails to do that, it will be denied.
|
||||
m_sessionLock->sendLockedTimer = makeShared<CEventLoopTimer>(
|
||||
// Clients get sent the "locked" event after they submitted a lock frame for each output.
|
||||
// If they fail to do this, we send the "locked" event after a fixed amount of time here.
|
||||
// Previously we sent denied after this timeout, but that forcefully makes the client exit and the protocol doesn't require that anyways.
|
||||
std::chrono::seconds(5),
|
||||
[](auto, auto) {
|
||||
if (!g_pSessionLockManager || g_pSessionLockManager->clientLocked() || g_pSessionLockManager->clientDenied())
|
||||
|
|
@ -115,29 +117,22 @@ void CSessionLockManager::onNewSessionLock(SP<CSessionLock> pLock) {
|
|||
if (!g_pSessionLockManager->m_sessionLock || !g_pSessionLockManager->m_sessionLock->lock)
|
||||
return;
|
||||
|
||||
if (g_pCompositor->m_unsafeState || !g_pCompositor->m_aqBackend->hasSession() || !g_pCompositor->m_aqBackend->session->active) {
|
||||
// Because the session is inactive, there is a good reason for why the client did't manage to render to all outputs.
|
||||
// We send locked, although this could lead to imperfect frames when we start to render again.
|
||||
g_pSessionLockManager->m_sessionLock->lock->sendLocked();
|
||||
g_pSessionLockManager->m_sessionLock->hasSentLocked = true;
|
||||
return;
|
||||
}
|
||||
|
||||
LOGM(Log::WARN, "Kicking lockscreen client, because it failed to render to all outputs within 5 seconds");
|
||||
g_pSessionLockManager->m_sessionLock->lock->sendDenied();
|
||||
g_pSessionLockManager->m_sessionLock->hasSentDenied = true;
|
||||
LOGM(Log::WARN,
|
||||
"Sending locked after a 5 second timeout. This happens when we failed to render a lock frame from the client for every output. Lockdead frames may be shown.");
|
||||
g_pSessionLockManager->m_sessionLock->lock->sendLocked();
|
||||
g_pSessionLockManager->m_sessionLock->hasSentLocked = true;
|
||||
},
|
||||
nullptr);
|
||||
|
||||
g_pEventLoopManager->addTimer(m_sessionLock->sendDeniedTimer);
|
||||
g_pEventLoopManager->addTimer(m_sessionLock->sendLockedTimer);
|
||||
}
|
||||
|
||||
void CSessionLockManager::removeSendDeniedTimer() {
|
||||
if (!m_sessionLock || !m_sessionLock->sendDeniedTimer)
|
||||
void CSessionLockManager::removeSendLockedTimer() {
|
||||
if (!m_sessionLock || !m_sessionLock->sendLockedTimer)
|
||||
return;
|
||||
|
||||
g_pEventLoopManager->removeTimer(m_sessionLock->sendDeniedTimer);
|
||||
m_sessionLock->sendDeniedTimer.reset();
|
||||
g_pEventLoopManager->removeTimer(m_sessionLock->sendLockedTimer);
|
||||
m_sessionLock->sendLockedTimer.reset();
|
||||
}
|
||||
|
||||
bool CSessionLockManager::isSessionLocked() {
|
||||
|
|
@ -169,7 +164,7 @@ void CSessionLockManager::onLockscreenRenderedOnMonitor(uint64_t id) {
|
|||
std::ranges::all_of(g_pCompositor->m_monitors, [this](auto m) { return !m->m_enabled || !m->m_dpmsStatus || m_sessionLock->lockedMonitors.contains(m->m_id); });
|
||||
|
||||
if (LOCKED && m_sessionLock->lock->good()) {
|
||||
removeSendDeniedTimer();
|
||||
removeSendLockedTimer();
|
||||
m_sessionLock->lock->sendLocked();
|
||||
m_sessionLock->hasSentLocked = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ struct SSessionLockSurface {
|
|||
struct SSessionLock {
|
||||
WP<CSessionLock> lock;
|
||||
CTimer lockTimer;
|
||||
SP<CEventLoopTimer> sendDeniedTimer;
|
||||
SP<CEventLoopTimer> sendLockedTimer;
|
||||
|
||||
std::vector<SP<SSessionLockSurface>> vSessionLockSurfaces;
|
||||
|
||||
|
|
@ -73,7 +73,7 @@ class CSessionLockManager {
|
|||
} m_listeners;
|
||||
|
||||
void onNewSessionLock(SP<CSessionLock> pWlrLock);
|
||||
void removeSendDeniedTimer();
|
||||
void removeSendLockedTimer();
|
||||
};
|
||||
|
||||
inline UP<CSessionLockManager> g_pSessionLockManager;
|
||||
|
|
|
|||
|
|
@ -265,31 +265,56 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, st
|
|||
g_pCompositor->scheduleFrameForMonitor(PMONITOR, Aquamarine::IOutput::AQ_SCHEDULE_CURSOR_MOVE);
|
||||
|
||||
// constraints
|
||||
if (!overridePos.has_value() && !g_pSeatManager->m_mouse.expired() && isConstrained()) {
|
||||
const auto SURF = Desktop::View::CWLSurface::fromResource(Desktop::focusState()->surface());
|
||||
const auto CONSTRAINT = SURF ? SURF->constraint() : nullptr;
|
||||
|
||||
if (CONSTRAINT) {
|
||||
if (CONSTRAINT->isLocked()) {
|
||||
const auto HINT = CONSTRAINT->logicPositionHint();
|
||||
g_pCompositor->warpCursorTo(HINT, true);
|
||||
} else {
|
||||
const auto RG = CONSTRAINT->logicConstraintRegion();
|
||||
const auto CLOSEST = RG.closestPoint(mouseCoords);
|
||||
const auto BOX = SURF->getSurfaceBoxGlobal();
|
||||
const auto WINDOW = Desktop::View::CWindow::fromView(SURF->view());
|
||||
const auto CLOSESTLOCAL = (CLOSEST - (BOX.has_value() ? BOX->pos() : Vector2D{})) * (WINDOW ? WINDOW->m_X11SurfaceScaledBy : 1.0);
|
||||
|
||||
g_pCompositor->warpCursorTo(CLOSEST, true);
|
||||
g_pSeatManager->sendPointerMotion(time, CLOSESTLOCAL);
|
||||
PROTO::relativePointer->sendRelativeMotion(sc<uint64_t>(time) * 1000, {}, {});
|
||||
}
|
||||
|
||||
auto confineToRegion = [&](const CRegion& rg, SP<Desktop::View::CWLSurface> surf) {
|
||||
if (!surf)
|
||||
return;
|
||||
|
||||
} else
|
||||
Log::logger->log(Log::ERR, "BUG THIS: Null SURF/CONSTRAINT in mouse refocus. Ignoring constraints. {:x} {:x}", rc<uintptr_t>(SURF.get()),
|
||||
rc<uintptr_t>(CONSTRAINT.get()));
|
||||
const auto CLOSEST = rg.closestPoint(mouseCoords);
|
||||
const auto BOX = surf->getSurfaceBoxGlobal();
|
||||
const auto WINDOW = Desktop::View::CWindow::fromView(surf->view());
|
||||
const auto CLOSESTLOCAL = (CLOSEST - (BOX.has_value() ? BOX->pos() : Vector2D{})) * (WINDOW ? WINDOW->m_X11SurfaceScaledBy : 1.0);
|
||||
|
||||
if (g_pSeatManager->m_state.pointerFocus != surf->resource())
|
||||
g_pSeatManager->setPointerFocus(surf->resource(), CLOSESTLOCAL);
|
||||
|
||||
g_pCompositor->warpCursorTo(CLOSEST, true);
|
||||
g_pSeatManager->sendPointerMotion(time, CLOSESTLOCAL);
|
||||
PROTO::relativePointer->sendRelativeMotion(sc<uint64_t>(time) * 1000, {}, {});
|
||||
};
|
||||
|
||||
if (!g_pSeatManager->m_mouse.expired()) {
|
||||
const auto SURF = Desktop::View::CWLSurface::fromResource(Desktop::focusState()->surface());
|
||||
|
||||
if (isConstrained()) {
|
||||
const auto CONSTRAINT = SURF ? SURF->constraint() : nullptr;
|
||||
|
||||
if (CONSTRAINT) {
|
||||
if (CONSTRAINT->isLocked()) {
|
||||
const auto HINT = CONSTRAINT->logicPositionHint();
|
||||
g_pCompositor->warpCursorTo(HINT, true);
|
||||
} else {
|
||||
confineToRegion(CONSTRAINT->logicConstraintRegion(), SURF);
|
||||
}
|
||||
|
||||
return;
|
||||
} else {
|
||||
Log::logger->log(Log::ERR, "BUG THIS: Null SURF/CONSTRAINT in mouse refocus. Ignoring constraints. {:x} {:x}", rc<uintptr_t>(SURF.get()),
|
||||
rc<uintptr_t>(CONSTRAINT.get()));
|
||||
}
|
||||
} else {
|
||||
const auto WINDOW = SURF ? Desktop::View::CWindow::fromView(SURF->view()) : nullptr;
|
||||
if (WINDOW) {
|
||||
if (WINDOW->m_ruleApplicator->confinePointer().valueOrDefault()) {
|
||||
const auto BOX = SURF->getSurfaceBoxGlobal();
|
||||
if (BOX.has_value()) {
|
||||
CRegion rg;
|
||||
rg.set(*BOX);
|
||||
confineToRegion(rg, SURF);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (PMONITOR != Desktop::focusState()->monitor() && (*PMOUSEFOCUSMON || refocus) && m_forcedFocus.expired())
|
||||
|
|
|
|||
|
|
@ -2,19 +2,40 @@
|
|||
|
||||
#include "../../../../Compositor.hpp"
|
||||
#include "../../../../helpers/Monitor.hpp"
|
||||
#include "../../../../managers/input/InputManager.hpp"
|
||||
#include <hyprutils/string/Numeric.hpp>
|
||||
|
||||
CCursorZoomTrackpadGesture::CCursorZoomTrackpadGesture(const std::string& first, const std::string& second) {
|
||||
try {
|
||||
m_zoomValue = std::stof(first);
|
||||
} catch (...) { ; }
|
||||
if (const auto n = Hyprutils::String::strToNumber<float>(first); n)
|
||||
m_zoomValue = n.value();
|
||||
|
||||
if (second == "mult")
|
||||
m_mode = MODE_MULT;
|
||||
else if (second == "live")
|
||||
m_mode = MODE_LIVE;
|
||||
}
|
||||
|
||||
void CCursorZoomTrackpadGesture::begin(const ITrackpadGesture::STrackpadGestureBegin& e) {
|
||||
ITrackpadGesture::begin(e);
|
||||
|
||||
if (m_mode == MODE_LIVE) {
|
||||
if (!e.pinch)
|
||||
return;
|
||||
|
||||
m_monitor = g_pCompositor->getMonitorFromCursor();
|
||||
if (!m_monitor)
|
||||
return;
|
||||
|
||||
const auto PMONITOR = m_monitor.lock();
|
||||
if (!PMONITOR)
|
||||
return;
|
||||
|
||||
m_zoomBegin = std::clamp(PMONITOR->m_cursorZoom->value(), 1.0F, 100.0F);
|
||||
PMONITOR->m_cursorZoom->setValueAndWarp(m_zoomBegin);
|
||||
PMONITOR->m_zoomController.pinAnchor(g_pInputManager->getMouseCoordsInternal() - PMONITOR->m_position);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_mode == MODE_TOGGLE)
|
||||
m_zoomed = !m_zoomed;
|
||||
|
||||
|
|
@ -25,9 +46,35 @@ void CCursorZoomTrackpadGesture::begin(const ITrackpadGesture::STrackpadGestureB
|
|||
*m->m_cursorZoom = m_zoomed ? m_zoomValue : *PZOOMFACTOR;
|
||||
break;
|
||||
case MODE_MULT: *m->m_cursorZoom = std::clamp(m->m_cursorZoom->goal() * m_zoomValue, 1.0F, 100.0F); break;
|
||||
case MODE_LIVE: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CCursorZoomTrackpadGesture::update(const ITrackpadGesture::STrackpadGestureUpdate& e) {}
|
||||
void CCursorZoomTrackpadGesture::end(const ITrackpadGesture::STrackpadGestureEnd& e) {}
|
||||
void CCursorZoomTrackpadGesture::update(const ITrackpadGesture::STrackpadGestureUpdate& e) {
|
||||
if (m_mode != MODE_LIVE || !m_monitor || !e.pinch)
|
||||
return;
|
||||
|
||||
const auto PMONITOR = m_monitor.lock();
|
||||
if (!PMONITOR)
|
||||
return;
|
||||
|
||||
auto zoom = std::clamp(m_zoomBegin * static_cast<float>(e.pinch->scale), 1.0F, 100.0F);
|
||||
|
||||
if (zoom < 1.05F)
|
||||
zoom = 1.0F;
|
||||
|
||||
PMONITOR->m_cursorZoom->setValueAndWarp(zoom);
|
||||
}
|
||||
|
||||
void CCursorZoomTrackpadGesture::end(const ITrackpadGesture::STrackpadGestureEnd& e) {
|
||||
if (m_mode != MODE_LIVE || !m_monitor)
|
||||
return;
|
||||
|
||||
const auto PMONITOR = m_monitor.lock();
|
||||
if (!PMONITOR)
|
||||
return;
|
||||
|
||||
PMONITOR->m_zoomController.clearAnchor();
|
||||
m_monitor.reset();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "ITrackpadGesture.hpp"
|
||||
|
||||
#include "../../../../desktop/DesktopTypes.hpp"
|
||||
|
||||
class CCursorZoomTrackpadGesture : public ITrackpadGesture {
|
||||
public:
|
||||
CCursorZoomTrackpadGesture(const std::string& zoomLevel, const std::string& mode);
|
||||
|
|
@ -14,10 +16,13 @@ class CCursorZoomTrackpadGesture : public ITrackpadGesture {
|
|||
private:
|
||||
float m_zoomValue = 1.0;
|
||||
inline static bool m_zoomed = false;
|
||||
PHLMONITORREF m_monitor;
|
||||
float m_zoomBegin = 1.0;
|
||||
|
||||
enum eMode : uint8_t {
|
||||
MODE_TOGGLE = 0,
|
||||
MODE_MULT,
|
||||
MODE_LIVE,
|
||||
};
|
||||
|
||||
eMode m_mode = MODE_TOGGLE;
|
||||
|
|
|
|||
|
|
@ -140,16 +140,18 @@ WP<CScreenshareSession> CScreenshareManager::getManagedSession(eScreenshareType
|
|||
m_sessions.emplace_back(session);
|
||||
|
||||
it = m_managedSessions.emplace(m_managedSessions.end(), makeUnique<SManagedSession>(std::move(session)));
|
||||
|
||||
auto& managed = *it;
|
||||
managed->stoppedListener = managed->m_session->m_events.stopped.listen([managed = WP<SManagedSession>(managed)]() {
|
||||
if (!managed)
|
||||
return;
|
||||
|
||||
const auto& session = managed->m_session;
|
||||
std::erase_if(Screenshare::mgr()->m_managedSessions, [&session](const auto& s) { return s && s->m_session == session; });
|
||||
});
|
||||
}
|
||||
|
||||
auto& session = *it;
|
||||
|
||||
session->stoppedListener = session->m_session->m_events.stopped.listen([session = WP<SManagedSession>(session)]() {
|
||||
if (!session.expired())
|
||||
std::erase_if(Screenshare::mgr()->m_managedSessions, [&](const auto& s) { return s && s->m_session.get() == session->m_session.get(); });
|
||||
});
|
||||
|
||||
return session->m_session;
|
||||
return (*it)->m_session;
|
||||
}
|
||||
|
||||
bool CScreenshareManager::isOutputBeingSSd(PHLMONITOR monitor) {
|
||||
|
|
|
|||
|
|
@ -56,9 +56,9 @@ void CScreenshareSession::stop() {
|
|||
if (m_stopped)
|
||||
return;
|
||||
m_stopped = true;
|
||||
m_events.stopped.emit();
|
||||
|
||||
screenshareEvents(false);
|
||||
m_events.stopped.emit();
|
||||
}
|
||||
|
||||
bool CScreenshareSession::isActive() {
|
||||
|
|
@ -80,8 +80,9 @@ void CScreenshareSession::init() {
|
|||
if (g_pEventLoopManager)
|
||||
g_pEventLoopManager->addTimer(m_shareStopTimer);
|
||||
|
||||
// scale capture box since it's in logical coords
|
||||
m_captureBox.scale(monitor()->m_scale);
|
||||
// scale capture box since it's in logical coords; round to integer pixel
|
||||
// dims so m_bufferSize matches the int32 size we send to the client
|
||||
m_captureBox.scale(monitor()->m_scale).round();
|
||||
|
||||
m_listeners.monitorDestroyed = monitor()->m_events.disconnect.listen([this]() { stop(); });
|
||||
m_listeners.monitorModeChanged = monitor()->m_events.modeChanged.listen([this]() {
|
||||
|
|
|
|||
|
|
@ -2191,8 +2191,9 @@ void IHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) {
|
|||
Event::bus()->m_events.render.stage.emit(RENDER_POST);
|
||||
|
||||
pMonitor->m_output->state->addDamage(frameDamage);
|
||||
pMonitor->m_output->state->setPresentationMode(shouldTear ? Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_IMMEDIATE :
|
||||
Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_VSYNC);
|
||||
auto presentationMode = shouldTear ? Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_IMMEDIATE : Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_VSYNC;
|
||||
if (pMonitor->m_output->state->state().presentationMode != presentationMode)
|
||||
pMonitor->m_output->state->setPresentationMode(presentationMode);
|
||||
|
||||
if (commit)
|
||||
commitPendingAndDoExplicitSync(pMonitor);
|
||||
|
|
|
|||
|
|
@ -48,6 +48,13 @@ namespace {
|
|||
lua_pop(L, 1);
|
||||
return v;
|
||||
}
|
||||
|
||||
bool isGlobalNil(lua_State* L, const char* name) {
|
||||
lua_getglobal(L, name);
|
||||
const bool isnil = lua_isnil(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return isnil;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ConfigLuaObjects, keybindCanToggleEnabledFromLua) {
|
||||
|
|
@ -138,10 +145,9 @@ TEST(ConfigLuaObjects, objectsAreReadOnlyFromLua) {
|
|||
Objects::CLuaKeybind::push(L, keybind);
|
||||
lua_setglobal(L, "kb");
|
||||
|
||||
EXPECT_NE(luaL_dostring(L, "kb.foo = 1"), LUA_OK);
|
||||
ASSERT_TRUE(lua_isstring(L, -1));
|
||||
EXPECT_NE(std::string(lua_tostring(L, -1)).find("read-only"), std::string::npos);
|
||||
lua_pop(L, 1);
|
||||
luaL_dostring(L, "kb.foo = 1");
|
||||
luaL_dostring(L, "x = kb.foo");
|
||||
EXPECT_TRUE(isGlobalNil(L, "x"));
|
||||
}
|
||||
|
||||
TEST(ConfigLuaObjects, keybindSupportsEqAndToString) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue