From 15072831cfd9efccebd1ad27b21c5604f224aa1b Mon Sep 17 00:00:00 2001 From: shezdy <77217897+shezdy@users.noreply.github.com> Date: Sat, 11 May 2024 17:02:26 -0600 Subject: [PATCH 01/57] keybinds: fix release binds in submaps (#6025) --- src/managers/KeybindManager.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp index 15f667f0..286372c4 100644 --- a/src/managers/KeybindManager.cpp +++ b/src/managers/KeybindManager.cpp @@ -386,8 +386,7 @@ bool CKeybindManager::onKeyEvent(std::any event, SP pKeyboard) { bool foundInPressedKeys = false; for (auto it = m_dPressedKeys.begin(); it != m_dPressedKeys.end();) { if (it->keycode == KEYCODE) { - if (it->submapAtPress == m_szCurrentSelectedSubmap) - handleKeybinds(MODS, *it, false); + handleKeybinds(MODS, *it, false); foundInPressedKeys = true; suppressEvent = !it->sent; it = m_dPressedKeys.erase(it); @@ -570,7 +569,7 @@ bool CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWi if (key.keycode != k.keycode) continue; } else if (k.catchAll) { - if (found) + if (found || key.submapAtPress != m_szCurrentSelectedSubmap) continue; } else { // oMg such performance hit!!11! From 33a7b7bb6b307d6e4a093f75ffdda0419cd7ffaf Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Sat, 11 May 2024 20:03:32 -0300 Subject: [PATCH 02/57] core: fix on-empty workspace being called too often (#6026) --- src/Compositor.cpp | 4 ++-- src/Compositor.hpp | 2 +- src/desktop/Workspace.cpp | 12 +++++++----- src/desktop/Workspace.hpp | 6 ++++-- src/managers/KeybindManager.cpp | 4 ++-- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 9a9ea30b..c532f958 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -2492,7 +2492,7 @@ void CCompositor::forceReportSizesToWindowsOnWorkspace(const int& wid) { } } -PHLWORKSPACE CCompositor::createNewWorkspace(const int& id, const int& monid, const std::string& name) { +PHLWORKSPACE CCompositor::createNewWorkspace(const int& id, const int& monid, const std::string& name, bool isEmtpy) { const auto NAME = name == "" ? std::to_string(id) : name; auto monID = monid; @@ -2503,7 +2503,7 @@ PHLWORKSPACE CCompositor::createNewWorkspace(const int& id, const int& monid, co const bool SPECIAL = id >= SPECIAL_WORKSPACE_START && id <= -2; - const auto PWORKSPACE = m_vWorkspaces.emplace_back(CWorkspace::create(id, monID, NAME, SPECIAL)); + const auto PWORKSPACE = m_vWorkspaces.emplace_back(CWorkspace::create(id, monID, NAME, SPECIAL, isEmtpy)); PWORKSPACE->m_fAlpha.setValueAndWarp(0); diff --git a/src/Compositor.hpp b/src/Compositor.hpp index 8812f8c8..c8bb8743 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -164,7 +164,7 @@ class CCompositor { void closeWindow(PHLWINDOW); Vector2D parseWindowVectorArgsRelative(const std::string&, const Vector2D&); void forceReportSizesToWindowsOnWorkspace(const int&); - PHLWORKSPACE createNewWorkspace(const int&, const int&, const std::string& name = ""); // will be deleted next frame if left empty and unfocused! + PHLWORKSPACE createNewWorkspace(const int&, const int&, const std::string& name = "", bool isEmtpy = true); // will be deleted next frame if left empty and unfocused! void renameWorkspace(const int&, const std::string& name = ""); void setActiveMonitor(CMonitor*); bool isWorkspaceSpecial(const int&); diff --git a/src/desktop/Workspace.cpp b/src/desktop/Workspace.cpp index b730c9ab..7a836b3b 100644 --- a/src/desktop/Workspace.cpp +++ b/src/desktop/Workspace.cpp @@ -2,17 +2,18 @@ #include "../Compositor.hpp" #include "../config/ConfigValue.hpp" -PHLWORKSPACE CWorkspace::create(int id, int monitorID, std::string name, bool special) { - PHLWORKSPACE workspace = makeShared(id, monitorID, name, special); +PHLWORKSPACE CWorkspace::create(int id, int monitorID, std::string name, bool special, bool isEmtpy) { + PHLWORKSPACE workspace = makeShared(id, monitorID, name, special, isEmtpy); workspace->init(workspace); return workspace; } -CWorkspace::CWorkspace(int id, int monitorID, std::string name, bool special) { +CWorkspace::CWorkspace(int id, int monitorID, std::string name, bool special, bool isEmtpy) { m_iMonitorID = monitorID; m_iID = id; m_szName = name; m_bIsSpecialWorkspace = special; + m_bWasCreatedEmtpy = isEmtpy; } void CWorkspace::init(PHLWORKSPACE self) { @@ -44,8 +45,9 @@ void CWorkspace::init(PHLWORKSPACE self) { const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(self); m_bPersistent = WORKSPACERULE.isPersistent; - if (auto cmd = WORKSPACERULE.onCreatedEmptyRunCmd) - g_pKeybindManager->spawn(*cmd); + if (self->m_bWasCreatedEmtpy) + if (auto cmd = WORKSPACERULE.onCreatedEmptyRunCmd) + g_pKeybindManager->spawn(*cmd); g_pEventManager->postEvent({"createworkspace", m_szName}); g_pEventManager->postEvent({"createworkspacev2", std::format("{},{}", m_iID, m_szName)}); diff --git a/src/desktop/Workspace.hpp b/src/desktop/Workspace.hpp index 3fae3dfc..17431215 100644 --- a/src/desktop/Workspace.hpp +++ b/src/desktop/Workspace.hpp @@ -15,9 +15,9 @@ class CWindow; class CWorkspace { public: - static PHLWORKSPACE create(int id, int monitorID, std::string name, bool special = false); + static PHLWORKSPACE create(int id, int monitorID, std::string name, bool special = false, bool isEmtpy = true); // use create() don't use this - CWorkspace(int id, int monitorID, std::string name, bool special = false); + CWorkspace(int id, int monitorID, std::string name, bool special = false, bool isEmpty = true); ~CWorkspace(); // Workspaces ID-based have IDs > 0 @@ -58,6 +58,8 @@ class CWorkspace { // last monitor (used on reconnect) std::string m_szLastMonitor = ""; + bool m_bWasCreatedEmtpy = true; + bool m_bPersistent = false; // Inert: destroyed and invalid. If this is true, release the ptr you have. diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp index 286372c4..146dd221 100644 --- a/src/managers/KeybindManager.cpp +++ b/src/managers/KeybindManager.cpp @@ -1102,7 +1102,7 @@ void CKeybindManager::moveActiveToWorkspace(std::string args) { pMonitor = g_pCompositor->getMonitorFromID(pWorkspace->m_iMonitorID); g_pCompositor->setActiveMonitor(pMonitor); } else { - pWorkspace = g_pCompositor->createNewWorkspace(WORKSPACEID, PWINDOW->m_iMonitorID, workspaceName); + pWorkspace = g_pCompositor->createNewWorkspace(WORKSPACEID, PWINDOW->m_iMonitorID, workspaceName, false); pMonitor = g_pCompositor->getMonitorFromID(pWorkspace->m_iMonitorID); g_pCompositor->moveWindowToWorkspaceSafe(PWINDOW, pWorkspace); } @@ -1158,7 +1158,7 @@ void CKeybindManager::moveActiveToWorkspaceSilent(std::string args) { if (pWorkspace) { g_pCompositor->moveWindowToWorkspaceSafe(PWINDOW, pWorkspace); } else { - pWorkspace = g_pCompositor->createNewWorkspace(WORKSPACEID, PWINDOW->m_iMonitorID, workspaceName); + pWorkspace = g_pCompositor->createNewWorkspace(WORKSPACEID, PWINDOW->m_iMonitorID, workspaceName, false); g_pCompositor->moveWindowToWorkspaceSafe(PWINDOW, pWorkspace); } From 6be765b7a1f4677f67f31f6aa45389d40109d4c8 Mon Sep 17 00:00:00 2001 From: Mihai Fufezan Date: Thu, 9 May 2024 19:24:31 +0300 Subject: [PATCH 03/57] Nix: fix pkgconfig prefix --- nix/default.nix | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nix/default.nix b/nix/default.nix index 419b1429..5da00d72 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -70,6 +70,9 @@ assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been remov postPatch = '' # Fix hardcoded paths to /usr installation sed -i "s#/usr#$out#" src/render/OpenGL.cpp + + # Remove extra @PREFIX@ to fix pkg-config paths + sed -i "s#@PREFIX@/##g" hyprland.pc.in ''; DATE = date; From cee639d9df68842e8ad321fba8ec99d794829621 Mon Sep 17 00:00:00 2001 From: Mihai Fufezan Date: Thu, 9 May 2024 19:38:42 +0300 Subject: [PATCH 04/57] pkg-config: fix wlroots dir --- hyprland.pc.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hyprland.pc.in b/hyprland.pc.in index 6484b95f..382fb1e6 100644 --- a/hyprland.pc.in +++ b/hyprland.pc.in @@ -4,4 +4,4 @@ Name: Hyprland URL: https://github.com/hyprwm/Hyprland Description: Hyprland header files Version: @HYPRLAND_VERSION@ -Cflags: -I${prefix} -I${prefix}/hyprland/protocols -I${prefix}/hyprland -I${prefix}/hyprland/wlroots-hyprland +Cflags: -I${prefix} -I${prefix}/hyprland/protocols -I${prefix}/hyprland -I${prefix}/hyprland/wlr From c8ae9a2e832251683cbb11edba79ab7c3a2443b2 Mon Sep 17 00:00:00 2001 From: Mihai Fufezan Date: Sat, 11 May 2024 00:56:45 +0300 Subject: [PATCH 05/57] Meson: fix Cflags --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 40883073..9b278198 100644 --- a/meson.build +++ b/meson.build @@ -89,5 +89,5 @@ import('pkgconfig').generate( url: 'https://github.com/hyprwm/Hyprland', description: 'Hyprland header files', install_dir: pkg_install_dir, - subdirs: ['', 'hyprland/protocols', 'hyprland/wlroots'], + subdirs: ['', 'hyprland/protocols', 'hyprland', 'hyprland/wlr'], ) From 071f6977dff6a4c49c2b52ac7030c7cbf666c9a5 Mon Sep 17 00:00:00 2001 From: Mihai Fufezan Date: Sat, 11 May 2024 01:59:06 +0300 Subject: [PATCH 06/57] wlroots: bump --- subprojects/wlroots-hyprland | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subprojects/wlroots-hyprland b/subprojects/wlroots-hyprland index 5c1d51c5..a336b9b1 160000 --- a/subprojects/wlroots-hyprland +++ b/subprojects/wlroots-hyprland @@ -1 +1 @@ -Subproject commit 5c1d51c5a2793480f5b6c4341ad0797052aec2ea +Subproject commit a336b9b1fb415433e849de002df68c45034d0419 From ff93820bbb9313e004e23afb1257aa774a922be1 Mon Sep 17 00:00:00 2001 From: Mihai Fufezan Date: Sat, 11 May 2024 14:46:04 +0300 Subject: [PATCH 07/57] Makefile: fix wlr dir --- Makefile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 53ac0fdf..fd9eab4d 100644 --- a/Makefile +++ b/Makefile @@ -46,17 +46,17 @@ pluginenv: installheaders: @if [ ! -f ./src/version.h ]; then echo -en "You need to run $(MAKE) all first.\n" && exit 1; fi + # remove previous headers from hyprpm's dir rm -fr ${PREFIX}/include/hyprland mkdir -p ${PREFIX}/include/hyprland mkdir -p ${PREFIX}/include/hyprland/protocols - mkdir -p ${PREFIX}/include/hyprland/wlroots-hyprland + mkdir -p ${PREFIX}/include/hyprland/wlr mkdir -p ${PREFIX}/share/pkgconfig find src -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland - cd subprojects/wlroots-hyprland/include && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlroots-hyprland && cd ../../.. - cd subprojects/wlroots-hyprland/build/include && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlroots-hyprland && cd ../../../.. - cp ./protocols/*.h ${PREFIX}/include/hyprland/protocols - cp ./protocols/*.hpp ${PREFIX}/include/hyprland/protocols + cd subprojects/wlroots-hyprland/include && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlr && cd ../../.. + cd subprojects/wlroots-hyprland/build/include && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlr && cd ../../../.. + cp ./protocols/*.h* ${PREFIX}/include/hyprland/protocols cp ./build/hyprland.pc ${PREFIX}/share/pkgconfig if [ -d /usr/share/pkgconfig ]; then cp ./build/hyprland.pc /usr/share/pkgconfig 2>/dev/null || true; fi From 2ccd45a84475fab46c6fecd2fe226d3173104743 Mon Sep 17 00:00:00 2001 From: Mihai Fufezan Date: Sun, 12 May 2024 02:53:50 +0300 Subject: [PATCH 08/57] hyprpm: don't shallow clone on non-main branches --- hyprpm/src/core/PluginManager.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hyprpm/src/core/PluginManager.cpp b/hyprpm/src/core/PluginManager.cpp index affc7eea..dc3bc548 100644 --- a/hyprpm/src/core/PluginManager.cpp +++ b/hyprpm/src/core/PluginManager.cpp @@ -442,15 +442,18 @@ bool CPluginManager::updateHeaders(bool force) { progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " Cloning https://github.com/hyprwm/hyprland, this might take a moment."); + const bool bShallow = HLVER.branch == "main" || HLVER.branch == ""; + // let us give a bit of leg-room for shallowing // due to timezones, etc. const std::string SHALLOW_DATE = removeBeginEndSpacesTabs(HLVER.date).empty() ? "" : execAndGet("LC_TIME=\"en_US.UTF-8\" date --date='" + HLVER.date + " - 1 weeks' '+\%a \%b \%d \%H:\%M:\%S \%Y'"); - if (m_bVerbose) + if (m_bVerbose && bShallow) progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "will shallow since: " + SHALLOW_DATE); - std::string ret = execAndGet("cd /tmp/hyprpm && git clone --recursive https://github.com/hyprwm/hyprland hyprland-" + USERNAME + " --shallow-since='" + SHALLOW_DATE + "'"); + std::string ret = + execAndGet("cd /tmp/hyprpm && git clone --recursive https://github.com/hyprwm/hyprland hyprland-" + USERNAME + (bShallow ? " --shallow-since='" + SHALLOW_DATE + "'" : "")); if (!std::filesystem::exists(WORKINGDIR)) { progress.printMessageAbove(std::string{Colors::RED} + "✖" + Colors::RESET + " Clone failed. Retrying without shallow."); From fd35b35000fa11ce540d944966ff17c71c31fd27 Mon Sep 17 00:00:00 2001 From: Vaxry Date: Sun, 12 May 2024 16:00:55 +0100 Subject: [PATCH 09/57] keybinds: fix pass reverts #5967 fixes #6022 --- src/managers/KeybindManager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp index 146dd221..c5d7067d 100644 --- a/src/managers/KeybindManager.cpp +++ b/src/managers/KeybindManager.cpp @@ -1911,6 +1911,7 @@ void CKeybindManager::pass(std::string regexp) { const auto XWTOXW = PWINDOW->m_bIsX11 && g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_bIsX11; const auto SL = Vector2D(g_pCompositor->m_sSeat.seat->pointer_state.sx, g_pCompositor->m_sSeat.seat->pointer_state.sy); uint32_t keycodes[32] = {0}; + const auto LASTSRF = g_pCompositor->m_pLastFocus; // pass all mf shit if (!XWTOXW) { @@ -1961,7 +1962,7 @@ void CKeybindManager::pass(std::string regexp) { } if (g_pKeybindManager->m_uLastCode != 0) - wlr_seat_keyboard_enter(g_pCompositor->m_sSeat.seat, PWINDOW->m_pWLSurface.wlr(), KEYBOARD->keycodes, KEYBOARD->num_keycodes, &KEYBOARD->modifiers); + wlr_seat_keyboard_enter(g_pCompositor->m_sSeat.seat, LASTSRF, KEYBOARD->keycodes, KEYBOARD->num_keycodes, &KEYBOARD->modifiers); else wlr_seat_pointer_enter(g_pCompositor->m_sSeat.seat, PWINDOW->m_pWLSurface.wlr(), SL.x, SL.y); } From 064bdb06f1f10676d18c333cc24caee2eb199408 Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 13 May 2024 16:57:06 +0300 Subject: [PATCH 10/57] hyprctl: Add locked cmd to requests (#6042) Co-authored-by: Leftas --- src/debug/HyprCtl.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp index a89e0710..f8fdb03f 100644 --- a/src/debug/HyprCtl.cpp +++ b/src/debug/HyprCtl.cpp @@ -1550,6 +1550,18 @@ std::string dispatchDismissNotify(eHyprCtlOutputFormat format, std::string reque return "ok"; } +std::string getIsLocked(eHyprCtlOutputFormat format, std::string request) { + std::string lockedStr = g_pSessionLockManager->isSessionLocked() ? "true" : "false"; + if (format == eHyprCtlOutputFormat::FORMAT_JSON) + lockedStr = std::format(R"#( +{{ + "locked": {} +}} +)#", + lockedStr); + return lockedStr; +} + CHyprCtl::CHyprCtl() { registerCommand(SHyprCtlCommand{"workspaces", true, workspacesRequest}); registerCommand(SHyprCtlCommand{"workspacerules", true, workspaceRulesRequest}); @@ -1569,6 +1581,7 @@ CHyprCtl::CHyprCtl() { registerCommand(SHyprCtlCommand{"rollinglog", true, rollinglogRequest}); registerCommand(SHyprCtlCommand{"layouts", true, layoutsRequest}); registerCommand(SHyprCtlCommand{"configerrors", true, configErrorsRequest}); + registerCommand(SHyprCtlCommand{"locked", true, getIsLocked}); registerCommand(SHyprCtlCommand{"monitors", false, monitorsRequest}); registerCommand(SHyprCtlCommand{"reload", false, reloadRequest}); From 4c625ce6734be79bf0ddd8dda80a161994b19892 Mon Sep 17 00:00:00 2001 From: vaxerski Date: Mon, 13 May 2024 13:58:35 +0000 Subject: [PATCH 11/57] [gha] Nix: update inputs --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index 2c2d62f3..98dda5bd 100644 --- a/flake.lock +++ b/flake.lock @@ -84,11 +84,11 @@ ] }, "locked": { - "lastModified": 1715287423, - "narHash": "sha256-B7AJIjOyWgVMKhu7DlOnWa0VprdhywUVHuB/j+EwSxM=", + "lastModified": 1715608589, + "narHash": "sha256-vimNaLjLcoNIvBhF37GaB6PRYEvKMamY3UnDE9M5MW8=", "owner": "hyprwm", "repo": "hyprwayland-scanner", - "rev": "e2fc1c0eb8b392110588f478cce644348ead7271", + "rev": "65c2636484e5cb00583b8a7446c3fb657f568883", "type": "github" }, "original": { @@ -99,11 +99,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1715087517, - "narHash": "sha256-CLU5Tsg24Ke4+7sH8azHWXKd0CFd4mhLWfhYgUiDBpQ=", + "lastModified": 1715534503, + "narHash": "sha256-5ZSVkFadZbFP1THataCaSf0JH2cAH3S29hU9rrxTEqk=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b211b392b8486ee79df6cdfb1157ad2133427a29", + "rev": "2057814051972fa1453ddfb0d98badbea9b83c06", "type": "github" }, "original": { From 60be4298e13b02913f241dd79fe7517f185299d0 Mon Sep 17 00:00:00 2001 From: Vaxry Date: Mon, 13 May 2024 15:16:10 +0100 Subject: [PATCH 12/57] makefile: fix wlroots headers dir --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index fd9eab4d..e7b9c1b6 100644 --- a/Makefile +++ b/Makefile @@ -54,7 +54,7 @@ installheaders: mkdir -p ${PREFIX}/share/pkgconfig find src -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland - cd subprojects/wlroots-hyprland/include && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlr && cd ../../.. + cd subprojects/wlroots-hyprland/include/wlr && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlr && cd ../../../.. cd subprojects/wlroots-hyprland/build/include && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlr && cd ../../../.. cp ./protocols/*.h* ${PREFIX}/include/hyprland/protocols cp ./build/hyprland.pc ${PREFIX}/share/pkgconfig From 47874f09f4d14703fe0c483a46b345c7be601ace Mon Sep 17 00:00:00 2001 From: Vaxry Date: Mon, 13 May 2024 15:29:18 +0100 Subject: [PATCH 13/57] cmake: remove forceful ffi and wayland deps for asan fixes #6050 --- CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c334ab06..cfcaa686 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,8 +130,6 @@ if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG) message(STATUS "Enabling ASan") target_link_libraries(Hyprland asan) - pkg_check_modules(ffidep REQUIRED IMPORTED_TARGET libffi) - target_link_libraries(Hyprland ${CMAKE_SOURCE_DIR}/libwayland-server.a PkgConfig::ffidep) target_compile_options(Hyprland PUBLIC -fsanitize=address) endif() From ba696521930059aa489ac6ffabe28553edaf2fa3 Mon Sep 17 00:00:00 2001 From: Vaxry Date: Mon, 13 May 2024 22:21:06 +0100 Subject: [PATCH 14/57] window: set sane default pseudo size --- src/desktop/Window.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/desktop/Window.hpp b/src/desktop/Window.hpp index efccb41c..6bff095c 100644 --- a/src/desktop/Window.hpp +++ b/src/desktop/Window.hpp @@ -261,7 +261,7 @@ class CWindow { // this is used for pseudotiling bool m_bIsPseudotiled = false; - Vector2D m_vPseudoSize = Vector2D(0, 0); + Vector2D m_vPseudoSize = Vector2D(1280, 720); bool m_bFirstMap = false; // for layouts bool m_bIsFloating = false; From 1584679004435d93d5fe2dd794d2aefaa0c9574a Mon Sep 17 00:00:00 2001 From: Daniil <118675096+dnvery@users.noreply.github.com> Date: Tue, 14 May 2024 12:33:20 +0000 Subject: [PATCH 15/57] xwayland: Remove delta for real position with xwayland zero scaling (#6057) --- src/events/Windows.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/events/Windows.cpp b/src/events/Windows.cpp index 4ccf8e41..7e0e3569 100644 --- a/src/events/Windows.cpp +++ b/src/events/Windows.cpp @@ -1144,9 +1144,7 @@ void Events::listener_unmanagedSetGeometry(void* owner, void* data) { if (*PXWLFORCESCALEZERO) { if (const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID); PMONITOR) { - const Vector2D DELTA = PWINDOW->m_vRealSize.goal() - PWINDOW->m_vRealSize.goal() / PMONITOR->scale; PWINDOW->m_vRealSize.setValueAndWarp(PWINDOW->m_vRealSize.goal() / PMONITOR->scale); - PWINDOW->m_vRealPosition.setValueAndWarp(PWINDOW->m_vRealPosition.goal() + DELTA / 2.0); } } From d0a4a0e0d8ecc0a47c0bcfbf84c090cae8cee7af Mon Sep 17 00:00:00 2001 From: Sungyoon Cho Date: Wed, 15 May 2024 00:14:43 +0900 Subject: [PATCH 16/57] input: fix modifier and leds (#6062) --- src/devices/IKeyboard.cpp | 11 ++++++++++- src/devices/IKeyboard.hpp | 1 + src/managers/input/InputManager.cpp | 28 ++++++++++++++++++++++------ src/managers/input/InputManager.hpp | 1 + src/protocols/InputMethodV2.cpp | 3 ++- 5 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/devices/IKeyboard.cpp b/src/devices/IKeyboard.cpp index 3bd42eac..7e4dd912 100644 --- a/src/devices/IKeyboard.cpp +++ b/src/devices/IKeyboard.cpp @@ -131,8 +131,17 @@ void IKeyboard::updateLEDs() { leds |= (1 << i); } + updateLEDs(leds); +} + +void IKeyboard::updateLEDs(uint32_t leds) { + auto keyboard = wlr(); + + if (!keyboard || keyboard->xkb_state == nullptr) + return; + if (isVirtual() && g_pInputManager->shouldIgnoreVirtualKeyboard(self.lock())) return; - wlr_keyboard_led_update(wlr(), leds); + wlr_keyboard_led_update(keyboard, leds); } diff --git a/src/devices/IKeyboard.hpp b/src/devices/IKeyboard.hpp index d90550da..ec58ff5b 100644 --- a/src/devices/IKeyboard.hpp +++ b/src/devices/IKeyboard.hpp @@ -42,6 +42,7 @@ class IKeyboard : public IHID { void updateXKBTranslationState(xkb_keymap* const keymap = nullptr); std::string getActiveLayout(); void updateLEDs(); + void updateLEDs(uint32_t leds); bool active = false; bool enabled = true; diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index 1d09dbb9..958fb255 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -1239,6 +1239,26 @@ void CInputManager::destroyTabletPad(SP pad) { removeFromHIDs(pad); } +void CInputManager::updateKeyboardsLeds(SP pKeyboard) { + if (!pKeyboard) + return; + + auto keyboard = pKeyboard->wlr(); + + if (!keyboard || keyboard->xkb_state == nullptr) + return; + + uint32_t leds = 0; + for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) { + if (xkb_state_led_index_is_active(keyboard->xkb_state, keyboard->led_indexes[i])) + leds |= (1 << i); + } + + for (auto& k : m_vKeyboards) { + k->updateLEDs(leds); + } +} + void CInputManager::onKeyboardKey(std::any event, SP pKeyboard) { if (!pKeyboard->enabled) return; @@ -1271,9 +1291,7 @@ void CInputManager::onKeyboardKey(std::any event, SP pKeyboard) { wlr_seat_keyboard_notify_key(g_pCompositor->m_sSeat.seat, e.timeMs, e.keycode, e.state); } - for (auto& k : m_vKeyboards) { - k->updateLEDs(); - } + updateKeyboardsLeds(pKeyboard); } } @@ -1299,9 +1317,7 @@ void CInputManager::onKeyboardMod(SP pKeyboard) { wlr_seat_keyboard_notify_modifiers(g_pCompositor->m_sSeat.seat, &MODS); } - for (auto& k : m_vKeyboards) { - k->updateLEDs(); - } + updateKeyboardsLeds(pKeyboard); if (PWLRKB->modifiers.group != pKeyboard->activeLayout) { pKeyboard->activeLayout = PWLRKB->modifiers.group; diff --git a/src/managers/input/InputManager.hpp b/src/managers/input/InputManager.hpp index 970d53c8..df36b975 100644 --- a/src/managers/input/InputManager.hpp +++ b/src/managers/input/InputManager.hpp @@ -114,6 +114,7 @@ class CInputManager { void updateDragIcon(); void updateCapabilities(); + void updateKeyboardsLeds(SP); void setClickMode(eClickBehaviorMode); eClickBehaviorMode getClickMode(); diff --git a/src/protocols/InputMethodV2.cpp b/src/protocols/InputMethodV2.cpp index d84f6772..9e5c863b 100644 --- a/src/protocols/InputMethodV2.cpp +++ b/src/protocols/InputMethodV2.cpp @@ -53,7 +53,8 @@ void CInputMethodKeyboardGrabV2::sendKeyboardData(wlr_keyboard* keyboard) { close(keymapFD); - sendMods(0, 0, 0, 0); + const auto MODS = keyboard->modifiers; + sendMods(MODS.depressed, MODS.latched, MODS.locked, MODS.group); resource->sendRepeatInfo(keyboard->repeat_info.rate, keyboard->repeat_info.delay); } From 4cdddcfe466cb21db81af0ac39e51cc15f574da9 Mon Sep 17 00:00:00 2001 From: Vaxry Date: Tue, 14 May 2024 16:45:01 +0100 Subject: [PATCH 17/57] cursor: minor fixes for unhiding surfaces the surface equality check is done in CPointerManager, the one in renderer can be wrong fixes #5975 --- src/managers/PointerManager.cpp | 2 +- src/render/Renderer.cpp | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/managers/PointerManager.cpp b/src/managers/PointerManager.cpp index 663456f8..3ddd7c23 100644 --- a/src/managers/PointerManager.cpp +++ b/src/managers/PointerManager.cpp @@ -223,7 +223,7 @@ void CPointerManager::setCursorSurface(CWLSurface* surf, const Vector2D& hotspot }, nullptr, "CPointerManager"); - if (surf->wlr()->current.buffer) { + if (wlr_surface_has_buffer(surf->wlr())) { timespec now; clock_gettime(CLOCK_MONOTONIC, &now); wlr_surface_send_frame_done(surf->wlr(), &now); diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 2574a015..d9529b6f 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -2255,9 +2255,6 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR void CHyprRenderer::setCursorSurface(CWLSurface* surf, int hotspotX, int hotspotY, bool force) { m_bCursorHasSurface = surf; - if (surf == m_sLastCursorData.surf && hotspotX == m_sLastCursorData.hotspotX && hotspotY == m_sLastCursorData.hotspotY && !force) - return; - m_sLastCursorData.name = ""; m_sLastCursorData.surf = surf; m_sLastCursorData.hotspotX = hotspotX; From 121d3a72137d4780602cf245704615f63357ea22 Mon Sep 17 00:00:00 2001 From: Vaxry Date: Fri, 10 May 2024 18:27:57 +0100 Subject: [PATCH 18/57] wl_seat: move to hyprland impl --- CMakeLists.txt | 10 + src/Compositor.cpp | 75 ++--- src/Compositor.hpp | 4 - src/desktop/LayerSurface.cpp | 13 +- src/desktop/WLSurface.cpp | 2 - src/events/Devices.cpp | 6 - src/events/Events.hpp | 1 - src/events/Misc.cpp | 24 +- src/events/Windows.cpp | 3 +- src/helpers/WLClasses.hpp | 8 - src/includes.hpp | 1 - src/managers/KeybindManager.cpp | 64 ++-- src/managers/PointerManager.cpp | 5 +- src/managers/ProtocolManager.cpp | 5 + src/managers/SeatManager.cpp | 391 ++++++++++++++++++++++++ src/managers/SeatManager.hpp | 106 +++++++ src/managers/input/InputManager.cpp | 138 ++++----- src/managers/input/InputManager.hpp | 4 +- src/managers/input/Tablets.cpp | 5 +- src/managers/input/Touch.cpp | 14 +- src/protocols/FocusGrab.cpp | 245 +++++++-------- src/protocols/FocusGrab.hpp | 8 +- src/protocols/InputMethodV2.cpp | 12 +- src/protocols/PointerConstraints.cpp | 5 +- src/protocols/PointerGestures.cpp | 52 ++-- src/protocols/RelativePointer.cpp | 8 +- src/protocols/SessionLock.cpp | 3 +- src/protocols/Tablet.cpp | 20 +- src/protocols/core/Seat.cpp | 437 +++++++++++++++++++++++++++ src/protocols/core/Seat.hpp | 163 ++++++++++ src/render/Renderer.cpp | 4 +- 31 files changed, 1441 insertions(+), 395 deletions(-) create mode 100644 src/managers/SeatManager.cpp create mode 100644 src/managers/SeatManager.hpp create mode 100644 src/protocols/core/Seat.cpp create mode 100644 src/protocols/core/Seat.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index cfcaa686..608ccc62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,6 +71,7 @@ pkg_get_variable(WaylandScanner wayland-scanner wayland_scanner) message(STATUS "Found WaylandScanner at ${WaylandScanner}") pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir) message(STATUS "Found wayland-protocols at ${WAYLAND_PROTOCOLS_DIR}") +pkg_get_variable(WAYLAND_SERVER_DIR wayland-server pkgdatadir) if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG) message(STATUS "Configuring Hyprland in Debug with CMake") @@ -239,6 +240,13 @@ function(protocolNew protoPath protoName external) target_sources(Hyprland PRIVATE protocols/${protoName}.cpp) endif() endfunction() +function(protocolWayland) + execute_process( + COMMAND hyprwayland-scanner --wayland-enums ${WAYLAND_SERVER_DIR}/wayland.xml ${CMAKE_SOURCE_DIR}/protocols/ + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + ) + target_sources(Hyprland PRIVATE protocols/wayland.cpp) +endfunction() target_link_libraries(Hyprland ${CMAKE_SOURCE_DIR}/subprojects/wlroots-hyprland/build/libwlroots.a @@ -285,6 +293,8 @@ protocolNew("staging/ext-session-lock/ext-session-lock-v1.xml" "ext-session-lock protocolNew("stable/tablet/tablet-v2.xml" "tablet-v2" false) protocolNew("stable/presentation-time/presentation-time.xml" "presentation-time" false) +protocolWayland() + # tools add_subdirectory(hyprctl) add_subdirectory(hyprpm) diff --git a/src/Compositor.cpp b/src/Compositor.cpp index c532f958..efc02a5f 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -4,6 +4,7 @@ #include "managers/CursorManager.hpp" #include "managers/TokenManager.hpp" #include "managers/PointerManager.hpp" +#include "managers/SeatManager.hpp" #include "managers/eventLoop/EventLoopManager.hpp" #include #include @@ -222,16 +223,14 @@ void CCompositor::initServer() { m_sWLRCompositor = wlr_compositor_create(m_sWLDisplay, 6, m_sWLRRenderer); m_sWLRSubCompositor = wlr_subcompositor_create(m_sWLDisplay); - m_sWLRDataDevMgr = wlr_data_device_manager_create(m_sWLDisplay); + // m_sWLRDataDevMgr = wlr_data_device_manager_create(m_sWLDisplay); - wlr_data_control_manager_v1_create(m_sWLDisplay); - wlr_primary_selection_v1_device_manager_create(m_sWLDisplay); + // wlr_data_control_manager_v1_create(m_sWLDisplay); + // wlr_primary_selection_v1_device_manager_create(m_sWLDisplay); wlr_viewporter_create(m_sWLDisplay); m_sWLRXDGShell = wlr_xdg_shell_create(m_sWLDisplay, 6); - m_sSeat.seat = wlr_seat_create(m_sWLDisplay, "seat0"); - m_sWRLDRMLeaseMgr = wlr_drm_lease_v1_manager_create(m_sWLDisplay, m_sWLRBackend); if (!m_sWRLDRMLeaseMgr) { Debug::log(INFO, "Failed to create wlr_drm_lease_v1_manager"); @@ -256,12 +255,11 @@ void CCompositor::initAllSignals() { addWLSignal(&m_sWLRBackend->events.new_output, &Events::listen_newOutput, m_sWLRBackend, "Backend"); addWLSignal(&m_sWLRXDGShell->events.new_toplevel, &Events::listen_newXDGToplevel, m_sWLRXDGShell, "XDG Shell"); addWLSignal(&m_sWLRBackend->events.new_input, &Events::listen_newInput, m_sWLRBackend, "Backend"); - addWLSignal(&m_sSeat.seat->events.request_set_cursor, &Events::listen_requestMouse, &m_sSeat, "Seat"); - addWLSignal(&m_sSeat.seat->events.request_set_selection, &Events::listen_requestSetSel, &m_sSeat, "Seat"); - addWLSignal(&m_sSeat.seat->events.request_start_drag, &Events::listen_requestDrag, &m_sSeat, "Seat"); - addWLSignal(&m_sSeat.seat->events.start_drag, &Events::listen_startDrag, &m_sSeat, "Seat"); - addWLSignal(&m_sSeat.seat->events.request_set_selection, &Events::listen_requestSetSel, &m_sSeat, "Seat"); - addWLSignal(&m_sSeat.seat->events.request_set_primary_selection, &Events::listen_requestSetPrimarySel, &m_sSeat, "Seat"); + // addWLSignal(&m_sSeat.seat->events.request_set_selection, &Events::listen_requestSetSel, &m_sSeat, "Seat"); + // addWLSignal(&m_sSeat.seat->events.request_start_drag, &Events::listen_requestDrag, &m_sSeat, "Seat"); + // addWLSignal(&m_sSeat.seat->events.start_drag, &Events::listen_startDrag, &m_sSeat, "Seat"); + // addWLSignal(&m_sSeat.seat->events.request_set_selection, &Events::listen_requestSetSel, &m_sSeat, "Seat"); + // addWLSignal(&m_sSeat.seat->events.request_set_primary_selection, &Events::listen_requestSetPrimarySel, &m_sSeat, "Seat"); addWLSignal(&m_sWLRRenderer->events.destroy, &Events::listen_RendererDestroy, m_sWLRRenderer, "WLRRenderer"); if (m_sWRLDRMLeaseMgr) @@ -275,7 +273,6 @@ void CCompositor::removeAllSignals() { removeWLSignal(&Events::listen_newOutput); removeWLSignal(&Events::listen_newXDGToplevel); removeWLSignal(&Events::listen_newInput); - removeWLSignal(&Events::listen_requestMouse); removeWLSignal(&Events::listen_requestSetSel); removeWLSignal(&Events::listen_requestDrag); removeWLSignal(&Events::listen_startDrag); @@ -379,9 +376,8 @@ void CCompositor::cleanup() { g_pHookSystem.reset(); g_pWatchdog.reset(); g_pXWaylandManager.reset(); - - if (m_sSeat.seat) - wlr_seat_destroy(m_sSeat.seat); + g_pPointerManager.reset(); + g_pSeatManager.reset(); if (m_sWLRRenderer) wlr_renderer_destroy(m_sWLRRenderer); @@ -412,6 +408,9 @@ void CCompositor::initManagers(eManagersInitStage stage) { Debug::log(LOG, "Creating the ProtocolManager!"); g_pProtocolManager = std::make_unique(); + Debug::log(LOG, "Creating the SeatManager!"); + g_pSeatManager = std::make_unique(); + Debug::log(LOG, "Creating the KeybindManager!"); g_pKeybindManager = std::make_unique(); @@ -890,8 +889,8 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, wlr_surface* pSurface) { static auto PFOLLOWMOUSE = CConfigValue("input:follow_mouse"); static auto PSPECIALFALLTHROUGH = CConfigValue("input:special_fallthrough"); - if (g_pCompositor->m_sSeat.exclusiveClient) { - Debug::log(LOG, "Disallowing setting focus to a window due to there being an active input inhibitor layer."); + if (g_pSessionLockManager->isSessionLocked()) { + Debug::log(LOG, "Refusing a keyboard focus to a window because of a sessionlock"); return; } @@ -919,7 +918,7 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, wlr_surface* pSurface) { g_pXWaylandManager->activateWindow(PLASTWINDOW, false); } - wlr_seat_keyboard_notify_clear_focus(m_sSeat.seat); + g_pSeatManager->setKeyboardFocus(nullptr); g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", ","}); g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", ""}); @@ -939,7 +938,7 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, wlr_surface* pSurface) { return; } - if (m_pLastWindow.lock() == pWindow && m_sSeat.seat->keyboard_state.focused_surface == pSurface) + if (m_pLastWindow.lock() == pWindow && g_pSeatManager->state.keyboardFocus == pSurface) return; if (pWindow->m_bPinned) @@ -1017,7 +1016,7 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, wlr_surface* pSurface) { void CCompositor::focusSurface(wlr_surface* pSurface, PHLWINDOW pWindowOwner) { - if (m_sSeat.seat->keyboard_state.focused_surface == pSurface || (pWindowOwner && m_sSeat.seat->keyboard_state.focused_surface == pWindowOwner->m_pWLSurface.wlr())) + if (g_pSeatManager->state.keyboardFocus == pSurface || (pWindowOwner && g_pSeatManager->state.keyboardFocus == pWindowOwner->m_pWLSurface.wlr())) return; // Don't focus when already focused on this. if (g_pSessionLockManager->isSessionLocked() && !g_pSessionLockManager->isSurfaceSessionLock(pSurface)) @@ -1030,7 +1029,7 @@ void CCompositor::focusSurface(wlr_surface* pSurface, PHLWINDOW pWindowOwner) { g_pXWaylandManager->activateSurface(m_pLastFocus, false); if (!pSurface) { - wlr_seat_keyboard_clear_focus(m_sSeat.seat); + g_pSeatManager->setKeyboardFocus(nullptr); g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", ","}); // unfocused g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", ""}); EMIT_HOOK_EVENT("keyboardFocus", (wlr_surface*)nullptr); @@ -1038,17 +1037,8 @@ void CCompositor::focusSurface(wlr_surface* pSurface, PHLWINDOW pWindowOwner) { return; } - if (const auto KEYBOARD = wlr_seat_get_keyboard(m_sSeat.seat); KEYBOARD) { - uint32_t keycodes[WLR_KEYBOARD_KEYS_CAP] = {0}; // TODO: maybe send valid, non-keybind codes? - wlr_seat_keyboard_notify_enter(m_sSeat.seat, pSurface, keycodes, 0, &KEYBOARD->modifiers); - - wlr_seat_keyboard_focus_change_event event = { - .seat = m_sSeat.seat, - .old_surface = m_pLastFocus, - .new_surface = pSurface, - }; - wl_signal_emit_mutable(&m_sSeat.seat->keyboard_state.events.focus_change, &event); - } + if (g_pSeatManager->keyboard) + g_pSeatManager->setKeyboardFocus(pSurface); if (pWindowOwner) Debug::log(LOG, "Set keyboard focus to surface {:x}, with {}", (uintptr_t)pSurface, pWindowOwner); @@ -1245,27 +1235,6 @@ PHLWINDOW CCompositor::getTopLeftWindowOnWorkspace(const int& id) { return nullptr; } -bool CCompositor::doesSeatAcceptInput(wlr_surface* surface) { - if (g_pSessionLockManager->isSessionLocked()) { - if (g_pSessionLockManager->isSurfaceSessionLock(surface)) - return true; - - if (surface && m_sSeat.exclusiveClient == wl_resource_get_client(surface->resource)) - return true; - - return false; - } - - if (m_sSeat.exclusiveClient) { - if (surface && m_sSeat.exclusiveClient == wl_resource_get_client(surface->resource)) - return true; - - return false; - } - - return true; -} - bool CCompositor::isWindowActive(PHLWINDOW pWindow) { if (m_pLastWindow.expired() && !m_pLastFocus) return false; diff --git a/src/Compositor.hpp b/src/Compositor.hpp index c8bb8743..eedcea56 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -48,7 +48,6 @@ class CCompositor { wlr_allocator* m_sWLRAllocator; wlr_compositor* m_sWLRCompositor; wlr_subcompositor* m_sWLRSubCompositor; - wlr_data_device_manager* m_sWLRDataDevMgr; wlr_drm* m_sWRLDRM; wlr_drm_lease_v1_manager* m_sWRLDRMLeaseMgr; wlr_xdg_shell* m_sWLRXDGShell; @@ -87,8 +86,6 @@ class CCompositor { std::vector m_vWindowFocusHistory; // first element is the most recently focused. - SSeat m_sSeat; - bool m_bReadyToProcess = false; bool m_bSessionActive = true; bool m_bDPMSStateON = true; @@ -132,7 +129,6 @@ class CCompositor { PHLWINDOW getFirstWindowOnWorkspace(const int&); PHLWINDOW getTopLeftWindowOnWorkspace(const int&); PHLWINDOW getFullscreenWindowOnWorkspace(const int&); - bool doesSeatAcceptInput(wlr_surface*); bool isWindowActive(PHLWINDOW); void changeWindowZOrder(PHLWINDOW, bool); void cleanupFadingOut(const int& monid); diff --git a/src/desktop/LayerSurface.cpp b/src/desktop/LayerSurface.cpp index 2312de72..cb10079c 100644 --- a/src/desktop/LayerSurface.cpp +++ b/src/desktop/LayerSurface.cpp @@ -2,6 +2,7 @@ #include "../Compositor.hpp" #include "../events/Events.hpp" #include "../protocols/LayerShell.hpp" +#include "../managers/SeatManager.hpp" PHLLS CLayerSurface::create(SP resource) { PHLLS pLS = SP(new CLayerSurface(resource)); @@ -132,15 +133,14 @@ void CLayerSurface::onMap() { const bool GRABSFOCUS = layerSurface->current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE && // don't focus if constrained - (g_pCompositor->m_sSeat.mouse.expired() || !g_pInputManager->isConstrained()); + (g_pSeatManager->mouse.expired() || !g_pInputManager->isConstrained()); if (GRABSFOCUS) { g_pInputManager->releaseAllMouseButtons(); g_pCompositor->focusSurface(surface.wlr()); const auto LOCAL = g_pInputManager->getMouseCoordsInternal() - Vector2D(geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y); - wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, surface.wlr(), LOCAL.x, LOCAL.y); - wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, 0, LOCAL.x, LOCAL.y); + g_pSeatManager->setPointerFocus(surface.wlr(), LOCAL); g_pInputManager->m_bEmptyFocusCursorSet = false; } @@ -304,15 +304,14 @@ void CLayerSurface::onCommit() { realSize.setValueAndWarp(geometry.size()); } - if (layerSurface->current.interactivity && (g_pCompositor->m_sSeat.mouse.expired() || !g_pInputManager->isConstrained()) // don't focus if constrained + if (layerSurface->current.interactivity && (g_pSeatManager->mouse.expired() || !g_pInputManager->isConstrained()) // don't focus if constrained && !keyboardExclusive && mapped) { g_pCompositor->focusSurface(layerSurface->surface); const auto LOCAL = g_pInputManager->getMouseCoordsInternal() - Vector2D(geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y); - wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, layerSurface->surface, LOCAL.x, LOCAL.y); - wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, 0, LOCAL.x, LOCAL.y); + g_pSeatManager->setPointerFocus(layerSurface->surface, LOCAL); g_pInputManager->m_bEmptyFocusCursorSet = false; - } else if (!layerSurface->current.interactivity && (g_pCompositor->m_sSeat.mouse.expired() || !g_pInputManager->isConstrained()) && keyboardExclusive) { + } else if (!layerSurface->current.interactivity && (g_pSeatManager->mouse.expired() || !g_pInputManager->isConstrained()) && keyboardExclusive) { g_pInputManager->refocus(); } diff --git a/src/desktop/WLSurface.cpp b/src/desktop/WLSurface.cpp index c09d8fda..78b50d45 100644 --- a/src/desktop/WLSurface.cpp +++ b/src/desktop/WLSurface.cpp @@ -120,8 +120,6 @@ void CWLSurface::destroy() { if (g_pCompositor && g_pCompositor->m_pLastFocus == m_pWLRSurface) g_pCompositor->m_pLastFocus = nullptr; - if (g_pInputManager && g_pInputManager->m_pLastMouseSurface == m_pWLRSurface) - g_pInputManager->m_pLastMouseSurface = nullptr; if (g_pHyprRenderer && g_pHyprRenderer->m_sLastCursorData.surf == this) g_pHyprRenderer->m_sLastCursorData.surf.reset(); diff --git a/src/events/Devices.cpp b/src/events/Devices.cpp index 16a64559..fedc844e 100644 --- a/src/events/Devices.cpp +++ b/src/events/Devices.cpp @@ -15,12 +15,6 @@ // // // ---------------------------------------------------- // -void Events::listener_requestMouse(wl_listener* listener, void* data) { - const auto EVENT = (wlr_seat_pointer_request_set_cursor_event*)data; - - g_pInputManager->processMouseRequest(EVENT); -} - void Events::listener_newInput(wl_listener* listener, void* data) { const auto DEVICE = (wlr_input_device*)data; diff --git a/src/events/Events.hpp b/src/events/Events.hpp index 69da2d09..2e627d39 100644 --- a/src/events/Events.hpp +++ b/src/events/Events.hpp @@ -46,7 +46,6 @@ namespace Events { LISTENER(newVirtPtr); // Various - LISTENER(requestMouse); LISTENER(requestSetSel); LISTENER(requestSetPrimarySel); diff --git a/src/events/Misc.cpp b/src/events/Misc.cpp index a67a6753..703dba6a 100644 --- a/src/events/Misc.cpp +++ b/src/events/Misc.cpp @@ -26,13 +26,13 @@ void Events::listener_leaseRequest(wl_listener* listener, void* data) { } void Events::listener_requestSetPrimarySel(wl_listener* listener, void* data) { - const auto EVENT = (wlr_seat_request_set_primary_selection_event*)data; - wlr_seat_set_primary_selection(g_pCompositor->m_sSeat.seat, EVENT->source, EVENT->serial); + // const auto EVENT = (wlr_seat_request_set_primary_selection_event*)data; + // wlr_seat_set_primary_selection(g_pCompositor->m_sSeat.seat, EVENT->source, EVENT->serial); } void Events::listener_requestSetSel(wl_listener* listener, void* data) { - const auto EVENT = (wlr_seat_request_set_selection_event*)data; - wlr_seat_set_selection(g_pCompositor->m_sSeat.seat, EVENT->source, EVENT->serial); + // const auto EVENT = (wlr_seat_request_set_selection_event*)data; + // wlr_seat_set_selection(g_pCompositor->m_sSeat.seat, EVENT->source, EVENT->serial); } void Events::listener_readyXWayland(wl_listener* listener, void* data) { @@ -58,7 +58,7 @@ void Events::listener_readyXWayland(wl_listener* listener, void* data) { free(reply); } - wlr_xwayland_set_seat(g_pXWaylandManager->m_sWLRXWayland, g_pCompositor->m_sSeat.seat); + //wlr_xwayland_set_seat(g_pXWaylandManager->m_sWLRXWayland, g_pCompositor->m_sSeat.seat); g_pCursorManager->setXWaylandCursor(g_pXWaylandManager->m_sWLRXWayland); @@ -80,15 +80,15 @@ void Events::listener_readyXWayland(wl_listener* listener, void* data) { } void Events::listener_requestDrag(wl_listener* listener, void* data) { - const auto E = (wlr_seat_request_start_drag_event*)data; + // const auto E = (wlr_seat_request_start_drag_event*)data; - if (!wlr_seat_validate_pointer_grab_serial(g_pCompositor->m_sSeat.seat, E->origin, E->serial)) { - Debug::log(LOG, "Ignoring drag and drop request: serial mismatch."); - wlr_data_source_destroy(E->drag->source); - return; - } + // if (!wlr_seat_validate_pointer_grab_serial(g_pCompositor->m_sSeat.seat, E->origin, E->serial)) { + // Debug::log(LOG, "Ignoring drag and drop request: serial mismatch."); + // wlr_data_source_destroy(E->drag->source); + // return; + // } - wlr_seat_start_pointer_drag(g_pCompositor->m_sSeat.seat, E->drag, E->serial); + // wlr_seat_start_pointer_drag(g_pCompositor->m_sSeat.seat, E->drag, E->serial); } void Events::listener_startDrag(wl_listener* listener, void* data) { diff --git a/src/events/Windows.cpp b/src/events/Windows.cpp index 7e0e3569..b85ed6e1 100644 --- a/src/events/Windows.cpp +++ b/src/events/Windows.cpp @@ -4,6 +4,7 @@ #include "../helpers/WLClasses.hpp" #include "../managers/input/InputManager.hpp" #include "../managers/TokenManager.hpp" +#include "../managers/SeatManager.hpp" #include "../render/Renderer.hpp" #include "../config/ConfigValue.hpp" #include "../protocols/LayerShell.hpp" @@ -668,7 +669,7 @@ void Events::listener_mapWindow(void* owner, void* data) { g_pCompositor->setPreferredScaleForSurface(PWINDOW->m_pWLSurface.wlr(), PMONITOR->scale); g_pCompositor->setPreferredTransformForSurface(PWINDOW->m_pWLSurface.wlr(), PMONITOR->transform); - if (g_pCompositor->m_sSeat.mouse.expired() || !g_pInputManager->isConstrained()) + if (g_pSeatManager->mouse.expired() || !g_pInputManager->isConstrained()) g_pInputManager->sendMotionEventsToFocused(); // fix some xwayland apps that don't behave nicely diff --git a/src/helpers/WLClasses.hpp b/src/helpers/WLClasses.hpp index bc07f6ba..7b24de7d 100644 --- a/src/helpers/WLClasses.hpp +++ b/src/helpers/WLClasses.hpp @@ -58,14 +58,6 @@ struct SExtensionFindingData { wlr_surface** found; }; -struct SSeat { - wlr_seat* seat = nullptr; - wl_client* exclusiveClient = nullptr; - - WP mouse; - WP keyboard; -}; - struct SDrag { wlr_drag* drag = nullptr; diff --git a/src/includes.hpp b/src/includes.hpp index 844a9173..c75796b9 100644 --- a/src/includes.hpp +++ b/src/includes.hpp @@ -51,7 +51,6 @@ extern "C" { #include #include #include -#include #include #include #include diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp index c5d7067d..ead83fa8 100644 --- a/src/managers/KeybindManager.cpp +++ b/src/managers/KeybindManager.cpp @@ -6,6 +6,7 @@ #include "TokenManager.hpp" #include "../protocols/ShortcutsInhibit.hpp" #include "../devices/IKeyboard.hpp" +#include "../managers/SeatManager.hpp" #include #include @@ -522,7 +523,7 @@ void CKeybindManager::onSwitchOffEvent(const std::string& switchName) { int repeatKeyHandler(void* data) { SKeybind** ppActiveKeybind = (SKeybind**)data; - if (!*ppActiveKeybind || g_pCompositor->m_sSeat.keyboard.expired()) + if (!*ppActiveKeybind || g_pSeatManager->keyboard.expired()) return 0; const auto DISPATCHER = g_pKeybindManager->m_mDispatchers.find((*ppActiveKeybind)->handler); @@ -530,16 +531,13 @@ int repeatKeyHandler(void* data) { Debug::log(LOG, "Keybind repeat triggered, calling dispatcher."); DISPATCHER->second((*ppActiveKeybind)->arg); - wl_event_source_timer_update(g_pKeybindManager->m_pActiveKeybindEventSource, 1000 / g_pCompositor->m_sSeat.keyboard->repeatRate); + wl_event_source_timer_update(g_pKeybindManager->m_pActiveKeybindEventSource, 1000 / g_pSeatManager->keyboard->wlr()->repeat_info.rate); return 0; } bool CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWithMods& key, bool pressed) { - bool found = false; - - if (g_pCompositor->m_sSeat.exclusiveClient) - Debug::log(LOG, "Keybind handling only locked (inhibitor)"); + bool found = false; static auto PDISABLEINHIBIT = CConfigValue("binds:disable_keybind_grabbing"); @@ -558,8 +556,7 @@ bool CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWi if (!k.locked && g_pSessionLockManager->isSessionLocked()) continue; - if (!IGNORECONDITIONS && - ((modmask != k.modmask && !k.ignoreMods) || (g_pCompositor->m_sSeat.exclusiveClient && !k.locked) || k.submap != m_szCurrentSelectedSubmap || k.shadowed)) + if (!IGNORECONDITIONS && ((modmask != k.modmask && !k.ignoreMods) || k.submap != m_szCurrentSelectedSubmap || k.shadowed)) continue; if (!key.keyName.empty()) { @@ -653,7 +650,7 @@ bool CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWi m_pActiveKeybind = &k; m_pActiveKeybindEventSource = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, repeatKeyHandler, &m_pActiveKeybind); - const auto PACTIVEKEEB = g_pCompositor->m_sSeat.keyboard.lock(); + const auto PACTIVEKEEB = g_pSeatManager->keyboard.lock(); wl_event_source_timer_update(m_pActiveKeybindEventSource, PACTIVEKEEB->repeatDelay); } @@ -1901,47 +1898,42 @@ void CKeybindManager::pass(std::string regexp) { return; } - const auto KEYBOARD = wlr_seat_get_keyboard(g_pCompositor->m_sSeat.seat); - - if (!KEYBOARD) { + if (!g_pSeatManager->keyboard) { Debug::log(ERR, "No kb in pass?"); return; } - const auto XWTOXW = PWINDOW->m_bIsX11 && g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_bIsX11; - const auto SL = Vector2D(g_pCompositor->m_sSeat.seat->pointer_state.sx, g_pCompositor->m_sSeat.seat->pointer_state.sy); - uint32_t keycodes[32] = {0}; - const auto LASTSRF = g_pCompositor->m_pLastFocus; + const auto XWTOXW = PWINDOW->m_bIsX11 && g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_bIsX11; + const auto LASTSRF = g_pCompositor->m_pLastFocus; // pass all mf shit if (!XWTOXW) { if (g_pKeybindManager->m_uLastCode != 0) - wlr_seat_keyboard_enter(g_pCompositor->m_sSeat.seat, PWINDOW->m_pWLSurface.wlr(), keycodes, 0, &KEYBOARD->modifiers); + g_pSeatManager->setKeyboardFocus(PWINDOW->m_pWLSurface.wlr()); else - wlr_seat_pointer_enter(g_pCompositor->m_sSeat.seat, PWINDOW->m_pWLSurface.wlr(), 1, 1); + g_pSeatManager->setPointerFocus(PWINDOW->m_pWLSurface.wlr(), {1, 1}); } - wlr_keyboard_modifiers kbmods = {g_pInputManager->accumulateModsFromAllKBs(), 0, 0, 0}; - wlr_seat_keyboard_notify_modifiers(g_pCompositor->m_sSeat.seat, &kbmods); + g_pSeatManager->sendKeyboardMods(g_pInputManager->accumulateModsFromAllKBs(), 0, 0, 0); if (g_pKeybindManager->m_iPassPressed == 1) { if (g_pKeybindManager->m_uLastCode != 0) - wlr_seat_keyboard_notify_key(g_pCompositor->m_sSeat.seat, g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastCode - 8, WLR_BUTTON_PRESSED); + g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastCode - 8, WL_KEYBOARD_KEY_STATE_PRESSED); else - wlr_seat_pointer_notify_button(g_pCompositor->m_sSeat.seat, g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastMouseCode, WL_POINTER_BUTTON_STATE_PRESSED); + g_pSeatManager->sendPointerButton(g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastMouseCode, WL_POINTER_BUTTON_STATE_PRESSED); } else if (g_pKeybindManager->m_iPassPressed == 0) if (g_pKeybindManager->m_uLastCode != 0) - wlr_seat_keyboard_notify_key(g_pCompositor->m_sSeat.seat, g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastCode - 8, WLR_BUTTON_RELEASED); + g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastCode - 8, WL_KEYBOARD_KEY_STATE_RELEASED); else - wlr_seat_pointer_notify_button(g_pCompositor->m_sSeat.seat, g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastMouseCode, WL_POINTER_BUTTON_STATE_RELEASED); + g_pSeatManager->sendPointerButton(g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastMouseCode, WL_POINTER_BUTTON_STATE_RELEASED); else { // dynamic call of the dispatcher if (g_pKeybindManager->m_uLastCode != 0) { - wlr_seat_keyboard_notify_key(g_pCompositor->m_sSeat.seat, g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastCode - 8, WLR_BUTTON_PRESSED); - wlr_seat_keyboard_notify_key(g_pCompositor->m_sSeat.seat, g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastCode - 8, WLR_BUTTON_RELEASED); + g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastCode - 8, WL_KEYBOARD_KEY_STATE_PRESSED); + g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastCode - 8, WL_KEYBOARD_KEY_STATE_RELEASED); } else { - wlr_seat_pointer_notify_button(g_pCompositor->m_sSeat.seat, g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastMouseCode, WL_POINTER_BUTTON_STATE_PRESSED); - wlr_seat_pointer_notify_button(g_pCompositor->m_sSeat.seat, g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastMouseCode, WL_POINTER_BUTTON_STATE_RELEASED); + g_pSeatManager->sendPointerButton(g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastMouseCode, WL_POINTER_BUTTON_STATE_PRESSED); + g_pSeatManager->sendPointerButton(g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastMouseCode, WL_POINTER_BUTTON_STATE_RELEASED); } } @@ -1949,22 +1941,24 @@ void CKeybindManager::pass(std::string regexp) { return; // Massive hack: - // this will make wlroots NOT send the leave event to XWayland apps, provided we are not on an XWayland window already. + // this will make g_pSeatManager NOT send the leave event to XWayland apps, provided we are not on an XWayland window already. // please kill me if (PWINDOW->m_bIsX11) { if (g_pKeybindManager->m_uLastCode != 0) { - g_pCompositor->m_sSeat.seat->keyboard_state.focused_client = nullptr; - g_pCompositor->m_sSeat.seat->keyboard_state.focused_surface = nullptr; + g_pSeatManager->state.keyboardFocus = nullptr; + g_pSeatManager->state.keyboardFocusResource.reset(); } else { - g_pCompositor->m_sSeat.seat->pointer_state.focused_client = nullptr; - g_pCompositor->m_sSeat.seat->pointer_state.focused_surface = nullptr; + g_pSeatManager->state.pointerFocus = nullptr; + g_pSeatManager->state.pointerFocusResource.reset(); } } + const auto SL = PWINDOW->m_vRealPosition.goal() - g_pInputManager->getMouseCoordsInternal(); + if (g_pKeybindManager->m_uLastCode != 0) - wlr_seat_keyboard_enter(g_pCompositor->m_sSeat.seat, LASTSRF, KEYBOARD->keycodes, KEYBOARD->num_keycodes, &KEYBOARD->modifiers); + g_pSeatManager->setKeyboardFocus(LASTSRF); else - wlr_seat_pointer_enter(g_pCompositor->m_sSeat.seat, PWINDOW->m_pWLSurface.wlr(), SL.x, SL.y); + g_pSeatManager->setPointerFocus(PWINDOW->m_pWLSurface.wlr(), SL); } void CKeybindManager::layoutmsg(std::string msg) { diff --git a/src/managers/PointerManager.cpp b/src/managers/PointerManager.cpp index 3ddd7c23..60d11e7f 100644 --- a/src/managers/PointerManager.cpp +++ b/src/managers/PointerManager.cpp @@ -3,6 +3,7 @@ #include "../config/ConfigValue.hpp" #include "../protocols/PointerGestures.hpp" #include "../protocols/FractionalScale.hpp" +#include "SeatManager.hpp" #include #include #include @@ -774,7 +775,7 @@ void CPointerManager::attachPointer(SP pointer) { }); listener->frame = pointer->pointerEvents.frame.registerListener([this] (std::any e) { - wlr_seat_pointer_notify_frame(g_pCompositor->m_sSeat.seat); + g_pSeatManager->sendPointerFrame(); }); listener->swipeBegin = pointer->pointerEvents.swipeBegin.registerListener([this] (std::any e) { @@ -865,7 +866,7 @@ void CPointerManager::attachTouch(SP touch) { }); listener->frame = touch->touchEvents.frame.registerListener([this] (std::any e) { - wlr_seat_touch_notify_frame(g_pCompositor->m_sSeat.seat); + g_pSeatManager->sendTouchFrame(); }); // clang-format on diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp index 2904ed53..f853e16f 100644 --- a/src/managers/ProtocolManager.cpp +++ b/src/managers/ProtocolManager.cpp @@ -28,9 +28,14 @@ #include "../protocols/Tablet.hpp" #include "../protocols/LayerShell.hpp" #include "../protocols/PresentationTime.hpp" +#include "../protocols/core/Seat.hpp" CProtocolManager::CProtocolManager() { + // Core + PROTO::seat = std::make_unique(&wl_seat_interface, 9, "WLSeat"); + + // Extensions PROTO::tearing = std::make_unique(&wp_tearing_control_manager_v1_interface, 1, "TearingControl"); PROTO::fractional = std::make_unique(&wp_fractional_scale_manager_v1_interface, 1, "FractionalScale"); PROTO::xdgOutput = std::make_unique(&zxdg_output_manager_v1_interface, 3, "XDGOutput"); diff --git a/src/managers/SeatManager.cpp b/src/managers/SeatManager.cpp new file mode 100644 index 00000000..f9d394b5 --- /dev/null +++ b/src/managers/SeatManager.cpp @@ -0,0 +1,391 @@ +#include "SeatManager.hpp" +#include "../protocols/core/Seat.hpp" +#include "../Compositor.hpp" +#include "../devices/IKeyboard.hpp" +#include + +CSeatManager::CSeatManager() { + listeners.newSeatResource = PROTO::seat->events.newSeatResource.registerListener([this](std::any res) { onNewSeatResource(std::any_cast>(res)); }); +} + +CSeatManager::SSeatResourceContainer::SSeatResourceContainer(SP res) { + resource = res; + listeners.destroy = res->events.destroy.registerListener( + [this](std::any data) { std::erase_if(g_pSeatManager->seatResources, [this](const auto& e) { return e->resource.expired() || e->resource == resource; }); }); +} + +void CSeatManager::onNewSeatResource(SP resource) { + seatResources.emplace_back(makeShared(resource)); +} + +SP CSeatManager::containerForResource(SP seatResource) { + for (auto& c : seatResources) { + if (c->resource == seatResource) + return c; + } + + return nullptr; +} + +uint32_t CSeatManager::nextSerial(SP seatResource) { + if (!seatResource) + return 0; + + auto container = containerForResource(seatResource); + + ASSERT(container); + + auto serial = wl_display_next_serial(g_pCompositor->m_sWLDisplay); + + container->serials.emplace_back(serial); + + if (container->serials.size() > MAX_SERIAL_STORE_LEN) + container->serials.erase(container->serials.begin()); + + return serial; +} + +bool CSeatManager::serialValid(SP seatResource, uint32_t serial) { + if (!seatResource) + return false; + + auto container = containerForResource(seatResource); + + ASSERT(container); + + for (auto it = container->serials.begin(); it != container->serials.end(); ++it) { + if (*it == serial) { + container->serials.erase(it); + return true; + } + } + + return false; +} + +void CSeatManager::updateCapabilities(uint32_t capabilities) { + PROTO::seat->updateCapabilities(capabilities); +} + +void CSeatManager::setMouse(SP MAUZ) { + if (mouse == MAUZ) + return; + + mouse = MAUZ; +} + +void CSeatManager::setKeyboard(SP KEEB) { + if (keyboard == KEEB) + return; + + if (keyboard) + keyboard->active = false; + keyboard = KEEB; + + if (KEEB) { + KEEB->active = true; + PROTO::seat->updateRepeatInfo(KEEB->wlr()->repeat_info.rate, KEEB->wlr()->repeat_info.delay); + } + PROTO::seat->updateKeymap(); +} + +void CSeatManager::setKeyboardFocus(wlr_surface* surf) { + if (state.keyboardFocus == surf) + return; + + if (!keyboard || !keyboard->wlr()) { + Debug::log(ERR, "BUG THIS: setKeyboardFocus without a valid keyboard set"); + return; + } + + hyprListener_keyboardSurfaceDestroy.removeCallback(); + + if (state.keyboardFocusResource) { + for (auto& k : state.keyboardFocusResource->keyboards) { + if (!k) + continue; + + k->sendLeave(); + } + } + + state.keyboardFocusResource.reset(); + state.keyboardFocus = surf; + + if (!surf) { + events.keyboardFocusChange.emit(); + return; + } + + auto client = wl_resource_get_client(surf->resource); + for (auto& r : seatResources) { + if (r->resource->client() == client) { + state.keyboardFocusResource = r->resource; + for (auto& k : state.keyboardFocusResource->keyboards) { + if (!k) + continue; + + k->sendEnter(surf); + k->sendMods(keyboard->wlr()->modifiers.depressed, keyboard->wlr()->modifiers.latched, keyboard->wlr()->modifiers.locked, keyboard->wlr()->modifiers.group); + } + + break; + } + } + + hyprListener_keyboardSurfaceDestroy.initCallback( + &surf->events.destroy, [this](void* owner, void* data) { setKeyboardFocus(nullptr); }, nullptr, "CSeatManager"); + + events.keyboardFocusChange.emit(); +} + +void CSeatManager::sendKeyboardKey(uint32_t timeMs, uint32_t key, wl_keyboard_key_state state_) { + if (!state.keyboardFocusResource) + return; + + for (auto& k : state.keyboardFocusResource->keyboards) { + if (!k) + continue; + + k->sendKey(timeMs, key, state_); + } +} + +void CSeatManager::sendKeyboardMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) { + if (!state.keyboardFocusResource) + return; + + for (auto& k : state.keyboardFocusResource->keyboards) { + if (!k) + continue; + + k->sendMods(depressed, latched, locked, group); + } +} + +void CSeatManager::setPointerFocus(wlr_surface* surf, const Vector2D& local) { + if (state.pointerFocus == surf) + return; + + if (!mouse || !mouse->wlr()) { + Debug::log(ERR, "BUG THIS: setPointerFocus without a valid mouse set"); + return; + } + + hyprListener_pointerSurfaceDestroy.removeCallback(); + + if (state.pointerFocusResource) { + for (auto& p : state.pointerFocusResource->pointers) { + if (!p) + continue; + + p->sendLeave(); + } + } + + state.pointerFocusResource.reset(); + state.pointerFocus = surf; + + if (!surf) { + events.pointerFocusChange.emit(); + return; + } + + auto client = wl_resource_get_client(surf->resource); + for (auto& r : seatResources) { + if (r->resource->client() == client) { + state.pointerFocusResource = r->resource; + for (auto& p : state.pointerFocusResource->pointers) { + if (!p) + continue; + + p->sendEnter(surf, local); + } + + break; + } + } + + hyprListener_pointerSurfaceDestroy.initCallback( + &surf->events.destroy, [this](void* owner, void* data) { setPointerFocus(nullptr, {}); }, nullptr, "CSeatManager"); + + events.pointerFocusChange.emit(); +} + +void CSeatManager::sendPointerMotion(uint32_t timeMs, const Vector2D& local) { + if (!state.pointerFocusResource) + return; + + for (auto& p : state.pointerFocusResource->pointers) { + if (!p) + continue; + + p->sendMotion(timeMs, local); + } +} + +void CSeatManager::sendPointerButton(uint32_t timeMs, uint32_t key, wl_pointer_button_state state_) { + if (!state.pointerFocusResource) + return; + + for (auto& p : state.pointerFocusResource->pointers) { + if (!p) + continue; + + p->sendButton(timeMs, key, state_); + } +} + +void CSeatManager::sendPointerFrame() { + if (!state.pointerFocusResource) + return; + + for (auto& p : state.pointerFocusResource->pointers) { + if (!p) + continue; + + p->sendFrame(); + } +} + +void CSeatManager::sendPointerAxis(uint32_t timeMs, wl_pointer_axis axis, double value, int32_t discrete, wl_pointer_axis_source source, + wl_pointer_axis_relative_direction relative) { + if (!state.pointerFocusResource) + return; + + for (auto& p : state.pointerFocusResource->pointers) { + if (!p) + continue; + + p->sendAxis(timeMs, axis, value); + p->sendAxisSource(source); + p->sendAxisRelativeDirection(axis, relative); + } +} + +void CSeatManager::sendTouchDown(wlr_surface* surf, uint32_t timeMs, int32_t id, const Vector2D& local) { + if (state.touchFocus == surf) + return; + + hyprListener_touchSurfaceDestroy.removeCallback(); + + if (state.touchFocusResource) { + for (auto& t : state.touchFocusResource->touches) { + if (!t) + continue; + + t->sendUp(timeMs, id); + } + } + + state.touchFocusResource.reset(); + state.touchFocus = surf; + + if (!surf) { + events.touchFocusChange.emit(); + return; + } + + auto client = wl_resource_get_client(surf->resource); + for (auto& r : seatResources) { + if (r->resource->client() == client) { + state.touchFocusResource = r->resource; + for (auto& t : state.touchFocusResource->touches) { + if (!t) + continue; + + t->sendDown(surf, timeMs, id, local); + } + + break; + } + } + + hyprListener_touchSurfaceDestroy.initCallback( + &surf->events.destroy, [this, timeMs, id](void* owner, void* data) { sendTouchUp(timeMs + 10, id); }, nullptr, "CSeatManager"); + + events.touchFocusChange.emit(); +} + +void CSeatManager::sendTouchUp(uint32_t timeMs, int32_t id) { + sendTouchDown(nullptr, timeMs, id, {}); +} + +void CSeatManager::sendTouchMotion(uint32_t timeMs, int32_t id, const Vector2D& local) { + if (!state.touchFocusResource) + return; + + for (auto& t : state.touchFocusResource->touches) { + if (!t) + continue; + + t->sendMotion(timeMs, id, local); + } +} + +void CSeatManager::sendTouchFrame() { + if (!state.touchFocusResource) + return; + + for (auto& t : state.touchFocusResource->touches) { + if (!t) + continue; + + t->sendFrame(); + } +} + +void CSeatManager::sendTouchCancel() { + if (!state.touchFocusResource) + return; + + for (auto& t : state.touchFocusResource->touches) { + if (!t) + continue; + + t->sendCancel(); + } +} + +void CSeatManager::sendTouchShape(int32_t id, const Vector2D& shape) { + if (!state.touchFocusResource) + return; + + for (auto& t : state.touchFocusResource->touches) { + if (!t) + continue; + + t->sendShape(id, shape); + } +} + +void CSeatManager::sendTouchOrientation(int32_t id, double angle) { + if (!state.touchFocusResource) + return; + + for (auto& t : state.touchFocusResource->touches) { + if (!t) + continue; + + t->sendOrientation(id, angle); + } +} + +void CSeatManager::onSetCursor(SP seatResource, uint32_t serial, wlr_surface* surf, const Vector2D& hotspot) { + if (!state.pointerFocusResource || !seatResource || seatResource->client() != state.pointerFocusResource->client()) { + Debug::log(LOG, "[seatmgr] Rejecting a setCursor because the client ain't in focus"); + return; + } + + // TODO: fix this. Probably should be done in the CWlPointer as the serial could be lost by us. + // if (!serialValid(seatResource, serial)) { + // Debug::log(LOG, "[seatmgr] Rejecting a setCursor because the serial is invalid"); + // return; + // } + + events.setCursor.emit(SSetCursorEvent{surf, hotspot}); +} + +SP CSeatManager::seatResourceForClient(wl_client* client) { + return PROTO::seat->seatResourceForClient(client); +} diff --git a/src/managers/SeatManager.hpp b/src/managers/SeatManager.hpp new file mode 100644 index 00000000..ef114024 --- /dev/null +++ b/src/managers/SeatManager.hpp @@ -0,0 +1,106 @@ +#pragma once + +#include +#include "../helpers/WLListener.hpp" +#include "../macros.hpp" +#include "../helpers/signal/Signal.hpp" +#include "../helpers/Vector2D.hpp" +#include + +constexpr size_t MAX_SERIAL_STORE_LEN = 100; + +struct wlr_surface; +class CWLSeatResource; +class IPointer; +class IKeyboard; + +class CSeatManager { + public: + CSeatManager(); + + void updateCapabilities(uint32_t capabilities); // in IHID caps + + void setMouse(SP mouse); + void setKeyboard(SP keeb); + + void setKeyboardFocus(wlr_surface* surf); + void sendKeyboardKey(uint32_t timeMs, uint32_t key, wl_keyboard_key_state state); + void sendKeyboardMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group); + + void setPointerFocus(wlr_surface* surf, const Vector2D& local); + void sendPointerMotion(uint32_t timeMs, const Vector2D& local); + void sendPointerButton(uint32_t timeMs, uint32_t key, wl_pointer_button_state state); + void sendPointerFrame(); + void sendPointerAxis(uint32_t timeMs, wl_pointer_axis axis, double value, int32_t discrete, wl_pointer_axis_source source, wl_pointer_axis_relative_direction relative); + + void sendTouchDown(wlr_surface* surf, uint32_t timeMs, int32_t id, const Vector2D& local); + void sendTouchUp(uint32_t timeMs, int32_t id); + void sendTouchMotion(uint32_t timeMs, int32_t id, const Vector2D& local); + void sendTouchFrame(); + void sendTouchCancel(); + void sendTouchShape(int32_t id, const Vector2D& shape); + void sendTouchOrientation(int32_t id, double angle); + + uint32_t nextSerial(SP seatResource); + // pops the serial if it was valid, meaning it is consumed. + bool serialValid(SP seatResource, uint32_t serial); + + void onSetCursor(SP seatResource, uint32_t serial, wlr_surface* surf, const Vector2D& hotspot); + + SP seatResourceForClient(wl_client* client); + + struct { + wlr_surface* keyboardFocus = nullptr; + WP keyboardFocusResource; + + wlr_surface* pointerFocus = nullptr; + WP pointerFocusResource; + + wlr_surface* touchFocus = nullptr; + WP touchFocusResource; + } state; + + struct SSetCursorEvent { + wlr_surface* surf = nullptr; + Vector2D hotspot; + }; + + struct { + CSignal keyboardFocusChange; + CSignal pointerFocusChange; + CSignal touchFocusChange; + CSignal setCursor; // SSetCursorEvent + } events; + + // do not write to directly, use set... + WP mouse; + WP keyboard; + + private: + struct SSeatResourceContainer { + SSeatResourceContainer(SP); + + WP resource; + std::vector serials; // old -> new + + struct { + CHyprSignalListener destroy; + } listeners; + }; + + std::vector> seatResources; + void onNewSeatResource(SP resource); + SP containerForResource(SP seatResource); + + struct { + CHyprSignalListener newSeatResource; + } listeners; + + DYNLISTENER(keyboardSurfaceDestroy); + DYNLISTENER(pointerSurfaceDestroy); + DYNLISTENER(touchSurfaceDestroy); + + friend struct SSeatResourceContainer; +}; + +inline UP g_pSeatManager; diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index 958fb255..62d9e687 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -14,6 +14,7 @@ #include "../../protocols/VirtualKeyboard.hpp" #include "../../protocols/VirtualPointer.hpp" #include "../../protocols/LayerShell.hpp" +#include "../../protocols/core/Seat.hpp" #include "../../devices/Mouse.hpp" #include "../../devices/VirtualPointer.hpp" @@ -22,6 +23,7 @@ #include "../../devices/TouchDevice.hpp" #include "../../managers/PointerManager.hpp" +#include "../../managers/SeatManager.hpp" CInputManager::CInputManager() { m_sListeners.setCursorShape = PROTO::cursorShape->events.setShape.registerListener([this](std::any data) { @@ -30,10 +32,10 @@ CInputManager::CInputManager() { auto event = std::any_cast(data); - if (!g_pCompositor->m_sSeat.seat->pointer_state.focused_client) + if (!g_pSeatManager->state.pointerFocusResource) return; - if (wl_resource_get_client(event.pMgr->resource()) != g_pCompositor->m_sSeat.seat->pointer_state.focused_client->client) + if (wl_resource_get_client(event.pMgr->resource()) != g_pSeatManager->state.pointerFocusResource->client()) return; Debug::log(LOG, "cursorImage request: shape {} -> {}", (uint32_t)event.shape, event.shapeName); @@ -52,6 +54,7 @@ CInputManager::CInputManager() { PROTO::virtualKeyboard->events.newKeyboard.registerListener([this](std::any data) { this->newVirtualKeyboard(std::any_cast>(data)); }); m_sListeners.newVirtualMouse = PROTO::virtualPointer->events.newPointer.registerListener([this](std::any data) { this->newVirtualMouse(std::any_cast>(data)); }); + m_sListeners.setCursor = g_pSeatManager->events.setCursor.registerListener([this](std::any d) { this->processMouseRequest(d); }); } CInputManager::~CInputManager() { @@ -119,8 +122,7 @@ void CInputManager::sendMotionEventsToFocused() { m_bEmptyFocusCursorSet = false; - wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, g_pCompositor->m_pLastFocus, LOCAL.x, LOCAL.y); - wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, now.tv_sec * 1000 + now.tv_nsec / 10000000, LOCAL.x, LOCAL.y); + g_pSeatManager->setPointerFocus(g_pCompositor->m_pLastFocus, LOCAL); } void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { @@ -190,7 +192,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { } // constraints - if (!g_pCompositor->m_sSeat.mouse.expired() && isConstrained()) { + if (!g_pSeatManager->mouse.expired() && isConstrained()) { const auto SURF = CWLSurface::surfaceFromWlr(g_pCompositor->m_pLastFocus); const auto CONSTRAINT = SURF->constraint(); @@ -205,7 +207,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { const auto CLOSESTLOCAL = (CLOSEST - (BOX.has_value() ? BOX->pos() : Vector2D{})) * (SURF->getWindow() ? SURF->getWindow()->m_fX11SurfaceScaledBy : 1.0); g_pCompositor->warpCursorTo(CLOSEST, true); - wlr_seat_pointer_send_motion(g_pCompositor->m_sSeat.seat, time, CLOSESTLOCAL.x, CLOSESTLOCAL.y); + g_pSeatManager->sendPointerMotion(time, CLOSESTLOCAL); PROTO::relativePointer->sendRelativeMotion((uint64_t)time * 1000, {}, {}); } @@ -218,8 +220,8 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { // update stuff updateDragIcon(); - if (!m_sDrag.drag && !m_lCurrentlyHeldButtons.empty() && g_pCompositor->m_pLastFocus && m_pLastMouseSurface) { - foundSurface = m_pLastMouseSurface; + if (!m_sDrag.drag && !m_lCurrentlyHeldButtons.empty() && g_pCompositor->m_pLastFocus && g_pSeatManager->state.pointerFocus) { + foundSurface = g_pSeatManager->state.pointerFocus; pFoundLayerSurface = g_pCompositor->getLayerSurfaceFromSurface(foundSurface); if (pFoundLayerSurface) { surfacePos = pFoundLayerSurface->position; @@ -232,7 +234,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { m_bFocusHeldByButtons = true; m_bRefocusHeldByButtons = refocus; } else if (!g_pCompositor->m_pLastWindow.expired()) { - foundSurface = m_pLastMouseSurface; + foundSurface = g_pSeatManager->state.pointerFocus; pFoundWindow = g_pCompositor->m_pLastWindow.lock(); surfaceCoords = g_pCompositor->vectorToSurfaceLocal(mouseCoords, pFoundWindow, foundSurface); @@ -371,8 +373,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { m_bEmptyFocusCursorSet = true; } - wlr_seat_pointer_clear_focus(g_pCompositor->m_sSeat.seat); - m_pLastMouseSurface = nullptr; + g_pSeatManager->setPointerFocus(nullptr, {}); if (refocus || g_pCompositor->m_pLastWindow.expired()) // if we are forcing a refocus, and we don't find a surface, clear the kb focus too! g_pCompositor->focusWindow(nullptr); @@ -412,8 +413,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { } if (currentlyDraggedWindow.lock() && pFoundWindow != currentlyDraggedWindow) { - wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, foundSurface, surfaceLocal.x, surfaceLocal.y); - wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, time, surfaceLocal.x, surfaceLocal.y); + g_pSeatManager->setPointerFocus(foundSurface, surfaceLocal); return; } @@ -441,20 +441,15 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { // enter if change floating style if (FOLLOWMOUSE != 3 && allowKeyboardRefocus) g_pCompositor->focusWindow(pFoundWindow, foundSurface); - m_pLastMouseSurface = foundSurface; - wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, foundSurface, surfaceLocal.x, surfaceLocal.y); - } else if (FOLLOWMOUSE == 2 || FOLLOWMOUSE == 3) { - m_pLastMouseSurface = foundSurface; - wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, foundSurface, surfaceLocal.x, surfaceLocal.y); - } + g_pSeatManager->setPointerFocus(foundSurface, surfaceLocal); + } else if (FOLLOWMOUSE == 2 || FOLLOWMOUSE == 3) + g_pSeatManager->setPointerFocus(foundSurface, surfaceLocal); - if (pFoundWindow == g_pCompositor->m_pLastWindow) { - m_pLastMouseSurface = foundSurface; - wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, foundSurface, surfaceLocal.x, surfaceLocal.y); - } + if (pFoundWindow == g_pCompositor->m_pLastWindow) + g_pSeatManager->setPointerFocus(foundSurface, surfaceLocal); if (FOLLOWMOUSE != 0 || pFoundWindow == g_pCompositor->m_pLastWindow) - wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, time, surfaceLocal.x, surfaceLocal.y); + g_pSeatManager->setPointerFocus(foundSurface, surfaceLocal); m_bLastFocusOnLS = false; return; // don't enter any new surfaces @@ -489,9 +484,8 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { m_bLastFocusOnLS = true; } - m_pLastMouseSurface = foundSurface; - wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, foundSurface, surfaceLocal.x, surfaceLocal.y); - wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, time, surfaceLocal.x, surfaceLocal.y); + g_pSeatManager->setPointerFocus(foundSurface, surfaceLocal); + g_pSeatManager->sendPointerMotion(time, surfaceLocal); } void CInputManager::onMouseButton(IPointer::SButtonEvent e) { @@ -526,34 +520,33 @@ void CInputManager::onMouseButton(IPointer::SButtonEvent e) { } } -void CInputManager::processMouseRequest(wlr_seat_pointer_request_set_cursor_event* e) { +void CInputManager::processMouseRequest(std::any E) { if (!cursorImageUnlocked()) return; - Debug::log(LOG, "cursorImage request: surface {:x}", (uintptr_t)e->surface); + auto e = std::any_cast(E); - if (e->seat_client == g_pCompositor->m_sSeat.seat->pointer_state.focused_client) { + Debug::log(LOG, "cursorImage request: surface {:x}", (uintptr_t)e.surf); - if (e->surface != m_sCursorSurfaceInfo.wlSurface.wlr()) { - m_sCursorSurfaceInfo.wlSurface.unassign(); + if (e.surf != m_sCursorSurfaceInfo.wlSurface.wlr()) { + m_sCursorSurfaceInfo.wlSurface.unassign(); - if (e->surface) - m_sCursorSurfaceInfo.wlSurface.assign(e->surface); - } - - if (e->surface) { - m_sCursorSurfaceInfo.vHotspot = {e->hotspot_x, e->hotspot_y}; - m_sCursorSurfaceInfo.hidden = false; - } else { - m_sCursorSurfaceInfo.vHotspot = {}; - m_sCursorSurfaceInfo.hidden = true; - } - - m_sCursorSurfaceInfo.name = ""; - - m_sCursorSurfaceInfo.inUse = true; - g_pHyprRenderer->setCursorSurface(&m_sCursorSurfaceInfo.wlSurface, e->hotspot_x, e->hotspot_y); + if (e.surf) + m_sCursorSurfaceInfo.wlSurface.assign(e.surf); } + + if (e.surf) { + m_sCursorSurfaceInfo.vHotspot = e.hotspot; + m_sCursorSurfaceInfo.hidden = false; + } else { + m_sCursorSurfaceInfo.vHotspot = {}; + m_sCursorSurfaceInfo.hidden = true; + } + + m_sCursorSurfaceInfo.name = ""; + + m_sCursorSurfaceInfo.inUse = true; + g_pHyprRenderer->setCursorSurface(&m_sCursorSurfaceInfo.wlSurface, e.hotspot.x, e.hotspot.y); } void CInputManager::restoreCursorIconToApp() { @@ -659,7 +652,7 @@ void CInputManager::processMouseDownNormal(const IPointer::SButtonEvent& e) { if (*PFOLLOWMOUSE == 3) // don't refocus on full loose break; - if ((g_pCompositor->m_sSeat.mouse.expired() || !isConstrained()) /* No constraints */ + if ((g_pSeatManager->mouse.expired() || !isConstrained()) /* No constraints */ && (w && g_pCompositor->m_pLastWindow.lock() != w) /* window should change */) { // a bit hacky // if we only pressed one button, allow us to refocus. m_lCurrentlyHeldButtons.size() > 0 will stick the focus @@ -681,8 +674,7 @@ void CInputManager::processMouseDownNormal(const IPointer::SButtonEvent& e) { } // notify app if we didnt handle it - if (g_pCompositor->doesSeatAcceptInput(g_pCompositor->m_pLastFocus)) - wlr_seat_pointer_notify_button(g_pCompositor->m_sSeat.seat, e.timeMs, e.button, e.state); + g_pSeatManager->sendPointerButton(e.timeMs, e.button, e.state); if (const auto PMON = g_pCompositor->getMonitorFromVector(mouseCoords); PMON != g_pCompositor->m_pLastMonitor.get() && PMON) g_pCompositor->setActiveMonitor(PMON); @@ -747,14 +739,13 @@ void CInputManager::onMouseWheel(IPointer::SAxisEvent e) { if (*POFFWINDOWAXIS == 3) g_pCompositor->warpCursorTo({TEMPCURX, TEMPCURY}, true); - wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, e.timeMs, TEMPCURX - BOX.x, TEMPCURY - BOX.y); - wlr_seat_pointer_notify_frame(g_pCompositor->m_sSeat.seat); + g_pSeatManager->sendPointerMotion(e.timeMs, Vector2D{TEMPCURX, TEMPCURY} - BOX.pos()); + g_pSeatManager->sendPointerFrame(); } } } - wlr_seat_pointer_notify_axis(g_pCompositor->m_sSeat.seat, e.timeMs, e.axis, factor * e.delta, std::round(factor * e.deltaDiscrete), e.source, - WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL); + g_pSeatManager->sendPointerAxis(e.timeMs, e.axis, factor * e.delta, std::round(factor * e.deltaDiscrete), e.source, WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL); } Vector2D CInputManager::getMouseCoordsInternal() { @@ -826,13 +817,9 @@ void CInputManager::setupKeyboard(SP keeb) { disableAllKeyboards(false); - g_pCompositor->m_sSeat.keyboard = keeb; - - keeb->active = true; - applyConfigToKeyboard(keeb); - wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, keeb->wlr()); + g_pSeatManager->setKeyboard(keeb); } void CInputManager::setKeyboardLayout() { @@ -1015,7 +1002,7 @@ void CInputManager::setupMouse(SP mauz) { }, mauz.get()); - g_pCompositor->m_sSeat.mouse = mauz; + g_pSeatManager->setMouse(mauz); m_tmrLastCursorMovement.reset(); } @@ -1185,12 +1172,11 @@ void CInputManager::destroyKeyboard(SP pKeyboard) { std::erase_if(m_vKeyboards, [pKeyboard](const auto& other) { return other == pKeyboard; }); if (m_vKeyboards.size() > 0) { - g_pCompositor->m_sSeat.keyboard = m_vKeyboards.back(); - g_pCompositor->m_sSeat.keyboard->active = true; - wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, g_pCompositor->m_sSeat.keyboard->wlr()); + const auto PNEWKEYBOARD = m_vKeyboards.back(); + g_pSeatManager->setKeyboard(PNEWKEYBOARD); + PNEWKEYBOARD->active = true; } else { - g_pCompositor->m_sSeat.keyboard.reset(); - wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, nullptr); + g_pSeatManager->setKeyboard(nullptr); } removeFromHIDs(pKeyboard); @@ -1199,9 +1185,9 @@ void CInputManager::destroyKeyboard(SP pKeyboard) { void CInputManager::destroyPointer(SP mouse) { std::erase_if(m_vPointers, [mouse](const auto& other) { return other == mouse; }); - g_pCompositor->m_sSeat.mouse = m_vPointers.size() > 0 ? m_vPointers.front() : nullptr; + g_pSeatManager->setMouse(m_vPointers.size() > 0 ? m_vPointers.front() : nullptr); - if (!g_pCompositor->m_sSeat.mouse.expired()) + if (!g_pSeatManager->mouse.expired()) unconstrainMouse(); removeFromHIDs(mouse); @@ -1287,8 +1273,8 @@ void CInputManager::onKeyboardKey(std::any event, SP pKeyboard) { IME->setKeyboard(pKeyboard->wlr()); IME->sendKey(e.timeMs, e.keycode, e.state); } else { - wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, pKeyboard->wlr()); - wlr_seat_keyboard_notify_key(g_pCompositor->m_sSeat.seat, e.timeMs, e.keycode, e.state); + g_pSeatManager->setKeyboard(pKeyboard); + g_pSeatManager->sendKeyboardKey(e.timeMs, e.keycode, e.state); } updateKeyboardsLeds(pKeyboard); @@ -1313,8 +1299,8 @@ void CInputManager::onKeyboardMod(SP pKeyboard) { IME->setKeyboard(PWLRKB); IME->sendMods(MODS.depressed, MODS.latched, MODS.locked, MODS.group); } else { - wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, PWLRKB); - wlr_seat_keyboard_notify_modifiers(g_pCompositor->m_sSeat.seat, &MODS); + g_pSeatManager->setKeyboard(pKeyboard); + g_pSeatManager->sendKeyboardMods(MODS.depressed, MODS.latched, MODS.locked, MODS.group); } updateKeyboardsLeds(pKeyboard); @@ -1361,7 +1347,7 @@ void CInputManager::updateDragIcon() { } void CInputManager::unconstrainMouse() { - if (g_pCompositor->m_sSeat.mouse.expired()) + if (g_pSeatManager->mouse.expired()) return; for (auto& c : m_vConstraints) { @@ -1410,7 +1396,7 @@ void CInputManager::updateCapabilities() { caps |= WL_SEAT_CAPABILITY_TOUCH; } - wlr_seat_set_capabilities(g_pCompositor->m_sSeat.seat, caps); + g_pSeatManager->updateCapabilities(caps); m_uiCapabilities = caps; } @@ -1422,7 +1408,7 @@ uint32_t CInputManager::accumulateModsFromAllKBs() { if (kb->isVirtual() && shouldIgnoreVirtualKeyboard(kb)) continue; - if (!kb->enabled) + if (!kb->enabled || !kb->wlr()) continue; finalMask |= wlr_keyboard_get_modifiers(kb->wlr()); @@ -1657,7 +1643,7 @@ void CInputManager::releaseAllMouseButtons() { return; for (auto& mb : buttonsCopy) { - wlr_seat_pointer_notify_button(g_pCompositor->m_sSeat.seat, 0, mb, WL_POINTER_BUTTON_STATE_RELEASED); + g_pSeatManager->sendPointerButton(0, mb, WL_POINTER_BUTTON_STATE_RELEASED); } m_lCurrentlyHeldButtons.clear(); diff --git a/src/managers/input/InputManager.hpp b/src/managers/input/InputManager.hpp index df36b975..42c9d3da 100644 --- a/src/managers/input/InputManager.hpp +++ b/src/managers/input/InputManager.hpp @@ -118,7 +118,7 @@ class CInputManager { void setClickMode(eClickBehaviorMode); eClickBehaviorMode getClickMode(); - void processMouseRequest(wlr_seat_pointer_request_set_cursor_event* e); + void processMouseRequest(std::any e); void onTouchDown(ITouch::SDownEvent); void onTouchUp(ITouch::SUpEvent); @@ -197,7 +197,6 @@ class CInputManager { // for tracking mouse refocus PHLWINDOWREF m_pLastMouseFocus; - wlr_surface* m_pLastMouseSurface = nullptr; // bool m_bEmptyFocusCursorSet = false; @@ -209,6 +208,7 @@ class CInputManager { CHyprSignalListener newIdleInhibitor; CHyprSignalListener newVirtualKeyboard; CHyprSignalListener newVirtualMouse; + CHyprSignalListener setCursor; } m_sListeners; bool m_bCursorImageOverridden = false; diff --git a/src/managers/input/Tablets.cpp b/src/managers/input/Tablets.cpp index cf666ec6..56e817ec 100644 --- a/src/managers/input/Tablets.cpp +++ b/src/managers/input/Tablets.cpp @@ -4,6 +4,7 @@ #include "../../protocols/Tablet.hpp" #include "../../devices/Tablet.hpp" #include "../../managers/PointerManager.hpp" +#include "../../managers/SeatManager.hpp" #include "../../protocols/PointerConstraints.hpp" static void unfocusTool(SP tool) { @@ -36,7 +37,7 @@ static void focusTool(SP tool, SP tablet, wlr_surface* sur } static void refocusTablet(SP tab, SP tool, bool motion = false) { - const auto LASTHLSURFACE = CWLSurface::surfaceFromWlr(g_pInputManager->m_pLastMouseSurface); + const auto LASTHLSURFACE = CWLSurface::surfaceFromWlr(g_pSeatManager->state.pointerFocus); if (!LASTHLSURFACE || !tool->active) { if (tool->getSurface()) @@ -56,7 +57,7 @@ static void refocusTablet(SP tab, SP tool, bool motion = f const auto CURSORPOS = g_pInputManager->getMouseCoordsInternal(); - focusTool(tool, tab, g_pInputManager->m_pLastMouseSurface); + focusTool(tool, tab, g_pSeatManager->state.pointerFocus); if (!motion) return; diff --git a/src/managers/input/Touch.cpp b/src/managers/input/Touch.cpp index 4a6484c2..7748813b 100644 --- a/src/managers/input/Touch.cpp +++ b/src/managers/input/Touch.cpp @@ -3,6 +3,7 @@ #include "../../config/ConfigValue.hpp" #include "../../protocols/IdleNotify.hpp" #include "../../devices/ITouch.hpp" +#include "../SeatManager.hpp" void CInputManager::onTouchDown(ITouch::SDownEvent e) { static auto PSWIPETOUCH = CConfigValue("gestures:workspace_swipe_touch"); @@ -76,7 +77,7 @@ void CInputManager::onTouchDown(ITouch::SDownEvent e) { } else return; // oops, nothing found. - wlr_seat_touch_notify_down(g_pCompositor->m_sSeat.seat, m_sTouchData.touchFocusSurface, e.timeMs, e.touchID, local.x, local.y); + g_pSeatManager->sendTouchDown(m_sTouchData.touchFocusSurface, e.timeMs, e.touchID, local); PROTO::idle->onActivity(); } @@ -90,9 +91,8 @@ void CInputManager::onTouchUp(ITouch::SUpEvent e) { return; } - if (m_sTouchData.touchFocusSurface) { - wlr_seat_touch_notify_up(g_pCompositor->m_sSeat.seat, e.timeMs, e.touchID); - } + if (m_sTouchData.touchFocusSurface) + g_pSeatManager->sendTouchUp(e.timeMs, e.touchID); } void CInputManager::onTouchMove(ITouch::SMotionEvent e) { @@ -131,8 +131,7 @@ void CInputManager::onTouchMove(ITouch::SMotionEvent e) { if (m_sTouchData.touchFocusWindow->m_bIsX11) local = local * m_sTouchData.touchFocusWindow->m_fX11SurfaceScaledBy; - wlr_seat_touch_notify_motion(g_pCompositor->m_sSeat.seat, e.timeMs, e.touchID, local.x, local.y); - // wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, e->time_msec, local.x, local.y); + g_pSeatManager->sendTouchMotion(e.timeMs, e.touchID, local); } else if (!m_sTouchData.touchFocusLS.expired()) { const auto PMONITOR = g_pCompositor->getMonitorFromID(m_sTouchData.touchFocusLS->monitorID); @@ -140,7 +139,6 @@ void CInputManager::onTouchMove(ITouch::SMotionEvent e) { const auto local = g_pInputManager->getMouseCoordsInternal() - m_sTouchData.touchSurfaceOrigin; - wlr_seat_touch_notify_motion(g_pCompositor->m_sSeat.seat, e.timeMs, e.touchID, local.x, local.y); - // wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, e->time_msec, local.x, local.y); + g_pSeatManager->sendTouchMotion(e.timeMs, e.touchID, local); } } diff --git a/src/protocols/FocusGrab.cpp b/src/protocols/FocusGrab.cpp index 1ab4d4a6..4189ed6e 100644 --- a/src/protocols/FocusGrab.cpp +++ b/src/protocols/FocusGrab.cpp @@ -1,124 +1,125 @@ #include "FocusGrab.hpp" #include "Compositor.hpp" #include -#include +#include "../managers/input/InputManager.hpp" +#include "../managers/SeatManager.hpp" #include #include #include -static void focus_grab_pointer_enter(wlr_seat_pointer_grab* grab, wlr_surface* surface, double sx, double sy) { - if (static_cast(grab->data)->isSurfaceComitted(surface)) - wlr_seat_pointer_enter(grab->seat, surface, sx, sy); - else - wlr_seat_pointer_clear_focus(grab->seat); -} +// static void focus_grab_pointer_enter(wlr_seat_pointer_grab* grab, wlr_surface* surface, double sx, double sy) { +// if (static_cast(grab->data)->isSurfaceComitted(surface)) +// wlr_seat_pointer_enter(grab->seat, surface, sx, sy); +// else +// wlr_seat_pointer_clear_focus(grab->seat); +// } -static void focus_grab_pointer_clear_focus(wlr_seat_pointer_grab* grab) { - wlr_seat_pointer_clear_focus(grab->seat); -} +// static void focus_grab_pointer_clear_focus(wlr_seat_pointer_grab* grab) { +// wlr_seat_pointer_clear_focus(grab->seat); +// } -static void focus_grab_pointer_motion(wlr_seat_pointer_grab* grab, uint32_t time, double sx, double sy) { - wlr_seat_pointer_send_motion(grab->seat, time, sx, sy); -} +// static void focus_grab_pointer_motion(wlr_seat_pointer_grab* grab, uint32_t time, double sx, double sy) { +// wlr_seat_pointer_send_motion(grab->seat, time, sx, sy); +// } -static uint32_t focus_grab_pointer_button(wlr_seat_pointer_grab* grab, uint32_t time, uint32_t button, wl_pointer_button_state state) { - uint32_t serial = wlr_seat_pointer_send_button(grab->seat, time, button, state); +// static uint32_t focus_grab_pointer_button(wlr_seat_pointer_grab* grab, uint32_t time, uint32_t button, wl_pointer_button_state state) { +// uint32_t serial = wlr_seat_pointer_send_button(grab->seat, time, button, state); - if (serial) - return serial; - else { - static_cast(grab->data)->finish(true); - return 0; - } -} +// if (serial) +// return serial; +// else { +// static_cast(grab->data)->finish(true); +// return 0; +// } +// } -static void focus_grab_pointer_axis(wlr_seat_pointer_grab* grab, uint32_t time, enum wl_pointer_axis orientation, double value, int32_t value_discrete, - enum wl_pointer_axis_source source, enum wl_pointer_axis_relative_direction relative_direction) { - wlr_seat_pointer_send_axis(grab->seat, time, orientation, value, value_discrete, source, relative_direction); -} +// static void focus_grab_pointer_axis(wlr_seat_pointer_grab* grab, uint32_t time, enum wl_pointer_axis orientation, double value, int32_t value_discrete, +// enum wl_pointer_axis_source source, enum wl_pointer_axis_relative_direction relative_direction) { +// wlr_seat_pointer_send_axis(grab->seat, time, orientation, value, value_discrete, source, relative_direction); +// } -static void focus_grab_pointer_frame(wlr_seat_pointer_grab* grab) { - wlr_seat_pointer_send_frame(grab->seat); -} +// static void focus_grab_pointer_frame(wlr_seat_pointer_grab* grab) { +// wlr_seat_pointer_send_frame(grab->seat); +// } -static void focus_grab_pointer_cancel(wlr_seat_pointer_grab* grab) { - static_cast(grab->data)->finish(true); -} +// static void focus_grab_pointer_cancel(wlr_seat_pointer_grab* grab) { +// static_cast(grab->data)->finish(true); +// } -static const wlr_pointer_grab_interface focus_grab_pointer_impl = { - .enter = focus_grab_pointer_enter, - .clear_focus = focus_grab_pointer_clear_focus, - .motion = focus_grab_pointer_motion, - .button = focus_grab_pointer_button, - .axis = focus_grab_pointer_axis, - .frame = focus_grab_pointer_frame, - .cancel = focus_grab_pointer_cancel, -}; +// static const wlr_pointer_grab_interface focus_grab_pointer_impl = { +// .enter = focus_grab_pointer_enter, +// .clear_focus = focus_grab_pointer_clear_focus, +// .motion = focus_grab_pointer_motion, +// .button = focus_grab_pointer_button, +// .axis = focus_grab_pointer_axis, +// .frame = focus_grab_pointer_frame, +// .cancel = focus_grab_pointer_cancel, +// }; -static void focus_grab_keyboard_enter(wlr_seat_keyboard_grab* grab, wlr_surface* surface, const uint32_t keycodes[], size_t num_keycodes, const wlr_keyboard_modifiers* modifiers) { - if (static_cast(grab->data)->isSurfaceComitted(surface)) - wlr_seat_keyboard_enter(grab->seat, surface, keycodes, num_keycodes, modifiers); +// static void focus_grab_keyboard_enter(wlr_seat_keyboard_grab* grab, wlr_surface* surface, const uint32_t keycodes[], size_t num_keycodes, const wlr_keyboard_modifiers* modifiers) { +// if (static_cast(grab->data)->isSurfaceComitted(surface)) +// wlr_seat_keyboard_enter(grab->seat, surface, keycodes, num_keycodes, modifiers); - // otherwise the last grabbed window should retain keyboard focus. -} +// // otherwise the last grabbed window should retain keyboard focus. +// } -static void focus_grab_keyboard_clear_focus(wlr_seat_keyboard_grab* grab) { - static_cast(grab->data)->finish(true); -} +// static void focus_grab_keyboard_clear_focus(wlr_seat_keyboard_grab* grab) { +// static_cast(grab->data)->finish(true); +// } -static void focus_grab_keyboard_key(wlr_seat_keyboard_grab* grab, uint32_t time, uint32_t key, uint32_t state) { - wlr_seat_keyboard_send_key(grab->seat, time, key, state); -} +// static void focus_grab_keyboard_key(wlr_seat_keyboard_grab* grab, uint32_t time, uint32_t key, uint32_t state) { +// wlr_seat_keyboard_send_key(grab->seat, time, key, state); +// } -static void focus_grab_keyboard_modifiers(wlr_seat_keyboard_grab* grab, const wlr_keyboard_modifiers* modifiers) { - wlr_seat_keyboard_send_modifiers(grab->seat, modifiers); -} +// static void focus_grab_keyboard_modifiers(wlr_seat_keyboard_grab* grab, const wlr_keyboard_modifiers* modifiers) { +// wlr_seat_keyboard_send_modifiers(grab->seat, modifiers); +// } -static void focus_grab_keyboard_cancel(wlr_seat_keyboard_grab* grab) { - static_cast(grab->data)->finish(true); -} +// static void focus_grab_keyboard_cancel(wlr_seat_keyboard_grab* grab) { +// static_cast(grab->data)->finish(true); +// } -static const wlr_keyboard_grab_interface focus_grab_keyboard_impl = { - .enter = focus_grab_keyboard_enter, - .clear_focus = focus_grab_keyboard_clear_focus, - .key = focus_grab_keyboard_key, - .modifiers = focus_grab_keyboard_modifiers, - .cancel = focus_grab_keyboard_cancel, -}; +// static const wlr_keyboard_grab_interface focus_grab_keyboard_impl = { +// .enter = focus_grab_keyboard_enter, +// .clear_focus = focus_grab_keyboard_clear_focus, +// .key = focus_grab_keyboard_key, +// .modifiers = focus_grab_keyboard_modifiers, +// .cancel = focus_grab_keyboard_cancel, +// }; -static uint32_t focus_grab_touch_down(wlr_seat_touch_grab* grab, uint32_t time, wlr_touch_point* point) { - if (!static_cast(grab->data)->isSurfaceComitted(point->surface)) - return 0; +// static uint32_t focus_grab_touch_down(wlr_seat_touch_grab* grab, uint32_t time, wlr_touch_point* point) { +// if (!static_cast(grab->data)->isSurfaceComitted(point->surface)) +// return 0; - return wlr_seat_touch_send_down(grab->seat, point->surface, time, point->touch_id, point->sx, point->sy); -} +// return wlr_seat_touch_send_down(grab->seat, point->surface, time, point->touch_id, point->sx, point->sy); +// } -static void focus_grab_touch_up(wlr_seat_touch_grab* grab, uint32_t time, wlr_touch_point* point) { - wlr_seat_touch_send_up(grab->seat, time, point->touch_id); -} +// static void focus_grab_touch_up(wlr_seat_touch_grab* grab, uint32_t time, wlr_touch_point* point) { +// wlr_seat_touch_send_up(grab->seat, time, point->touch_id); +// } -static void focus_grab_touch_motion(wlr_seat_touch_grab* grab, uint32_t time, wlr_touch_point* point) { - wlr_seat_touch_send_motion(grab->seat, time, point->touch_id, point->sx, point->sy); -} +// static void focus_grab_touch_motion(wlr_seat_touch_grab* grab, uint32_t time, wlr_touch_point* point) { +// wlr_seat_touch_send_motion(grab->seat, time, point->touch_id, point->sx, point->sy); +// } -static void focus_grab_touch_enter(wlr_seat_touch_grab* grab, uint32_t time, wlr_touch_point* point) {} +// static void focus_grab_touch_enter(wlr_seat_touch_grab* grab, uint32_t time, wlr_touch_point* point) {} -static void focus_grab_touch_frame(wlr_seat_touch_grab* grab) { - wlr_seat_touch_send_frame(grab->seat); -} +// static void focus_grab_touch_frame(wlr_seat_touch_grab* grab) { +// wlr_seat_touch_send_frame(grab->seat); +// } -static void focus_grab_touch_cancel(wlr_seat_touch_grab* grab) { - static_cast(grab->data)->finish(true); -} +// static void focus_grab_touch_cancel(wlr_seat_touch_grab* grab) { +// static_cast(grab->data)->finish(true); +// } -static const wlr_touch_grab_interface focus_grab_touch_impl = { - .down = focus_grab_touch_down, - .up = focus_grab_touch_up, - .motion = focus_grab_touch_motion, - .enter = focus_grab_touch_enter, - .frame = focus_grab_touch_frame, - .cancel = focus_grab_touch_cancel, -}; +// static const wlr_touch_grab_interface focus_grab_touch_impl = { +// .down = focus_grab_touch_down, +// .up = focus_grab_touch_up, +// .motion = focus_grab_touch_motion, +// .enter = focus_grab_touch_enter, +// .frame = focus_grab_touch_frame, +// .cancel = focus_grab_touch_cancel, +// }; CFocusGrabSurfaceState::CFocusGrabSurfaceState(CFocusGrab* grab, wlr_surface* surface) { hyprListener_surfaceDestroy.initCallback( @@ -133,14 +134,14 @@ CFocusGrab::CFocusGrab(SP resource_) : resource(resource_) if (!resource->resource()) return; - m_sPointerGrab.interface = &focus_grab_pointer_impl; - m_sPointerGrab.data = this; + // m_sPointerGrab.interface = &focus_grab_pointer_impl; + // m_sPointerGrab.data = this; - m_sKeyboardGrab.interface = &focus_grab_keyboard_impl; - m_sKeyboardGrab.data = this; + // m_sKeyboardGrab.interface = &focus_grab_keyboard_impl; + // m_sKeyboardGrab.data = this; - m_sTouchGrab.interface = &focus_grab_touch_impl; - m_sTouchGrab.data = this; + // m_sTouchGrab.interface = &focus_grab_touch_impl; + // m_sTouchGrab.data = this; resource->setDestroy([this](CHyprlandFocusGrabV1* pMgr) { PROTO::focusGrab->destroyGrab(this); }); resource->setOnDestroy([this](CHyprlandFocusGrabV1* pMgr) { PROTO::focusGrab->destroyGrab(this); }); @@ -167,21 +168,21 @@ bool CFocusGrab::isSurfaceComitted(wlr_surface* surface) { void CFocusGrab::start() { if (!m_bGrabActive) { - wlr_seat_pointer_start_grab(g_pCompositor->m_sSeat.seat, &m_sPointerGrab); - wlr_seat_keyboard_start_grab(g_pCompositor->m_sSeat.seat, &m_sKeyboardGrab); - wlr_seat_touch_start_grab(g_pCompositor->m_sSeat.seat, &m_sTouchGrab); + // wlr_seat_pointer_start_grab(g_pCompositor->m_sSeat.seat, &m_sPointerGrab); + // wlr_seat_keyboard_start_grab(g_pCompositor->m_sSeat.seat, &m_sKeyboardGrab); + // wlr_seat_touch_start_grab(g_pCompositor->m_sSeat.seat, &m_sTouchGrab); m_bGrabActive = true; // Ensure the grab ends if another grab begins, including from xdg_popup::grab. - hyprListener_pointerGrabStarted.initCallback( - &g_pCompositor->m_sSeat.seat->events.pointer_grab_begin, [this](void*, void*) { finish(true); }, this, "CFocusGrab"); + // hyprListener_pointerGrabStarted.initCallback( + // &g_pCompositor->m_sSeat.seat->events.pointer_grab_begin, [this](void*, void*) { finish(true); }, this, "CFocusGrab"); - hyprListener_keyboardGrabStarted.initCallback( - &g_pCompositor->m_sSeat.seat->events.keyboard_grab_begin, [this](void*, void*) { finish(true); }, this, "CFocusGrab"); + // hyprListener_keyboardGrabStarted.initCallback( + // &g_pCompositor->m_sSeat.seat->events.keyboard_grab_begin, [this](void*, void*) { finish(true); }, this, "CFocusGrab"); - hyprListener_touchGrabStarted.initCallback( - &g_pCompositor->m_sSeat.seat->events.touch_grab_begin, [this](void*, void*) { finish(true); }, this, "CFocusGrab"); + // hyprListener_touchGrabStarted.initCallback( + // &g_pCompositor->m_sSeat.seat->events.touch_grab_begin, [this](void*, void*) { finish(true); }, this, "CFocusGrab"); } // Ensure new surfaces are focused if under the mouse when comitted. @@ -192,28 +193,28 @@ void CFocusGrab::start() { void CFocusGrab::finish(bool sendCleared) { if (m_bGrabActive) { m_bGrabActive = false; - hyprListener_pointerGrabStarted.removeCallback(); - hyprListener_keyboardGrabStarted.removeCallback(); - hyprListener_touchGrabStarted.removeCallback(); + // hyprListener_pointerGrabStarted.removeCallback(); + // hyprListener_keyboardGrabStarted.removeCallback(); + // hyprListener_touchGrabStarted.removeCallback(); // Only clear grabs that belong to this focus grab. When superseded by another grab // or xdg_popup grab we might not own the current grab. bool hadGrab = false; - if (g_pCompositor->m_sSeat.seat->pointer_state.grab == &m_sPointerGrab) { - wlr_seat_pointer_end_grab(g_pCompositor->m_sSeat.seat); - hadGrab = true; - } + // if (g_pCompositor->m_sSeat.seat->pointer_state.grab == &m_sPointerGrab) { + // wlr_seat_pointer_end_grab(g_pCompositor->m_sSeat.seat); + // hadGrab = true; + // } - if (g_pCompositor->m_sSeat.seat->keyboard_state.grab == &m_sKeyboardGrab) { - wlr_seat_keyboard_end_grab(g_pCompositor->m_sSeat.seat); - hadGrab = true; - } + // if (g_pCompositor->m_sSeat.seat->keyboard_state.grab == &m_sKeyboardGrab) { + // wlr_seat_keyboard_end_grab(g_pCompositor->m_sSeat.seat); + // hadGrab = true; + // } - if (g_pCompositor->m_sSeat.seat->touch_state.grab == &m_sTouchGrab) { - wlr_seat_touch_end_grab(g_pCompositor->m_sSeat.seat); - hadGrab = true; - } + // if (g_pCompositor->m_sSeat.seat->touch_state.grab == &m_sTouchGrab) { + // wlr_seat_touch_end_grab(g_pCompositor->m_sSeat.seat); + // hadGrab = true; + // } m_mSurfaces.clear(); @@ -248,7 +249,7 @@ void CFocusGrab::eraseSurface(wlr_surface* surface) { } void CFocusGrab::refocusKeyboard() { - auto keyboardSurface = g_pCompositor->m_sSeat.seat->keyboard_state.focused_surface; + auto keyboardSurface = g_pSeatManager->state.keyboardFocus; if (keyboardSurface != nullptr && isSurfaceComitted(keyboardSurface)) return; diff --git a/src/protocols/FocusGrab.hpp b/src/protocols/FocusGrab.hpp index 11c26774..3e5d36ab 100644 --- a/src/protocols/FocusGrab.hpp +++ b/src/protocols/FocusGrab.hpp @@ -44,10 +44,10 @@ class CFocusGrab { SP resource; std::unordered_map> m_mSurfaces; - wlr_seat_pointer_grab m_sPointerGrab; - wlr_seat_keyboard_grab m_sKeyboardGrab; - wlr_seat_touch_grab m_sTouchGrab; - bool m_bGrabActive = false; + // wlr_seat_pointer_grab m_sPointerGrab; + // wlr_seat_keyboard_grab m_sKeyboardGrab; + // wlr_seat_touch_grab m_sTouchGrab; + bool m_bGrabActive = false; DYNLISTENER(pointerGrabStarted); DYNLISTENER(keyboardGrabStarted); diff --git a/src/protocols/InputMethodV2.cpp b/src/protocols/InputMethodV2.cpp index 9e5c863b..93ca38c0 100644 --- a/src/protocols/InputMethodV2.cpp +++ b/src/protocols/InputMethodV2.cpp @@ -1,5 +1,7 @@ #include "InputMethodV2.hpp" #include "../Compositor.hpp" +#include "../managers/SeatManager.hpp" +#include "../devices/IKeyboard.hpp" #include #define LOGM PROTO::ime->protoLog @@ -11,14 +13,12 @@ CInputMethodKeyboardGrabV2::CInputMethodKeyboardGrabV2(SPsetRelease([this](CZwpInputMethodKeyboardGrabV2* r) { PROTO::ime->destroyResource(this); }); resource->setOnDestroy([this](CZwpInputMethodKeyboardGrabV2* r) { PROTO::ime->destroyResource(this); }); - const auto PKEYBOARD = wlr_seat_get_keyboard(g_pCompositor->m_sSeat.seat); - - if (!PKEYBOARD) { + if (!g_pSeatManager->keyboard) { LOGM(ERR, "IME called but no active keyboard???"); return; } - sendKeyboardData(PKEYBOARD); + sendKeyboardData(g_pSeatManager->keyboard->wlr()); } CInputMethodKeyboardGrabV2::~CInputMethodKeyboardGrabV2() { @@ -60,13 +60,13 @@ void CInputMethodKeyboardGrabV2::sendKeyboardData(wlr_keyboard* keyboard) { } void CInputMethodKeyboardGrabV2::sendKey(uint32_t time, uint32_t key, wl_keyboard_key_state state) { - const auto SERIAL = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, resource->client())); + const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(resource->client())); resource->sendKey(SERIAL, time, key, (uint32_t)state); } void CInputMethodKeyboardGrabV2::sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) { - const auto SERIAL = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, resource->client())); + const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(resource->client())); resource->sendModifiers(SERIAL, depressed, latched, locked, group); } diff --git a/src/protocols/PointerConstraints.cpp b/src/protocols/PointerConstraints.cpp index 7b5729c0..87abdcdd 100644 --- a/src/protocols/PointerConstraints.cpp +++ b/src/protocols/PointerConstraints.cpp @@ -2,6 +2,7 @@ #include "../desktop/WLSurface.hpp" #include "../Compositor.hpp" #include "../config/ConfigValue.hpp" +#include "../managers/SeatManager.hpp" #define LOGM PROTO::constraints->protoLog @@ -125,10 +126,10 @@ void CPointerConstraint::activate() { return; // TODO: hack, probably not a super duper great idea - if (g_pCompositor->m_sSeat.seat->pointer_state.focused_surface != pHLSurface->wlr()) { + if (g_pSeatManager->state.pointerFocus != pHLSurface->wlr()) { const auto SURFBOX = pHLSurface->getSurfaceBoxGlobal(); const auto LOCAL = SURFBOX.has_value() ? logicPositionHint() - SURFBOX->pos() : Vector2D{}; - wlr_seat_pointer_enter(g_pCompositor->m_sSeat.seat, pHLSurface->wlr(), LOCAL.x, LOCAL.y); + g_pSeatManager->setPointerFocus(pHLSurface->wlr(), LOCAL); } if (locked) diff --git a/src/protocols/PointerGestures.cpp b/src/protocols/PointerGestures.cpp index 5b5d1c52..2bfd74da 100644 --- a/src/protocols/PointerGestures.cpp +++ b/src/protocols/PointerGestures.cpp @@ -1,5 +1,7 @@ #include "PointerGestures.hpp" #include "../Compositor.hpp" +#include "../managers/SeatManager.hpp" +#include "core/Seat.hpp" #define LOGM PROTO::pointerGestures->protoLog @@ -103,26 +105,26 @@ void CPointerGesturesProtocol::onGetHoldGesture(CZwpPointerGesturesV1* pMgr, uin } void CPointerGesturesProtocol::swipeBegin(uint32_t timeMs, uint32_t fingers) { - if (!g_pCompositor->m_sSeat.seat->pointer_state.focused_client) + if (!g_pSeatManager->state.pointerFocusResource) return; - const auto FOCUSEDCLIENT = g_pCompositor->m_sSeat.seat->pointer_state.focused_client->client; + const auto FOCUSEDCLIENT = g_pSeatManager->state.pointerFocusResource->client(); - const auto SERIAL = wlr_seat_client_next_serial(g_pCompositor->m_sSeat.seat->pointer_state.focused_client); + const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->state.pointerFocusResource.lock()); for (auto& sw : m_vSwipes) { if (sw->resource->client() != FOCUSEDCLIENT) continue; - sw->resource->sendBegin(SERIAL, timeMs, g_pCompositor->m_sSeat.seat->pointer_state.focused_surface->resource, fingers); + sw->resource->sendBegin(SERIAL, timeMs, g_pSeatManager->state.pointerFocus->resource, fingers); } } void CPointerGesturesProtocol::swipeUpdate(uint32_t timeMs, const Vector2D& delta) { - if (!g_pCompositor->m_sSeat.seat->pointer_state.focused_client) + if (!g_pSeatManager->state.pointerFocusResource) return; - const auto FOCUSEDCLIENT = g_pCompositor->m_sSeat.seat->pointer_state.focused_client->client; + const auto FOCUSEDCLIENT = g_pSeatManager->state.pointerFocusResource->client(); for (auto& sw : m_vSwipes) { if (sw->resource->client() != FOCUSEDCLIENT) @@ -133,12 +135,12 @@ void CPointerGesturesProtocol::swipeUpdate(uint32_t timeMs, const Vector2D& delt } void CPointerGesturesProtocol::swipeEnd(uint32_t timeMs, bool cancelled) { - if (!g_pCompositor->m_sSeat.seat->pointer_state.focused_client) + if (!g_pSeatManager->state.pointerFocusResource) return; - const auto FOCUSEDCLIENT = g_pCompositor->m_sSeat.seat->pointer_state.focused_client->client; + const auto FOCUSEDCLIENT = g_pSeatManager->state.pointerFocusResource->client(); - const auto SERIAL = wlr_seat_client_next_serial(g_pCompositor->m_sSeat.seat->pointer_state.focused_client); + const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->state.pointerFocusResource.lock()); for (auto& sw : m_vSwipes) { if (sw->resource->client() != FOCUSEDCLIENT) @@ -149,26 +151,26 @@ void CPointerGesturesProtocol::swipeEnd(uint32_t timeMs, bool cancelled) { } void CPointerGesturesProtocol::pinchBegin(uint32_t timeMs, uint32_t fingers) { - if (!g_pCompositor->m_sSeat.seat->pointer_state.focused_client) + if (!g_pSeatManager->state.pointerFocusResource) return; - const auto FOCUSEDCLIENT = g_pCompositor->m_sSeat.seat->pointer_state.focused_client->client; + const auto FOCUSEDCLIENT = g_pSeatManager->state.pointerFocusResource->client(); - const auto SERIAL = wlr_seat_client_next_serial(g_pCompositor->m_sSeat.seat->pointer_state.focused_client); + const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->state.pointerFocusResource.lock()); for (auto& sw : m_vPinches) { if (sw->resource->client() != FOCUSEDCLIENT) continue; - sw->resource->sendBegin(SERIAL, timeMs, g_pCompositor->m_sSeat.seat->pointer_state.focused_surface->resource, fingers); + sw->resource->sendBegin(SERIAL, timeMs, g_pSeatManager->state.pointerFocus->resource, fingers); } } void CPointerGesturesProtocol::pinchUpdate(uint32_t timeMs, const Vector2D& delta, double scale, double rotation) { - if (!g_pCompositor->m_sSeat.seat->pointer_state.focused_client) + if (!g_pSeatManager->state.pointerFocusResource) return; - const auto FOCUSEDCLIENT = g_pCompositor->m_sSeat.seat->pointer_state.focused_client->client; + const auto FOCUSEDCLIENT = g_pSeatManager->state.pointerFocusResource->client(); for (auto& sw : m_vPinches) { if (sw->resource->client() != FOCUSEDCLIENT) @@ -179,12 +181,12 @@ void CPointerGesturesProtocol::pinchUpdate(uint32_t timeMs, const Vector2D& delt } void CPointerGesturesProtocol::pinchEnd(uint32_t timeMs, bool cancelled) { - if (!g_pCompositor->m_sSeat.seat->pointer_state.focused_client) + if (!g_pSeatManager->state.pointerFocusResource) return; - const auto FOCUSEDCLIENT = g_pCompositor->m_sSeat.seat->pointer_state.focused_client->client; + const auto FOCUSEDCLIENT = g_pSeatManager->state.pointerFocusResource->client(); - const auto SERIAL = wlr_seat_client_next_serial(g_pCompositor->m_sSeat.seat->pointer_state.focused_client); + const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->state.pointerFocusResource.lock()); for (auto& sw : m_vPinches) { if (sw->resource->client() != FOCUSEDCLIENT) @@ -195,28 +197,28 @@ void CPointerGesturesProtocol::pinchEnd(uint32_t timeMs, bool cancelled) { } void CPointerGesturesProtocol::holdBegin(uint32_t timeMs, uint32_t fingers) { - if (!g_pCompositor->m_sSeat.seat->pointer_state.focused_client) + if (!g_pSeatManager->state.pointerFocusResource) return; - const auto FOCUSEDCLIENT = g_pCompositor->m_sSeat.seat->pointer_state.focused_client->client; + const auto FOCUSEDCLIENT = g_pSeatManager->state.pointerFocusResource->client(); - const auto SERIAL = wlr_seat_client_next_serial(g_pCompositor->m_sSeat.seat->pointer_state.focused_client); + const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->state.pointerFocusResource.lock()); for (auto& sw : m_vHolds) { if (sw->resource->client() != FOCUSEDCLIENT) continue; - sw->resource->sendBegin(SERIAL, timeMs, g_pCompositor->m_sSeat.seat->pointer_state.focused_surface->resource, fingers); + sw->resource->sendBegin(SERIAL, timeMs, g_pSeatManager->state.pointerFocus->resource, fingers); } } void CPointerGesturesProtocol::holdEnd(uint32_t timeMs, bool cancelled) { - if (!g_pCompositor->m_sSeat.seat->pointer_state.focused_client) + if (!g_pSeatManager->state.pointerFocusResource) return; - const auto FOCUSEDCLIENT = g_pCompositor->m_sSeat.seat->pointer_state.focused_client->client; + const auto FOCUSEDCLIENT = g_pSeatManager->state.pointerFocusResource->client(); - const auto SERIAL = wlr_seat_client_next_serial(g_pCompositor->m_sSeat.seat->pointer_state.focused_client); + const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->state.pointerFocusResource.lock()); for (auto& sw : m_vHolds) { if (sw->resource->client() != FOCUSEDCLIENT) diff --git a/src/protocols/RelativePointer.cpp b/src/protocols/RelativePointer.cpp index c64687ee..e4818758 100644 --- a/src/protocols/RelativePointer.cpp +++ b/src/protocols/RelativePointer.cpp @@ -1,5 +1,7 @@ #include "RelativePointer.hpp" -#include "Compositor.hpp" +#include "../Compositor.hpp" +#include "../managers/SeatManager.hpp" +#include "core/Seat.hpp" #include CRelativePointer::CRelativePointer(SP resource_) : resource(resource_) { @@ -58,10 +60,10 @@ void CRelativePointerProtocol::onGetRelativePointer(CZwpRelativePointerManagerV1 void CRelativePointerProtocol::sendRelativeMotion(uint64_t time, const Vector2D& delta, const Vector2D& deltaUnaccel) { - if (!g_pCompositor->m_sSeat.seat->pointer_state.focused_client) + if (!g_pSeatManager->state.pointerFocusResource) return; - const auto FOCUSED = g_pCompositor->m_sSeat.seat->pointer_state.focused_client->client; + const auto FOCUSED = g_pSeatManager->state.pointerFocusResource->client(); for (auto& rp : m_vRelativePointers) { if (FOCUSED != rp->client()) diff --git a/src/protocols/SessionLock.cpp b/src/protocols/SessionLock.cpp index db3404be..fd803eda 100644 --- a/src/protocols/SessionLock.cpp +++ b/src/protocols/SessionLock.cpp @@ -1,5 +1,6 @@ #include "SessionLock.hpp" #include "../Compositor.hpp" +#include "../managers/SeatManager.hpp" #include "FractionalScale.hpp" #define LOGM PROTO::sessionLock->protoLog @@ -76,7 +77,7 @@ CSessionLockSurface::~CSessionLockSurface() { } void CSessionLockSurface::sendConfigure() { - const auto SERIAL = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, resource->client())); + const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(resource->client())); resource->sendConfigure(SERIAL, pMonitor->vecSize.x, pMonitor->vecSize.y); } diff --git a/src/protocols/Tablet.cpp b/src/protocols/Tablet.cpp index b3ca76f5..7c54a116 100644 --- a/src/protocols/Tablet.cpp +++ b/src/protocols/Tablet.cpp @@ -1,6 +1,8 @@ #include "Tablet.hpp" #include "../devices/Tablet.hpp" #include "../Compositor.hpp" +#include "../managers/SeatManager.hpp" +#include "core/Seat.hpp" #include #define LOGM PROTO::tablet->protoLog @@ -159,12 +161,10 @@ CTabletToolV2Resource::CTabletToolV2Resource(SP resource_, SP< resource->setOnDestroy([this](CZwpTabletToolV2* r) { PROTO::tablet->destroyResource(this); }); resource->setSetCursor([this](CZwpTabletToolV2* r, uint32_t serial, wl_resource* surf, int32_t hot_x, int32_t hot_y) { - wlr_seat_pointer_request_set_cursor_event e; - e.hotspot_x = hot_x; - e.hotspot_y = hot_y; - e.surface = surf ? wlr_surface_from_resource(surf) : nullptr; - e.serial = serial; - g_pInputManager->processMouseRequest(&e); + if (!g_pSeatManager->state.pointerFocusResource || g_pSeatManager->state.pointerFocusResource->client() != r->client()) + return; + + g_pInputManager->processMouseRequest(CSeatManager::SSetCursorEvent{wlr_surface_from_resource(surf), {hot_x, hot_y}}); }); } @@ -539,7 +539,7 @@ void CTabletV2Protocol::down(SP tool) { if (t->tool != tool || !t->current) continue; - auto serial = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, t->resource->client())); + auto serial = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(t->resource->client())); t->resource->sendDown(serial); t->queueFrame(); } @@ -586,7 +586,7 @@ void CTabletV2Protocol::proximityIn(SP tool, SP tablet, wl toolResource->current = true; toolResource->lastSurf = surf; - auto serial = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, toolResource->resource->client())); + auto serial = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(toolResource->resource->client())); toolResource->resource->sendProximityIn(serial, tabletResource->resource.get(), surf->resource); toolResource->queueFrame(); @@ -610,7 +610,7 @@ void CTabletV2Protocol::buttonTool(SP tool, uint32_t button, uint32 if (t->tool != tool || !t->current) continue; - auto serial = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, t->resource->client())); + auto serial = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(t->resource->client())); t->resource->sendButton(serial, button, (zwpTabletToolV2ButtonState)state); t->queueFrame(); } @@ -634,7 +634,7 @@ void CTabletV2Protocol::mode(SP pad, uint32_t group, uint32_t mode, LOGM(ERR, "BUG THIS: group >= t->groups.size()"); return; } - auto serial = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, t->resource->client())); + auto serial = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(t->resource->client())); t->groups.at(group)->resource->sendModeSwitch(timeMs, serial, mode); } } diff --git a/src/protocols/core/Seat.cpp b/src/protocols/core/Seat.cpp new file mode 100644 index 00000000..489fced7 --- /dev/null +++ b/src/protocols/core/Seat.cpp @@ -0,0 +1,437 @@ +#include "Seat.hpp" +#include "../../devices/IKeyboard.hpp" +#include "../../managers/SeatManager.hpp" +#include "../../config/ConfigValue.hpp" +#include + +#include + +#define LOGM PROTO::seat->protoLog + +CWLTouchResource::CWLTouchResource(SP resource_, SP owner_) : owner(owner_), resource(resource_) { + if (!good()) + return; + + resource->setRelease([this](CWlTouch* r) { PROTO::seat->destroyResource(this); }); + resource->setOnDestroy([this](CWlTouch* r) { PROTO::seat->destroyResource(this); }); +} + +bool CWLTouchResource::good() { + return resource->resource(); +} + +void CWLTouchResource::sendDown(wlr_surface* surface, uint32_t timeMs, int32_t id, const Vector2D& local) { + if (!owner) + return; + + if (currentSurface) { + LOGM(WARN, "requested CWLTouchResource::sendDown without sendUp first."); + sendUp(timeMs, id); + } + + ASSERT(wl_resource_get_client(surface->resource) == owner->client()); + + currentSurface = surface; + hyprListener_surfaceDestroy.initCallback( + &surface->events.destroy, [this, id, timeMs](void* owner, void* data) { sendUp(timeMs + 10 /* hack */, id); }, this, "CWLTouchResource"); + + // FIXME: + // fix this once we get our own wlr_surface, this is horrible + resource->sendDownRaw(g_pSeatManager->nextSerial(owner.lock()), timeMs, surface->resource, id, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y)); +} + +void CWLTouchResource::sendUp(uint32_t timeMs, int32_t id) { + if (!owner || !currentSurface) + return; + + resource->sendUp(g_pSeatManager->nextSerial(owner.lock()), timeMs, id); + currentSurface = nullptr; + hyprListener_surfaceDestroy.removeCallback(); +} + +void CWLTouchResource::sendMotion(uint32_t timeMs, int32_t id, const Vector2D& local) { + if (!owner || !currentSurface) + return; + + resource->sendMotion(timeMs, id, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y)); +} + +void CWLTouchResource::sendFrame() { + if (!owner || !currentSurface) + return; + + resource->sendFrame(); +} + +void CWLTouchResource::sendCancel() { + if (!owner || !currentSurface) + return; + + resource->sendCancel(); +} + +void CWLTouchResource::sendShape(int32_t id, const Vector2D& shape) { + if (!owner || !currentSurface || resource->version() < 6) + return; + + resource->sendShape(id, wl_fixed_from_double(shape.x), wl_fixed_from_double(shape.y)); +} + +void CWLTouchResource::sendOrientation(int32_t id, double angle) { + if (!owner || !currentSurface || resource->version() < 6) + return; + + resource->sendOrientation(id, wl_fixed_from_double(angle)); +} + +CWLPointerResource::CWLPointerResource(SP resource_, SP owner_) : owner(owner_), resource(resource_) { + if (!good()) + return; + + resource->setRelease([this](CWlPointer* r) { PROTO::seat->destroyResource(this); }); + resource->setOnDestroy([this](CWlPointer* r) { PROTO::seat->destroyResource(this); }); + + resource->setSetCursor([this](CWlPointer* r, uint32_t serial, wl_resource* surf, int32_t hotX, int32_t hotY) { + if (!owner) { + LOGM(ERR, "Client bug: setCursor when seatClient is already dead"); + return; + } + + g_pSeatManager->onSetCursor(owner.lock(), serial, surf ? wlr_surface_from_resource(surf) : nullptr, {hotX, hotY}); + }); +} + +bool CWLPointerResource::good() { + return resource->resource(); +} + +void CWLPointerResource::sendEnter(wlr_surface* surface, const Vector2D& local) { + if (!owner || currentSurface == surface) + return; + + if (currentSurface) { + LOGM(WARN, "requested CWLPointerResource::sendEnter without sendLeave first."); + sendLeave(); + } + + ASSERT(wl_resource_get_client(surface->resource) == owner->client()); + + currentSurface = surface; + hyprListener_surfaceDestroy.initCallback( + &surface->events.destroy, [this](void* owner, void* data) { sendLeave(); }, this, "CWLPointerResource"); + + resource->sendEnterRaw(g_pSeatManager->nextSerial(owner.lock()), surface->resource, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y)); +} + +void CWLPointerResource::sendLeave() { + if (!owner || !currentSurface) + return; + + resource->sendLeaveRaw(g_pSeatManager->nextSerial(owner.lock()), currentSurface->resource); + currentSurface = nullptr; + hyprListener_surfaceDestroy.removeCallback(); +} + +void CWLPointerResource::sendMotion(uint32_t timeMs, const Vector2D& local) { + if (!owner || !currentSurface) + return; + + resource->sendMotion(timeMs, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y)); +} + +void CWLPointerResource::sendButton(uint32_t timeMs, uint32_t button, wl_pointer_button_state state) { + if (!owner || !currentSurface) + return; + + resource->sendButton(g_pSeatManager->nextSerial(owner.lock()), timeMs, button, state); +} + +void CWLPointerResource::sendAxis(uint32_t timeMs, wl_pointer_axis axis, double value) { + if (!owner || !currentSurface) + return; + + resource->sendAxis(timeMs, axis, wl_fixed_from_double(value)); +} + +void CWLPointerResource::sendFrame() { + if (!owner || resource->version() < 5) + return; + + resource->sendFrame(); +} + +void CWLPointerResource::sendAxisSource(wl_pointer_axis_source source) { + if (!owner || !currentSurface || resource->version() < 5) + return; + + resource->sendAxisSource(source); +} + +void CWLPointerResource::sendAxisStop(uint32_t timeMs, wl_pointer_axis axis) { + if (!owner || !currentSurface || resource->version() < 5) + return; + + resource->sendAxisStop(timeMs, axis); +} + +void CWLPointerResource::sendAxisDiscrete(wl_pointer_axis axis, int32_t discrete) { + if (!owner || !currentSurface || resource->version() < 5) + return; + + resource->sendAxisDiscrete(axis, discrete); +} + +void CWLPointerResource::sendAxisValue120(wl_pointer_axis axis, int32_t value120) { + if (!owner || !currentSurface || resource->version() < 8) + return; + + resource->sendAxisValue120(axis, value120); +} + +void CWLPointerResource::sendAxisRelativeDirection(wl_pointer_axis axis, wl_pointer_axis_relative_direction direction) { + if (!owner || !currentSurface || resource->version() < 9) + return; + + resource->sendAxisRelativeDirection(axis, direction); +} + +CWLKeyboardResource::CWLKeyboardResource(SP resource_, SP owner_) : owner(owner_), resource(resource_) { + if (!good()) + return; + + resource->setRelease([this](CWlKeyboard* r) { PROTO::seat->destroyResource(this); }); + resource->setOnDestroy([this](CWlKeyboard* r) { PROTO::seat->destroyResource(this); }); + + static auto REPEAT = CConfigValue("input:repeat_rate"); + static auto DELAY = CConfigValue("input:repeat_delay"); + sendKeymap(g_pSeatManager->keyboard.lock()); + repeatInfo(*REPEAT, *DELAY); +} + +bool CWLKeyboardResource::good() { + return resource->resource(); +} + +void CWLKeyboardResource::sendKeymap(SP keyboard) { + wl_keyboard_keymap_format format = keyboard ? WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1 : WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP; + int fd; + uint32_t size; + if (keyboard) { + fd = keyboard->wlr()->keymap_fd; + size = keyboard->wlr()->keymap_size; + } else { + fd = open("/dev/null", O_RDONLY | O_CLOEXEC); + if (fd < 0) { + LOGM(ERR, "Failed to open /dev/null"); + return; + } + size = 0; + } + + resource->sendKeymap(format, fd, size); + + if (!keyboard) + close(fd); +} + +void CWLKeyboardResource::sendEnter(wlr_surface* surface) { + if (!owner || currentSurface == surface) + return; + + if (currentSurface) { + LOGM(WARN, "requested CWLKeyboardResource::sendEnter without sendLeave first."); + sendLeave(); + } + + ASSERT(wl_resource_get_client(surface->resource) == owner->client()); + + currentSurface = surface; + hyprListener_surfaceDestroy.initCallback( + &surface->events.destroy, [this](void* owner, void* data) { sendLeave(); }, this, "CWLKeyboardResource"); + + wl_array arr; + wl_array_init(&arr); + + resource->sendEnterRaw(g_pSeatManager->nextSerial(owner.lock()), surface->resource, &arr); + + wl_array_release(&arr); +} + +void CWLKeyboardResource::sendLeave() { + if (!owner || !currentSurface) + return; + + resource->sendLeaveRaw(g_pSeatManager->nextSerial(owner.lock()), currentSurface->resource); + currentSurface = nullptr; + hyprListener_surfaceDestroy.removeCallback(); +} + +void CWLKeyboardResource::sendKey(uint32_t timeMs, uint32_t key, wl_keyboard_key_state state) { + if (!owner || !currentSurface) + return; + + resource->sendKey(g_pSeatManager->nextSerial(owner.lock()), timeMs, key, state); +} + +void CWLKeyboardResource::sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) { + if (!owner || !currentSurface) + return; + + resource->sendModifiers(g_pSeatManager->nextSerial(owner.lock()), depressed, latched, locked, group); +} + +void CWLKeyboardResource::repeatInfo(uint32_t rate, uint32_t delayMs) { + if (!owner || resource->version() < 4) + return; + + resource->sendRepeatInfo(rate, delayMs); +} + +CWLSeatResource::CWLSeatResource(SP resource_) : resource(resource_) { + if (!good()) + return; + + resource->setOnDestroy([this](CWlSeat* r) { + events.destroy.emit(); + PROTO::seat->destroyResource(this); + }); + resource->setRelease([this](CWlSeat* r) { + events.destroy.emit(); + PROTO::seat->destroyResource(this); + }); + + pClient = resource->client(); + + resource->setGetKeyboard([this](CWlSeat* r, uint32_t id) { + const auto RESOURCE = PROTO::seat->m_vKeyboards.emplace_back(makeShared(makeShared(r->client(), r->version(), id), self.lock())); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::seat->m_vKeyboards.pop_back(); + return; + } + + keyboards.push_back(RESOURCE); + }); + + resource->setGetPointer([this](CWlSeat* r, uint32_t id) { + const auto RESOURCE = PROTO::seat->m_vPointers.emplace_back(makeShared(makeShared(r->client(), r->version(), id), self.lock())); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::seat->m_vPointers.pop_back(); + return; + } + + pointers.push_back(RESOURCE); + }); + + resource->setGetTouch([this](CWlSeat* r, uint32_t id) { + const auto RESOURCE = PROTO::seat->m_vTouches.emplace_back(makeShared(makeShared(r->client(), r->version(), id), self.lock())); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::seat->m_vTouches.pop_back(); + return; + } + + touches.push_back(RESOURCE); + }); + + if (resource->version() >= 2) + resource->sendName(HL_SEAT_NAME); + + sendCapabilities(PROTO::seat->currentCaps); +} + +CWLSeatResource::~CWLSeatResource() { + events.destroy.emit(); +} + +void CWLSeatResource::sendCapabilities(uint32_t caps) { + uint32_t wlCaps = 0; + if (caps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD) + wlCaps |= WL_SEAT_CAPABILITY_KEYBOARD; + if (caps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER) + wlCaps |= WL_SEAT_CAPABILITY_POINTER; + if (caps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH) + wlCaps |= WL_SEAT_CAPABILITY_TOUCH; + + resource->sendCapabilities((wl_seat_capability)wlCaps); +} + +bool CWLSeatResource::good() { + return resource->resource(); +} + +wl_client* CWLSeatResource::client() { + return pClient; +} + +CWLSeatProtocol::CWLSeatProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + ; +} + +void CWLSeatProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + const auto RESOURCE = m_vSeatResources.emplace_back(makeShared(makeShared(client, ver, id))); + + if (!RESOURCE->good()) { + wl_client_post_no_memory(client); + m_vSeatResources.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + + LOGM(LOG, "New seat resource bound at {:x}", (uintptr_t)RESOURCE.get()); + + events.newSeatResource.emit(RESOURCE); +} + +void CWLSeatProtocol::destroyResource(CWLSeatResource* seat) { + std::erase_if(m_vSeatResources, [&](const auto& other) { return other.get() == seat; }); +} + +void CWLSeatProtocol::destroyResource(CWLKeyboardResource* resource) { + std::erase_if(m_vKeyboards, [&](const auto& other) { return other.get() == resource; }); +} + +void CWLSeatProtocol::destroyResource(CWLPointerResource* resource) { + std::erase_if(m_vPointers, [&](const auto& other) { return other.get() == resource; }); +} + +void CWLSeatProtocol::destroyResource(CWLTouchResource* resource) { + std::erase_if(m_vTouches, [&](const auto& other) { return other.get() == resource; }); +} + +void CWLSeatProtocol::updateCapabilities(uint32_t caps) { + if (caps == currentCaps) + return; + + currentCaps = caps; + + for (auto& s : m_vSeatResources) { + s->sendCapabilities(caps); + } +} + +void CWLSeatProtocol::updateKeymap() { + for (auto& k : m_vKeyboards) { + k->sendKeymap(g_pSeatManager->keyboard.lock()); + } +} + +void CWLSeatProtocol::updateRepeatInfo(uint32_t rate, uint32_t delayMs) { + for (auto& k : m_vKeyboards) { + k->repeatInfo(rate, delayMs); + } +} + +SP CWLSeatProtocol::seatResourceForClient(wl_client* client) { + for (auto& r : m_vSeatResources) { + if (r->client() == client) + return r; + } + + return nullptr; +} diff --git a/src/protocols/core/Seat.hpp b/src/protocols/core/Seat.hpp new file mode 100644 index 00000000..4cd0d124 --- /dev/null +++ b/src/protocols/core/Seat.hpp @@ -0,0 +1,163 @@ +#pragma once + +/* + Implementations for: + - wl_seat + - wl_keyboard + - wl_pointer + - wl_touch +*/ + +#include +#include +#include +#include "../WaylandProtocol.hpp" +#include +#include "wayland.hpp" +#include "../../helpers/signal/Signal.hpp" +#include "../../helpers/Vector2D.hpp" + +constexpr const char* HL_SEAT_NAME = "Hyprland"; + +class IKeyboard; + +class CWLPointerResource; +class CWLKeyboardResource; +class CWLTouchResource; +class CWLSeatResource; + +class CWLTouchResource { + public: + CWLTouchResource(SP resource_, SP owner_); + + bool good(); + void sendDown(wlr_surface* surface, uint32_t timeMs, int32_t id, const Vector2D& local); + void sendUp(uint32_t timeMs, int32_t id); + void sendMotion(uint32_t timeMs, int32_t id, const Vector2D& local); + void sendFrame(); + void sendCancel(); + void sendShape(int32_t id, const Vector2D& shape); + void sendOrientation(int32_t id, double angle); + + WP owner; + + private: + SP resource; + wlr_surface* currentSurface = nullptr; + + DYNLISTENER(surfaceDestroy); +}; + +class CWLPointerResource { + public: + CWLPointerResource(SP resource_, SP owner_); + + bool good(); + void sendEnter(wlr_surface* surface, const Vector2D& local); + void sendLeave(); + void sendMotion(uint32_t timeMs, const Vector2D& local); + void sendButton(uint32_t timeMs, uint32_t button, wl_pointer_button_state state); + void sendAxis(uint32_t timeMs, wl_pointer_axis axis, double value); + void sendFrame(); + void sendAxisSource(wl_pointer_axis_source source); + void sendAxisStop(uint32_t timeMs, wl_pointer_axis axis); + void sendAxisDiscrete(wl_pointer_axis axis, int32_t discrete); + void sendAxisValue120(wl_pointer_axis axis, int32_t value120); + void sendAxisRelativeDirection(wl_pointer_axis axis, wl_pointer_axis_relative_direction direction); + + WP owner; + + private: + SP resource; + wlr_surface* currentSurface = nullptr; + + DYNLISTENER(surfaceDestroy); +}; + +class CWLKeyboardResource { + public: + CWLKeyboardResource(SP resource_, SP owner_); + + bool good(); + void sendKeymap(SP keeb); + void sendEnter(wlr_surface* surface); + void sendLeave(); + void sendKey(uint32_t timeMs, uint32_t key, wl_keyboard_key_state state); + void sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group); + void repeatInfo(uint32_t rate, uint32_t delayMs); + + WP owner; + + private: + SP resource; + wlr_surface* currentSurface = nullptr; + + DYNLISTENER(surfaceDestroy); +}; + +class CWLSeatResource { + public: + CWLSeatResource(SP resource_); + ~CWLSeatResource(); + + void sendCapabilities(uint32_t caps); // uses IHID capabilities + + bool good(); + wl_client* client(); + + std::vector> pointers; + std::vector> keyboards; + std::vector> touches; + + WP self; + + struct { + CSignal destroy; + } events; + + private: + SP resource; + wl_client* pClient = nullptr; +}; + +class CWLSeatProtocol : public IWaylandProtocol { + public: + CWLSeatProtocol(const wl_interface* iface, const int& ver, const std::string& name); + + virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); + + struct { + CSignal newSeatResource; // SP + } events; + + private: + void updateCapabilities(uint32_t caps); // in IHID caps + void updateRepeatInfo(uint32_t rate, uint32_t delayMs); + void updateKeymap(); + + void destroyResource(CWLSeatResource* resource); + void destroyResource(CWLKeyboardResource* resource); + void destroyResource(CWLTouchResource* resource); + void destroyResource(CWLPointerResource* resource); + + // + std::vector> m_vSeatResources; + std::vector> m_vKeyboards; + std::vector> m_vTouches; + std::vector> m_vPointers; + + SP seatResourceForClient(wl_client* client); + + // + uint32_t currentCaps = 0; + + friend class CWLSeatResource; + friend class CWLKeyboardResource; + friend class CWLTouchResource; + friend class CWLPointerResource; + friend class CSeatManager; +}; + +namespace PROTO { + inline UP seat; +}; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index d9529b6f..38dba439 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -2490,8 +2490,8 @@ void CHyprRenderer::recheckSolitaryForMonitor(CMonitor* pMonitor) { const auto PWORKSPACE = pMonitor->activeWorkspace; - if (!PWORKSPACE || !PWORKSPACE->m_bHasFullscreenWindow || g_pInputManager->m_sDrag.drag || g_pCompositor->m_sSeat.exclusiveClient || pMonitor->activeSpecialWorkspace || - PWORKSPACE->m_fAlpha.value() != 1.f || PWORKSPACE->m_vRenderOffset.value() != Vector2D{}) + if (!PWORKSPACE || !PWORKSPACE->m_bHasFullscreenWindow || g_pInputManager->m_sDrag.drag || pMonitor->activeSpecialWorkspace || PWORKSPACE->m_fAlpha.value() != 1.f || + PWORKSPACE->m_vRenderOffset.value() != Vector2D{}) return; const auto PCANDIDATE = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID); From 0cfdde3d1acbfbf698af17f6986fbc4d644214da Mon Sep 17 00:00:00 2001 From: Vaxry Date: Fri, 10 May 2024 23:28:33 +0100 Subject: [PATCH 19/57] xdg-shell: move to new impl --- CMakeLists.txt | 4 +- protocols/meson.build | 1 + src/Compositor.cpp | 51 +- src/Compositor.hpp | 1 - src/config/ConfigManager.cpp | 4 +- src/debug/HyprCtl.cpp | 12 +- src/desktop/Popup.cpp | 116 ++--- src/desktop/Popup.hpp | 31 +- src/desktop/Window.cpp | 206 ++++++-- src/desktop/Window.hpp | 31 +- src/events/Events.hpp | 3 - src/events/Windows.cpp | 175 ++----- src/includes.hpp | 2 - src/layout/IHyprLayout.cpp | 11 +- src/managers/ProtocolManager.cpp | 2 + src/managers/XWaylandManager.cpp | 121 ++--- src/managers/XWaylandManager.hpp | 3 - src/managers/input/InputManager.cpp | 6 +- src/protocols/ForeignToplevel.cpp | 2 +- src/protocols/ForeignToplevelWlr.cpp | 4 +- src/protocols/LayerShell.cpp | 7 +- src/protocols/XDGShell.cpp | 697 +++++++++++++++++++++++++++ src/protocols/XDGShell.hpp | 256 ++++++++++ src/render/Renderer.cpp | 27 +- 24 files changed, 1352 insertions(+), 421 deletions(-) create mode 100644 src/protocols/XDGShell.cpp create mode 100644 src/protocols/XDGShell.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 608ccc62..80483c4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,7 +110,7 @@ pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-server wayland-client wayland-cursor wayland-protocols cairo pango pangocairo pixman-1 libdrm libinput hwdata libseat libdisplay-info libliftoff libudev gbm - hyprwayland-scanner>=0.3.6 hyprlang>=0.3.2 hyprcursor>=0.1.7 + hyprwayland-scanner>=0.3.7 hyprlang>=0.3.2 hyprcursor>=0.1.7 ) file(GLOB_RECURSE SRCFILES "src/*.cpp") @@ -260,7 +260,6 @@ target_link_libraries(Hyprland protocol("protocols/wlr-screencopy-unstable-v1.xml" "wlr-screencopy-unstable-v1" true) protocol("subprojects/hyprland-protocols/protocols/hyprland-global-shortcuts-v1.xml" "hyprland-global-shortcuts-v1" true) protocol("subprojects/hyprland-protocols/protocols/hyprland-toplevel-export-v1.xml" "hyprland-toplevel-export-v1" true) -protocol("stable/xdg-shell/xdg-shell.xml" "xdg-shell" false) protocol("unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml" "linux-dmabuf-unstable-v1" false) protocol("unstable/text-input/text-input-unstable-v1.xml" "text-input-unstable-v1" false) @@ -292,6 +291,7 @@ protocolNew("staging/ext-idle-notify/ext-idle-notify-v1.xml" "ext-idle-notify-v1 protocolNew("staging/ext-session-lock/ext-session-lock-v1.xml" "ext-session-lock-v1" false) protocolNew("stable/tablet/tablet-v2.xml" "tablet-v2" false) protocolNew("stable/presentation-time/presentation-time.xml" "presentation-time" false) +protocolNew("stable/xdg-shell/xdg-shell.xml" "xdg-shell" false) protocolWayland() diff --git a/protocols/meson.build b/protocols/meson.build index 95c9fec4..4b7aa200 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -61,6 +61,7 @@ new_protocols = [ [wl_protocol_dir, 'staging/ext-session-lock/ext-session-lock-v1.xml'], [wl_protocol_dir, 'stable/tablet/tablet-v2.xml'], [wl_protocol_dir, 'stable/presentation-time/presentation-time.xml'], + [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], ] wl_protos_src = [] diff --git a/src/Compositor.cpp b/src/Compositor.cpp index efc02a5f..c56dd280 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -18,6 +18,7 @@ #include "protocols/FractionalScale.hpp" #include "protocols/PointerConstraints.hpp" #include "protocols/LayerShell.hpp" +#include "protocols/XDGShell.hpp" #include "desktop/LayerSurface.hpp" #include @@ -229,8 +230,6 @@ void CCompositor::initServer() { // wlr_primary_selection_v1_device_manager_create(m_sWLDisplay); wlr_viewporter_create(m_sWLDisplay); - m_sWLRXDGShell = wlr_xdg_shell_create(m_sWLDisplay, 6); - m_sWRLDRMLeaseMgr = wlr_drm_lease_v1_manager_create(m_sWLDisplay, m_sWLRBackend); if (!m_sWRLDRMLeaseMgr) { Debug::log(INFO, "Failed to create wlr_drm_lease_v1_manager"); @@ -253,7 +252,6 @@ void CCompositor::initServer() { void CCompositor::initAllSignals() { addWLSignal(&m_sWLRBackend->events.new_output, &Events::listen_newOutput, m_sWLRBackend, "Backend"); - addWLSignal(&m_sWLRXDGShell->events.new_toplevel, &Events::listen_newXDGToplevel, m_sWLRXDGShell, "XDG Shell"); addWLSignal(&m_sWLRBackend->events.new_input, &Events::listen_newInput, m_sWLRBackend, "Backend"); // addWLSignal(&m_sSeat.seat->events.request_set_selection, &Events::listen_requestSetSel, &m_sSeat, "Seat"); // addWLSignal(&m_sSeat.seat->events.request_start_drag, &Events::listen_requestDrag, &m_sSeat, "Seat"); @@ -271,7 +269,6 @@ void CCompositor::initAllSignals() { void CCompositor::removeAllSignals() { removeWLSignal(&Events::listen_newOutput); - removeWLSignal(&Events::listen_newXDGToplevel); removeWLSignal(&Events::listen_newInput); removeWLSignal(&Events::listen_requestSetSel); removeWLSignal(&Events::listen_requestDrag); @@ -805,22 +802,28 @@ wlr_surface* CCompositor::vectorWindowToSurface(const Vector2D& pos, PHLWINDOW p RASSERT(!pWindow->m_bIsX11, "Cannot call vectorWindowToSurface on an X11 window!"); - const auto PSURFACE = pWindow->m_uSurface.xdg; + double subx, suby; - double subx, suby; + CBox geom = pWindow->m_pXDGSurface->current.geometry; - // calc for oversized windows... fucking bullshit, again. - CBox geom; - wlr_xdg_surface_get_geometry(pWindow->m_uSurface.xdg, geom.pWlr()); - geom.applyFromWlr(); + // try popups first + const auto PPOPUP = pWindow->m_pPopupHead->at(pos); - const auto PFOUND = - wlr_xdg_surface_surface_at(PSURFACE, pos.x - pWindow->m_vRealPosition.value().x + geom.x, pos.y - pWindow->m_vRealPosition.value().y + geom.y, &subx, &suby); + wlr_surface* found = PPOPUP ? PPOPUP->m_sWLSurface.wlr() : nullptr; - if (PFOUND) { + if (!PPOPUP) + found = wlr_surface_surface_at(pWindow->m_pWLSurface.wlr(), pos.x - pWindow->m_vRealPosition.value().x + geom.x, pos.y - pWindow->m_vRealPosition.value().y + geom.y, &subx, + &suby); + else { + const auto OFF = PPOPUP->coordsRelativeToParent(); + subx = pos.x - OFF.x + geom.x - pWindow->m_vRealPosition.goal().x; + suby = pos.y - OFF.y + geom.y - pWindow->m_vRealPosition.goal().y; + } + + if (found) { sl.x = subx; sl.y = suby; - return PFOUND; + return found; } sl.x = pos.x - pWindow->m_vRealPosition.value().x; @@ -829,7 +832,7 @@ wlr_surface* CCompositor::vectorWindowToSurface(const Vector2D& pos, PHLWINDOW p sl.x += geom.x; sl.y += geom.y; - return PSURFACE->surface; + return pWindow->m_pWLSurface.wlr(); } Vector2D CCompositor::vectorToSurfaceLocal(const Vector2D& vec, PHLWINDOW pWindow, wlr_surface* pSurface) { @@ -839,12 +842,14 @@ Vector2D CCompositor::vectorToSurfaceLocal(const Vector2D& vec, PHLWINDOW pWindo if (pWindow->m_bIsX11) return vec - pWindow->m_vRealPosition.goal(); - const auto PSURFACE = pWindow->m_uSurface.xdg; + const auto PPOPUP = pWindow->m_pPopupHead->at(vec); + if (PPOPUP) + return vec - PPOPUP->coordsGlobal(); std::tuple iterData = {pSurface, -1337, -1337}; - wlr_xdg_surface_for_each_surface( - PSURFACE, + wlr_surface_for_each_surface( + pWindow->m_pWLSurface.wlr(), [](wlr_surface* surf, int x, int y, void* data) { const auto PDATA = (std::tuple*)data; if (surf == std::get<0>(*PDATA)) { @@ -854,9 +859,7 @@ Vector2D CCompositor::vectorToSurfaceLocal(const Vector2D& vec, PHLWINDOW pWindo }, &iterData); - CBox geom = {}; - wlr_xdg_surface_get_geometry(PSURFACE, geom.pWlr()); - geom.applyFromWlr(); + CBox geom = pWindow->m_pXDGSurface->current.geometry; if (std::get<1>(iterData) == -1337 && std::get<2>(iterData) == -1337) return vec - pWindow->m_vRealPosition.goal(); @@ -993,7 +996,7 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, wlr_surface* pSurface) { pWindow->m_bIsUrgent = false; // Send an event - g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", g_pXWaylandManager->getAppIDClass(pWindow) + "," + pWindow->m_szTitle}); + g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", pWindow->m_szClass + "," + pWindow->m_szTitle}); g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", std::format("{:x}", (uintptr_t)pWindow.get())}); EMIT_HOOK_EVENT("activeWindow", pWindow); @@ -2310,7 +2313,7 @@ PHLWINDOW CCompositor::getWindowByRegex(const std::string& regexp) { switch (mode) { case MODE_CLASS_REGEX: { - const auto windowClass = g_pXWaylandManager->getAppIDClass(w); + const auto windowClass = w->m_szClass; if (!std::regex_search(windowClass, regexCheck)) continue; break; @@ -2322,7 +2325,7 @@ PHLWINDOW CCompositor::getWindowByRegex(const std::string& regexp) { break; } case MODE_TITLE_REGEX: { - const auto windowTitle = g_pXWaylandManager->getTitle(w); + const auto windowTitle = w->m_szTitle; if (!std::regex_search(windowTitle, regexCheck)) continue; break; diff --git a/src/Compositor.hpp b/src/Compositor.hpp index eedcea56..574889bc 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -50,7 +50,6 @@ class CCompositor { wlr_subcompositor* m_sWLRSubCompositor; wlr_drm* m_sWRLDRM; wlr_drm_lease_v1_manager* m_sWRLDRMLeaseMgr; - wlr_xdg_shell* m_sWLRXDGShell; wlr_egl* m_sWLREGL; int m_iDRMFD; wlr_linux_dmabuf_v1* m_sWLRLinuxDMABuf; diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 4a75ca1d..15d5ae81 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -1042,8 +1042,8 @@ std::vector CConfigManager::getMatchingRules(PHLWINDOW pWindow, boo std::vector returns; - std::string title = g_pXWaylandManager->getTitle(pWindow); - std::string appidclass = g_pXWaylandManager->getAppIDClass(pWindow); + std::string title = pWindow->m_szTitle; + std::string appidclass = pWindow->m_szClass; Debug::log(LOG, "Searching for matching rules for {} (title: {})", appidclass, title); diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp index f8fdb03f..51071533 100644 --- a/src/debug/HyprCtl.cpp +++ b/src/debug/HyprCtl.cpp @@ -210,10 +210,10 @@ static std::string getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) { (uintptr_t)w.get(), (w->m_bIsMapped ? "true" : "false"), (w->isHidden() ? "true" : "false"), (int)w->m_vRealPosition.goal().x, (int)w->m_vRealPosition.goal().y, (int)w->m_vRealSize.goal().x, (int)w->m_vRealSize.goal().y, w->m_pWorkspace ? w->workspaceID() : WORKSPACE_INVALID, escapeJSONStrings(!w->m_pWorkspace ? "" : w->m_pWorkspace->m_szName), ((int)w->m_bIsFloating == 1 ? "true" : "false"), (int64_t)w->m_iMonitorID, - escapeJSONStrings(g_pXWaylandManager->getAppIDClass(w)), escapeJSONStrings(g_pXWaylandManager->getTitle(w)), escapeJSONStrings(w->m_szInitialClass), - escapeJSONStrings(w->m_szInitialTitle), w->getPID(), ((int)w->m_bIsX11 == 1 ? "true" : "false"), (w->m_bPinned ? "true" : "false"), - (w->m_bIsFullscreen ? "true" : "false"), (w->m_bIsFullscreen ? (w->m_pWorkspace ? (int)w->m_pWorkspace->m_efFullscreenMode : 0) : 0), - w->m_bFakeFullscreenState ? "true" : "false", getGroupedData(w, format), (uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w)); + escapeJSONStrings(w->m_szClass), escapeJSONStrings(w->m_szTitle), escapeJSONStrings(w->m_szInitialClass), escapeJSONStrings(w->m_szInitialTitle), w->getPID(), + ((int)w->m_bIsX11 == 1 ? "true" : "false"), (w->m_bPinned ? "true" : "false"), (w->m_bIsFullscreen ? "true" : "false"), + (w->m_bIsFullscreen ? (w->m_pWorkspace ? (int)w->m_pWorkspace->m_efFullscreenMode : 0) : 0), w->m_bFakeFullscreenState ? "true" : "false", getGroupedData(w, format), + (uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w)); } else { return std::format("Window {:x} -> {}:\n\tmapped: {}\n\thidden: {}\n\tat: {},{}\n\tsize: {},{}\n\tworkspace: {} ({})\n\tfloating: {}\n\tmonitor: {}\n\tclass: {}\n\ttitle: " "{}\n\tinitialClass: {}\n\tinitialTitle: {}\n\tpid: " @@ -221,8 +221,8 @@ static std::string getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) { "{}\n\tfullscreen: {}\n\tfullscreenmode: {}\n\tfakefullscreen: {}\n\tgrouped: {}\n\tswallowing: {:x}\n\tfocusHistoryID: {}\n\n", (uintptr_t)w.get(), w->m_szTitle, (int)w->m_bIsMapped, (int)w->isHidden(), (int)w->m_vRealPosition.goal().x, (int)w->m_vRealPosition.goal().y, (int)w->m_vRealSize.goal().x, (int)w->m_vRealSize.goal().y, w->m_pWorkspace ? w->workspaceID() : WORKSPACE_INVALID, - (!w->m_pWorkspace ? "" : w->m_pWorkspace->m_szName), (int)w->m_bIsFloating, (int64_t)w->m_iMonitorID, g_pXWaylandManager->getAppIDClass(w), - g_pXWaylandManager->getTitle(w), w->m_szInitialClass, w->m_szInitialTitle, w->getPID(), (int)w->m_bIsX11, (int)w->m_bPinned, (int)w->m_bIsFullscreen, + (!w->m_pWorkspace ? "" : w->m_pWorkspace->m_szName), (int)w->m_bIsFloating, (int64_t)w->m_iMonitorID, w->m_szClass, w->m_szTitle, w->m_szInitialClass, + w->m_szInitialTitle, w->getPID(), (int)w->m_bIsX11, (int)w->m_bPinned, (int)w->m_bIsFullscreen, (w->m_bIsFullscreen ? (w->m_pWorkspace ? w->m_pWorkspace->m_efFullscreenMode : 0) : 0), (int)w->m_bFakeFullscreenState, getGroupedData(w, format), (uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w)); } diff --git a/src/desktop/Popup.cpp b/src/desktop/Popup.cpp index e172744d..605dfdfd 100644 --- a/src/desktop/Popup.cpp +++ b/src/desktop/Popup.cpp @@ -2,6 +2,7 @@ #include "../config/ConfigValue.hpp" #include "../Compositor.hpp" #include "../protocols/LayerShell.hpp" +#include "../protocols/XDGShell.hpp" #include CPopup::CPopup(PHLWINDOW pOwner) : m_pWindowOwner(pOwner) { @@ -12,14 +13,13 @@ CPopup::CPopup(PHLLS pOwner) : m_pLayerOwner(pOwner) { initAllSignals(); } -CPopup::CPopup(wlr_xdg_popup* popup, CPopup* pOwner) : m_pParent(pOwner), m_pWLR(popup) { - m_pWLR->base->data = this; - m_sWLSurface.assign(popup->base->surface, this); +CPopup::CPopup(SP popup, CPopup* pOwner) : m_pParent(pOwner), m_pResource(popup) { + m_sWLSurface.assign(popup->surface->surface, this); m_pLayerOwner = pOwner->m_pLayerOwner; m_pWindowOwner = pOwner->m_pWindowOwner; - m_vLastSize = {m_pWLR->current.geometry.width, m_pWLR->current.geometry.height}; + m_vLastSize = popup->surface->current.geometry.size(); unconstrain(); initAllSignals(); @@ -27,71 +27,32 @@ CPopup::CPopup(wlr_xdg_popup* popup, CPopup* pOwner) : m_pParent(pOwner), m_pWLR CPopup::~CPopup() { m_sWLSurface.unassign(); - if (m_pWLR) - m_pWLR->base->data = nullptr; - - hyprListener_commitPopup.removeCallback(); - hyprListener_repositionPopup.removeCallback(); - hyprListener_mapPopup.removeCallback(); - hyprListener_unmapPopup.removeCallback(); - hyprListener_newPopup.removeCallback(); - hyprListener_destroyPopup.removeCallback(); -} - -static void onNewPopup(void* owner, void* data) { - const auto POPUP = (CPopup*)owner; - POPUP->onNewPopup((wlr_xdg_popup*)data); -} - -static void onMapPopup(void* owner, void* data) { - const auto POPUP = (CPopup*)owner; - POPUP->onMap(); -} - -static void onDestroyPopup(void* owner, void* data) { - const auto POPUP = (CPopup*)owner; - POPUP->onDestroy(); -} - -static void onUnmapPopup(void* owner, void* data) { - const auto POPUP = (CPopup*)owner; - POPUP->onUnmap(); -} - -static void onCommitPopup(void* owner, void* data) { - const auto POPUP = (CPopup*)owner; - POPUP->onCommit(); -} - -static void onRepositionPopup(void* owner, void* data) { - const auto POPUP = (CPopup*)owner; - POPUP->onReposition(); } void CPopup::initAllSignals() { - if (!m_pWLR) { + if (!m_pResource) { if (!m_pWindowOwner.expired()) - hyprListener_newPopup.initCallback(&m_pWindowOwner->m_uSurface.xdg->events.new_popup, ::onNewPopup, this, "CPopup Head"); + listeners.newPopup = m_pWindowOwner->m_pXDGSurface->events.newPopup.registerListener([this](std::any d) { this->onNewPopup(std::any_cast>(d)); }); else if (!m_pLayerOwner.expired()) - listeners.newPopup = m_pLayerOwner->layerSurface->events.newPopup.registerListener([this](std::any d) { this->onNewPopup(std::any_cast(d)); }); + listeners.newPopup = m_pLayerOwner->layerSurface->events.newPopup.registerListener([this](std::any d) { this->onNewPopup(std::any_cast>(d)); }); else ASSERT(false); return; } - hyprListener_repositionPopup.initCallback(&m_pWLR->events.reposition, ::onRepositionPopup, this, "CPopup"); - hyprListener_destroyPopup.initCallback(&m_pWLR->events.destroy, ::onDestroyPopup, this, "CPopup"); - hyprListener_mapPopup.initCallback(&m_sWLSurface.wlr()->events.map, ::onMapPopup, this, "CPopup"); - hyprListener_unmapPopup.initCallback(&m_sWLSurface.wlr()->events.unmap, ::onUnmapPopup, this, "CPopup"); - hyprListener_commitPopup.initCallback(&m_sWLSurface.wlr()->events.commit, ::onCommitPopup, this, "CPopup"); - hyprListener_newPopup.initCallback(&m_pWLR->base->events.new_popup, ::onNewPopup, this, "CPopup"); + listeners.reposition = m_pResource->events.reposition.registerListener([this](std::any d) { this->onReposition(); }); + listeners.map = m_pResource->surface->events.map.registerListener([this](std::any d) { this->onMap(); }); + listeners.unmap = m_pResource->surface->events.unmap.registerListener([this](std::any d) { this->onUnmap(); }); + listeners.destroy = m_pResource->surface->events.destroy.registerListener([this](std::any d) { this->onDestroy(); }); + listeners.commit = m_pResource->surface->events.commit.registerListener([this](std::any d) { this->onCommit(); }); + listeners.newPopup = m_pResource->surface->events.newPopup.registerListener([this](std::any d) { this->onNewPopup(std::any_cast>(d)); }); } -void CPopup::onNewPopup(wlr_xdg_popup* popup) { +void CPopup::onNewPopup(SP popup) { const auto POPUP = m_vChildren.emplace_back(std::make_unique(popup, this)).get(); - Debug::log(LOG, "New popup at wlr {:x} and hl {:x}", (uintptr_t)popup, (uintptr_t)POPUP); + Debug::log(LOG, "New popup at {:x}", (uintptr_t)POPUP); } void CPopup::onDestroy() { @@ -104,7 +65,7 @@ void CPopup::onDestroy() { } void CPopup::onMap() { - m_vLastSize = {m_pWLR->base->current.geometry.width, m_pWLR->base->current.geometry.height}; + m_vLastSize = {m_pResource->surface->surface->current.width, m_pResource->surface->surface->current.height}; const auto COORDS = coordsGlobal(); CBox box; @@ -126,7 +87,9 @@ void CPopup::onMap() { } void CPopup::onUnmap() { - m_vLastSize = {m_pWLR->base->current.geometry.width, m_pWLR->base->current.geometry.height}; + if (!m_pResource || !m_pResource->surface) + return; + m_vLastSize = {m_pResource->surface->surface->current.width, m_pResource->surface->surface->current.height}; const auto COORDS = coordsGlobal(); CBox box; @@ -140,16 +103,27 @@ void CPopup::onUnmap() { if (!m_pLayerOwner.expired() && m_pLayerOwner->layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP) g_pHyprOpenGL->markBlurDirtyForMonitor(g_pCompositor->getMonitorFromID(m_pLayerOwner->layer)); + + // damage all children + breadthfirst( + [this](CPopup* p, void* data) { + if (!p->m_pResource) + return; + + auto box = CBox{p->coordsGlobal(), p->size()}; + g_pHyprRenderer->damageBox(&box); + }, + nullptr); } void CPopup::onCommit(bool ignoreSiblings) { - if (m_pWLR->base->initial_commit) { - wlr_xdg_surface_schedule_configure(m_pWLR->base); + if (m_pResource->surface->initialCommit) { + m_pResource->surface->scheduleConfigure(); return; } if (!m_pWindowOwner.expired() && (!m_pWindowOwner->m_bIsMapped || !m_pWindowOwner->m_pWorkspace->m_bVisible)) { - m_vLastSize = {m_pWLR->base->current.geometry.width, m_pWLR->base->current.geometry.height}; + m_vLastSize = {m_pResource->surface->surface->current.width, m_pResource->surface->surface->current.height}; static auto PLOGDAMAGE = CConfigValue("debug:log_damage"); if (*PLOGDAMAGE) @@ -157,16 +131,17 @@ void CPopup::onCommit(bool ignoreSiblings) { return; } - if (!m_pWLR->base->surface->mapped) + if (!m_pResource->surface->mapped) return; const auto COORDS = coordsGlobal(); const auto COORDSLOCAL = coordsRelativeToParent(); - if (m_vLastSize != Vector2D{m_pWLR->base->current.geometry.width, m_pWLR->base->current.geometry.height} || m_bRequestedReposition || m_vLastPos != COORDSLOCAL) { + if (m_vLastSize != Vector2D{m_pResource->surface->surface->current.width, m_pResource->surface->surface->current.height} || m_bRequestedReposition || + m_vLastPos != COORDSLOCAL) { CBox box = {localToGlobal(m_vLastPos), m_vLastSize}; g_pHyprRenderer->damageBox(&box); - m_vLastSize = {m_pWLR->base->current.geometry.width, m_pWLR->base->current.geometry.height}; + m_vLastSize = {m_pResource->surface->surface->current.width, m_pResource->surface->surface->current.height}; box = {COORDS, m_vLastSize}; g_pHyprRenderer->damageBox(&box); @@ -202,20 +177,25 @@ void CPopup::unconstrain() { return; CBox box = {PMONITOR->vecPosition.x - COORDS.x, PMONITOR->vecPosition.y - COORDS.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y}; - wlr_xdg_popup_unconstrain_from_box(m_pWLR, box.pWlr()); + m_pResource->applyPositioning(box, COORDS - PMONITOR->vecPosition); + wlr_surface_send_enter(m_pResource->surface->surface, PMONITOR->output); } Vector2D CPopup::coordsRelativeToParent() { Vector2D offset; - CPopup* current = this; + if (!m_pResource) + return {}; - offset -= {m_pWLR->base->current.geometry.x, m_pWLR->base->current.geometry.y}; + CPopup* current = this; + offset -= current->m_pResource->surface->current.geometry.pos(); - while (current->m_pParent) { + offset -= m_pResource->surface->current.geometry.pos(); + + while (current->m_pParent && current->m_pResource) { offset += {current->m_sWLSurface.wlr()->current.dx, current->m_sWLSurface.wlr()->current.dy}; - offset += {current->m_pWLR->current.geometry.x, current->m_pWLR->current.geometry.y}; + offset += current->m_pResource->geometry.pos(); current = current->m_pParent; } @@ -309,7 +289,7 @@ CPopup* CPopup::at(const Vector2D& globalCoords, bool allowsInput) { breadthfirst([](CPopup* popup, void* data) { ((std::vector*)data)->push_back(popup); }, &popups); for (auto& p : popups | std::views::reverse) { - if (!p->m_pWLR) + if (!p->m_pResource) continue; if (!allowsInput) { diff --git a/src/desktop/Popup.hpp b/src/desktop/Popup.hpp index 6aa7ce61..e6c35e68 100644 --- a/src/desktop/Popup.hpp +++ b/src/desktop/Popup.hpp @@ -5,6 +5,8 @@ #include "Subsurface.hpp" #include "../helpers/signal/Listener.hpp" +class CXDGPopupResource; + class CPopup { public: // dummy head nodes @@ -12,7 +14,7 @@ class CPopup { CPopup(PHLLS pOwner); // real nodes - CPopup(wlr_xdg_popup* popup, CPopup* pOwner); + CPopup(SP popup, CPopup* pOwner); ~CPopup(); @@ -21,7 +23,7 @@ class CPopup { Vector2D size(); - void onNewPopup(wlr_xdg_popup* popup); + void onNewPopup(SP popup); void onDestroy(); void onMap(); void onUnmap(); @@ -45,31 +47,28 @@ class CPopup { PHLLSREF m_pLayerOwner; // T2 owners - CPopup* m_pParent = nullptr; + CPopup* m_pParent = nullptr; - wlr_xdg_popup* m_pWLR = nullptr; + WP m_pResource; - Vector2D m_vLastSize = {}; - Vector2D m_vLastPos = {}; + Vector2D m_vLastSize = {}; + Vector2D m_vLastPos = {}; - bool m_bRequestedReposition = false; + bool m_bRequestedReposition = false; - bool m_bInert = false; + bool m_bInert = false; // std::vector> m_vChildren; std::unique_ptr m_pSubsurfaceHead; - // signals - DYNLISTENER(newPopup); - DYNLISTENER(destroyPopup); - DYNLISTENER(mapPopup); - DYNLISTENER(unmapPopup); - DYNLISTENER(commitPopup); - DYNLISTENER(repositionPopup); - struct { CHyprSignalListener newPopup; + CHyprSignalListener destroy; + CHyprSignalListener map; + CHyprSignalListener unmap; + CHyprSignalListener commit; + CHyprSignalListener reposition; } listeners; void initAllSignals(); diff --git a/src/desktop/Window.cpp b/src/desktop/Window.cpp index e87bd99c..c11a714b 100644 --- a/src/desktop/Window.cpp +++ b/src/desktop/Window.cpp @@ -6,6 +6,7 @@ #include "../config/ConfigValue.hpp" #include #include "../managers/TokenManager.hpp" +#include "../protocols/XDGShell.hpp" PHLWINDOW CWindow::create() { PHLWINDOW pWindow = SP(new CWindow); @@ -27,6 +28,39 @@ PHLWINDOW CWindow::create() { return pWindow; } +PHLWINDOW CWindow::create(SP resource) { + PHLWINDOW pWindow = SP(new CWindow(resource)); + + pWindow->m_pSelf = pWindow; + resource->toplevel->window = pWindow; + + pWindow->m_vRealPosition.create(g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE); + pWindow->m_vRealSize.create(g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE); + pWindow->m_fBorderFadeAnimationProgress.create(g_pConfigManager->getAnimationPropertyConfig("border"), pWindow, AVARDAMAGE_BORDER); + pWindow->m_fBorderAngleAnimationProgress.create(g_pConfigManager->getAnimationPropertyConfig("borderangle"), pWindow, AVARDAMAGE_BORDER); + pWindow->m_fAlpha.create(g_pConfigManager->getAnimationPropertyConfig("fadeIn"), pWindow, AVARDAMAGE_ENTIRE); + pWindow->m_fActiveInactiveAlpha.create(g_pConfigManager->getAnimationPropertyConfig("fadeSwitch"), pWindow, AVARDAMAGE_ENTIRE); + pWindow->m_cRealShadowColor.create(g_pConfigManager->getAnimationPropertyConfig("fadeShadow"), pWindow, AVARDAMAGE_SHADOW); + pWindow->m_fDimPercent.create(g_pConfigManager->getAnimationPropertyConfig("fadeDim"), pWindow, AVARDAMAGE_ENTIRE); + + pWindow->addWindowDeco(std::make_unique(pWindow)); + pWindow->addWindowDeco(std::make_unique(pWindow)); + + pWindow->m_pWLSurface.assign(pWindow->m_pXDGSurface->surface, pWindow); + + return pWindow; +} + +CWindow::CWindow(SP resource) : m_pXDGSurface(resource) { + listeners.map = m_pXDGSurface->events.map.registerListener([this](std::any d) { Events::listener_mapWindow(this, nullptr); }); + listeners.ack = m_pXDGSurface->events.ack.registerListener([this](std::any d) { onAck(std::any_cast(d)); }); + listeners.unmap = m_pXDGSurface->events.unmap.registerListener([this](std::any d) { Events::listener_unmapWindow(this, nullptr); }); + listeners.destroy = m_pXDGSurface->events.destroy.registerListener([this](std::any d) { Events::listener_destroyWindow(this, nullptr); }); + listeners.commit = m_pXDGSurface->events.commit.registerListener([this](std::any d) { Events::listener_commitWindow(this, nullptr); }); + listeners.updateState = m_pXDGSurface->toplevel->events.stateChanged.registerListener([this](std::any d) { onUpdateState(); }); + listeners.updateMetadata = m_pXDGSurface->toplevel->events.metadataChanged.registerListener([this](std::any d) { onUpdateMeta(); }); +} + CWindow::CWindow() { ; } @@ -74,21 +108,24 @@ SWindowDecorationExtents CWindow::getFullWindowExtents() { if (EXTENTS.bottomRight.y > maxExtents.bottomRight.y) maxExtents.bottomRight.y = EXTENTS.bottomRight.y; - if (m_pWLSurface.exists() && !m_bIsX11) { + if (m_pWLSurface.exists() && !m_bIsX11 && m_pPopupHead) { CBox surfaceExtents = {0, 0, 0, 0}; // TODO: this could be better, perhaps make a getFullWindowRegion? - wlr_xdg_surface_for_each_popup_surface( - m_uSurface.xdg, - [](wlr_surface* surf, int sx, int sy, void* data) { + m_pPopupHead->breadthfirst( + [](CPopup* popup, void* data) { + if (!popup->m_sWLSurface.wlr()) + return; + CBox* pSurfaceExtents = (CBox*)data; - if (sx < pSurfaceExtents->x) - pSurfaceExtents->x = sx; - if (sy < pSurfaceExtents->y) - pSurfaceExtents->y = sy; - if (sx + surf->current.width > pSurfaceExtents->width) - pSurfaceExtents->width = sx + surf->current.width - pSurfaceExtents->x; - if (sy + surf->current.height > pSurfaceExtents->height) - pSurfaceExtents->height = sy + surf->current.height - pSurfaceExtents->y; + CBox surf = CBox{popup->coordsRelativeToParent(), popup->size()}; + if (surf.x < pSurfaceExtents->x) + pSurfaceExtents->x = surf.x; + if (surf.y < pSurfaceExtents->y) + pSurfaceExtents->y = surf.y; + if (surf.x + surf.w > pSurfaceExtents->width) + pSurfaceExtents->width = surf.x + surf.w - pSurfaceExtents->x; + if (surf.y + surf.h > pSurfaceExtents->height) + pSurfaceExtents->height = surf.y + surf.h - pSurfaceExtents->y; }, &surfaceExtents); @@ -258,10 +295,10 @@ bool CWindow::checkInputOnDecos(const eInputType type, const Vector2D& mouseCoor pid_t CWindow::getPID() { pid_t PID = -1; if (!m_bIsX11) { - if (!m_uSurface.xdg) + if (!m_pXDGSurface || !m_pXDGSurface->owner /* happens at unmap */) return -1; - wl_client_get_credentials(wl_resource_get_client(m_uSurface.xdg->resource), &PID, nullptr, nullptr); + wl_client_get_credentials(m_pXDGSurface->owner->client(), &PID, nullptr, nullptr); } else { if (!m_uSurface.xwayland) return -1; @@ -511,8 +548,8 @@ void CWindow::onMap() { g_pCompositor->m_vWindowFocusHistory.push_back(m_pSelf); - hyprListener_unmapWindow.initCallback(m_bIsX11 ? &m_uSurface.xwayland->surface->events.unmap : &m_uSurface.xdg->surface->events.unmap, &Events::listener_unmapWindow, this, - "CWindow"); + if (m_bIsX11) + hyprListener_unmapWindow.initCallback(&m_uSurface.xwayland->surface->events.unmap, &Events::listener_unmapWindow, this, "CWindow"); m_vReportedSize = m_vPendingReportedSize; m_bAnimatingIn = true; @@ -793,26 +830,14 @@ bool CWindow::isInCurvedCorner(double x, double y) { return false; } -void findExtensionForVector2D(wlr_surface* surface, int x, int y, void* data) { - const auto DATA = (SExtensionFindingData*)data; - - CBox box = {DATA->origin.x + x, DATA->origin.y + y, surface->current.width, surface->current.height}; - - if (box.containsPoint(DATA->vec)) - *DATA->found = surface; -} - // checks if the wayland window has a popup at pos bool CWindow::hasPopupAt(const Vector2D& pos) { if (m_bIsX11) return false; - wlr_surface* resultSurf = nullptr; - Vector2D origin = m_vRealPosition.value(); - SExtensionFindingData data = {origin, pos, &resultSurf}; - wlr_xdg_surface_for_each_popup_surface(m_uSurface.xdg, findExtensionForVector2D, &data); + CPopup* popup = m_pPopupHead->at(pos); - return resultSurf; + return popup && popup->m_sWLSurface.wlr(); } void CWindow::applyGroupRules() { @@ -1096,11 +1121,11 @@ bool CWindow::opaque() { if (m_bIsX11) return !m_uSurface.xwayland->has_alpha; - if (m_uSurface.xdg->surface->opaque) + if (m_pXDGSurface->surface->opaque) return true; - const auto EXTENTS = pixman_region32_extents(&m_uSurface.xdg->surface->opaque_region); - if (EXTENTS->x2 - EXTENTS->x1 >= m_uSurface.xdg->surface->current.buffer_width && EXTENTS->y2 - EXTENTS->y1 >= m_uSurface.xdg->surface->current.buffer_height) + const auto EXTENTS = pixman_region32_extents(&m_pXDGSurface->surface->opaque_region); + if (EXTENTS->x2 - EXTENTS->x1 >= m_pXDGSurface->surface->current.buffer_width && EXTENTS->y2 - EXTENTS->y1 >= m_pXDGSurface->surface->current.buffer_height) return true; return false; @@ -1162,10 +1187,10 @@ void CWindow::setSuspended(bool suspend) { if (suspend == m_bSuspended) return; - if (m_bIsX11) + if (m_bIsX11 || !m_pXDGSurface->toplevel) return; - wlr_xdg_toplevel_set_suspended(m_uSurface.xdg->toplevel, suspend); + m_pXDGSurface->toplevel->setSuspeneded(suspend); m_bSuspended = suspend; } @@ -1221,12 +1246,21 @@ void CWindow::onWorkspaceAnimUpdate() { } int CWindow::popupsCount() { + if (m_bIsX11) + return 0; + + int no = -1; + m_pPopupHead->breadthfirst([](CPopup* p, void* d) { *((int*)d) += 1; }, &no); + return no; +} + +int CWindow::surfacesCount() { if (m_bIsX11) return 1; int no = 0; - wlr_xdg_surface_for_each_popup_surface( - m_uSurface.xdg, [](wlr_surface* s, int x, int y, void* data) { *(int*)data += 1; }, &no); + wlr_surface_for_each_surface( + m_pWLSurface.wlr(), [](wlr_surface* surf, int x, int y, void* data) { *((int*)data) += 1; }, &no); return no; } @@ -1278,6 +1312,9 @@ std::unordered_map CWindow::getEnv() { } void CWindow::activate(bool force) { + if (g_pCompositor->m_pLastWindow == m_pSelf) + return; + static auto PFOCUSONACTIVATE = CConfigValue("misc:focus_on_activate"); g_pEventManager->postEvent(SHyprIPCEvent{"urgent", std::format("{:x}", (uintptr_t)this)}); @@ -1295,3 +1332,98 @@ void CWindow::activate(bool force) { g_pCompositor->focusWindow(m_pSelf.lock()); g_pCompositor->warpCursorTo(middle()); } + +void CWindow::onUpdateState() { + if (!m_pXDGSurface) + return; + + if (m_pXDGSurface->toplevel->state.requestsFullscreen) { + bool fs = m_pXDGSurface->toplevel->state.requestsFullscreen.value(); + + if (fs != m_bIsFullscreen && m_pXDGSurface->mapped) + g_pCompositor->setWindowFullscreen(m_pSelf.lock(), fs, FULLSCREEN_FULL); + + if (!m_pXDGSurface->mapped) + m_bWantsInitialFullscreen = fs; + } + + if (m_pXDGSurface->toplevel->state.requestsMaximize) { + bool fs = m_pXDGSurface->toplevel->state.requestsMaximize.value(); + + if (fs != m_bIsFullscreen && m_pXDGSurface->mapped) + g_pCompositor->setWindowFullscreen(m_pSelf.lock(), fs, FULLSCREEN_MAXIMIZED); + } +} + +void CWindow::onUpdateMeta() { + const auto NEWTITLE = fetchTitle(); + + if (m_szTitle != NEWTITLE) { + m_szTitle = NEWTITLE; + g_pEventManager->postEvent(SHyprIPCEvent{"windowtitle", std::format("{:x}", (uintptr_t)this)}); + EMIT_HOOK_EVENT("windowTitle", m_pSelf.lock()); + + if (m_pSelf == g_pCompositor->m_pLastWindow) { // if it's the active, let's post an event to update others + g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", m_szClass + "," + m_szTitle}); + g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", std::format("{:x}", (uintptr_t)this)}); + EMIT_HOOK_EVENT("activeWindow", m_pSelf.lock()); + } + + updateDynamicRules(); + g_pCompositor->updateWindowAnimatedDecorationValues(m_pSelf.lock()); + updateToplevel(); + + Debug::log(LOG, "Window {:x} set title to {}", (uintptr_t)this, m_szTitle); + } + + const auto NEWCLASS = fetchClass(); + if (m_szClass != NEWCLASS) { + m_szClass = NEWCLASS; + + if (m_pSelf == g_pCompositor->m_pLastWindow) { // if it's the active, let's post an event to update others + g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", m_szClass + "," + m_szTitle}); + g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", std::format("{:x}", (uintptr_t)this)}); + EMIT_HOOK_EVENT("activeWindow", m_pSelf.lock()); + } + + updateDynamicRules(); + g_pCompositor->updateWindowAnimatedDecorationValues(m_pSelf.lock()); + updateToplevel(); + + Debug::log(LOG, "Window {:x} set class to {}", (uintptr_t)this, m_szClass); + } +} + +std::string CWindow::fetchTitle() { + if (!m_bIsX11) { + if (m_pXDGSurface && m_pXDGSurface->toplevel) + return m_pXDGSurface->toplevel->state.title; + } else { + if (m_uSurface.xwayland && m_uSurface.xwayland->title) + return m_uSurface.xwayland->title; + } + + return ""; +} + +std::string CWindow::fetchClass() { + if (!m_bIsX11) { + if (m_pXDGSurface && m_pXDGSurface->toplevel) + return m_pXDGSurface->toplevel->state.appid; + } else { + if (m_uSurface.xwayland && m_uSurface.xwayland->_class) + return m_uSurface.xwayland->_class; + } + + return ""; +} + +void CWindow::onAck(uint32_t serial) { + const auto SERIAL = std::find_if(m_vPendingSizeAcks.rbegin(), m_vPendingSizeAcks.rend(), [serial](const auto& e) { return e.first == serial; }); + + if (SERIAL == m_vPendingSizeAcks.rend()) + return; + + m_pPendingSizeAck = *SERIAL; + std::erase_if(m_vPendingSizeAcks, [&](const auto& el) { return el.first <= SERIAL->first; }); +} \ No newline at end of file diff --git a/src/desktop/Window.hpp b/src/desktop/Window.hpp index 6bff095c..24d9562b 100644 --- a/src/desktop/Window.hpp +++ b/src/desktop/Window.hpp @@ -14,6 +14,8 @@ #include "DesktopTypes.hpp" #include "../helpers/signal/Signal.hpp" +class CXDGSurfaceResource; + enum eIdleInhibitMode { IDLEINHIBIT_NONE = 0, IDLEINHIBIT_ALWAYS, @@ -196,9 +198,12 @@ struct SInitialWorkspaceToken { class CWindow { public: + static PHLWINDOW create(SP); + // xwl static PHLWINDOW create(); private: + CWindow(SP resource); CWindow(); public: @@ -233,9 +238,9 @@ class CWindow { } events; union { - wlr_xdg_surface* xdg; wlr_xwayland_surface* xwayland; } m_uSurface; + WP m_pXDGSurface; // this is the position and size of the "bounding box" Vector2D m_vPosition = Vector2D(0, 0); @@ -271,6 +276,7 @@ class CWindow { bool m_bWasMaximized = false; uint64_t m_iMonitorID = -1; std::string m_szTitle = ""; + std::string m_szClass = ""; std::string m_szInitialTitle = ""; std::string m_szInitialClass = ""; PHLWORKSPACE m_pWorkspace; @@ -385,7 +391,7 @@ class CWindow { // For the list lookup bool operator==(const CWindow& rhs) { - return m_uSurface.xdg == rhs.m_uSurface.xdg && m_uSurface.xwayland == rhs.m_uSurface.xwayland && m_vPosition == rhs.m_vPosition && m_vSize == rhs.m_vSize && + return m_pXDGSurface == rhs.m_pXDGSurface && m_uSurface.xwayland == rhs.m_uSurface.xwayland && m_vPosition == rhs.m_vPosition && m_vSize == rhs.m_vSize && m_bFadingOut == rhs.m_bFadingOut; } @@ -424,6 +430,7 @@ class CWindow { int workspaceID(); bool onSpecialWorkspace(); void activate(bool force = false); + int surfacesCount(); int getRealBorderSize(); void updateSpecialRenderData(); @@ -450,6 +457,13 @@ class CWindow { void switchWithWindowInGroup(PHLWINDOW pWindow); void setAnimationsToMove(); void onWorkspaceAnimUpdate(); + void onUpdateState(); + void onUpdateMeta(); + std::string fetchTitle(); + std::string fetchClass(); + + // listeners + void onAck(uint32_t serial); // std::unordered_map getEnv(); @@ -457,6 +471,17 @@ class CWindow { // PHLWINDOWREF m_pSelf; + // make private once we move listeners to inside CWindow + struct { + CHyprSignalListener map; + CHyprSignalListener ack; + CHyprSignalListener unmap; + CHyprSignalListener commit; + CHyprSignalListener destroy; + CHyprSignalListener updateState; + CHyprSignalListener updateMetadata; + } listeners; + private: // For hidden windows and stuff bool m_bHidden = false; @@ -520,7 +545,7 @@ struct std::formatter : std::formatter { if (formatMonitor) std::format_to(out, ", monitor: {}", w->m_iMonitorID); if (formatClass) - std::format_to(out, ", class: {}", g_pXWaylandManager->getAppIDClass(w)); + std::format_to(out, ", class: {}", w->m_szClass); return std::format_to(out, "]"); } }; diff --git a/src/events/Events.hpp b/src/events/Events.hpp index 2e627d39..7cc0eb32 100644 --- a/src/events/Events.hpp +++ b/src/events/Events.hpp @@ -18,9 +18,6 @@ namespace Events { // Layer events LISTENER(newLayerSurface); - // Surface XDG (window) - LISTENER(newXDGToplevel); - // Window events DYNLISTENFUNC(commitWindow); DYNLISTENFUNC(mapWindow); diff --git a/src/events/Windows.cpp b/src/events/Windows.cpp index b85ed6e1..384ea9ed 100644 --- a/src/events/Windows.cpp +++ b/src/events/Windows.cpp @@ -8,6 +8,7 @@ #include "../render/Renderer.hpp" #include "../config/ConfigValue.hpp" #include "../protocols/LayerShell.hpp" +#include "../protocols/XDGShell.hpp" // ------------------------------------------------------------ // // __ _______ _ _ _____ ______ _______ // @@ -25,11 +26,10 @@ void addViewCoords(void* pWindow, int* x, int* y) { *y += PWINDOW->m_vRealPosition.goal().y; if (!PWINDOW->m_bIsX11 && PWINDOW->m_bIsMapped) { - wlr_box geom; - wlr_xdg_surface_get_geometry(PWINDOW->m_uSurface.xdg, &geom); + Vector2D pos = PWINDOW->m_pXDGSurface->current.geometry.pos(); - *x -= geom.x; - *y -= geom.y; + *x -= pos.x; + *y -= pos.y; } } @@ -67,11 +67,11 @@ void Events::listener_mapWindow(void* owner, void* data) { PWINDOW->m_bIsMapped = true; PWINDOW->m_bReadyToDelete = false; PWINDOW->m_bFadingOut = false; - PWINDOW->m_szTitle = g_pXWaylandManager->getTitle(PWINDOW); + PWINDOW->m_szTitle = PWINDOW->fetchTitle(); PWINDOW->m_iX11Type = PWINDOW->m_bIsX11 ? (PWINDOW->m_uSurface.xwayland->override_redirect ? 2 : 1) : 1; PWINDOW->m_bFirstMap = true; PWINDOW->m_szInitialTitle = PWINDOW->m_szTitle; - PWINDOW->m_szInitialClass = g_pXWaylandManager->getAppIDClass(PWINDOW); + PWINDOW->m_szInitialClass = PWINDOW->fetchClass(); // check for token std::string requestedWorkspace = ""; @@ -111,9 +111,6 @@ void Events::listener_mapWindow(void* owner, void* data) { if (g_pInputManager->m_bLastFocusOnLS) // waybar fix g_pInputManager->releaseAllMouseButtons(); - // Set all windows tiled regardless of anything - g_pXWaylandManager->setWindowStyleTiled(PWINDOW, WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | WLR_EDGE_BOTTOM); - // checks if the window wants borders and sets the appropriate flag g_pXWaylandManager->checkBorders(PWINDOW); @@ -146,10 +143,8 @@ void Events::listener_mapWindow(void* owner, void* data) { } // window rules - PWINDOW->m_vMatchedRules = g_pConfigManager->getMatchingRules(PWINDOW, false); - bool requestsFullscreen = PWINDOW->m_bWantsInitialFullscreen || - (!PWINDOW->m_bIsX11 && PWINDOW->m_uSurface.xdg->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL && PWINDOW->m_uSurface.xdg->toplevel->requested.fullscreen) || - (PWINDOW->m_bIsX11 && PWINDOW->m_uSurface.xwayland->fullscreen); + PWINDOW->m_vMatchedRules = g_pConfigManager->getMatchingRules(PWINDOW, false); + bool requestsFullscreen = PWINDOW->m_bWantsInitialFullscreen || (PWINDOW->m_bIsX11 && PWINDOW->m_uSurface.xwayland->fullscreen); bool requestsFakeFullscreen = false; bool requestsMaximize = false; bool overridingNoFullscreen = false; @@ -508,19 +503,7 @@ void Events::listener_mapWindow(void* owner, void* data) { PWINDOW->m_fDimPercent.setValueAndWarp(0); } - if (!PWINDOW->m_bIsX11) { - PWINDOW->hyprListener_setTitleWindow.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.set_title, &Events::listener_setTitleWindow, PWINDOW.get(), "XDG Window Late"); - PWINDOW->hyprListener_requestMaximize.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_maximize, &Events::listener_requestMaximize, PWINDOW.get(), - "XDG Window Late"); - PWINDOW->hyprListener_requestMinimize.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_minimize, &Events::listener_requestMinimize, PWINDOW.get(), - "XDG Window Late"); - PWINDOW->hyprListener_requestMove.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_move, &Events::listener_requestMove, PWINDOW.get(), "XDG Window Late"); - PWINDOW->hyprListener_requestResize.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_resize, &Events::listener_requestResize, PWINDOW.get(), - "XDG Window Late"); - PWINDOW->hyprListener_fullscreenWindow.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_fullscreen, &Events::listener_fullscreenWindow, PWINDOW.get(), - "XDG Window Late"); - PWINDOW->hyprListener_ackConfigure.initCallback(&PWINDOW->m_uSurface.xdg->events.ack_configure, &Events::listener_ackConfigure, PWINDOW.get(), "XDG Window Late"); - } else { + if (PWINDOW->m_bIsX11) { PWINDOW->hyprListener_fullscreenWindow.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_fullscreen, &Events::listener_fullscreenWindow, PWINDOW.get(), "XWayland Window Late"); PWINDOW->hyprListener_activateX11.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_activate, &Events::listener_activateX11, PWINDOW.get(), @@ -573,7 +556,7 @@ void Events::listener_mapWindow(void* owner, void* data) { if (*PSWALLOW && std::string{*PSWALLOWREGEX} != STRVAL_EMPTY) { // don't swallow ourselves std::regex rgx(*PSWALLOWREGEX); - if (!std::regex_match(g_pXWaylandManager->getAppIDClass(PWINDOW), rgx)) { + if (!std::regex_match(PWINDOW->m_szClass, rgx)) { // check parent int ppid = getPPIDof(PWINDOW->getPID()); @@ -615,12 +598,12 @@ void Events::listener_mapWindow(void* owner, void* data) { } if (finalFound) { - bool valid = std::regex_match(g_pXWaylandManager->getAppIDClass(finalFound), rgx); + bool valid = std::regex_match(PWINDOW->m_szClass, rgx); if (std::string{*PSWALLOWEXREGEX} != STRVAL_EMPTY) { std::regex exc(*PSWALLOWEXREGEX); - valid = valid && !std::regex_match(g_pXWaylandManager->getTitle(finalFound), exc); + valid = valid && !std::regex_match(PWINDOW->m_szTitle, exc); } // check if it's the window we want & not exempt from getting swallowed @@ -644,7 +627,7 @@ void Events::listener_mapWindow(void* owner, void* data) { Debug::log(LOG, "Map request dispatched, monitor {}, window pos: {:5j}, window size: {:5j}", PMONITOR->szName, PWINDOW->m_vRealPosition.goal(), PWINDOW->m_vRealSize.goal()); auto workspaceID = requestedWorkspace != "" ? requestedWorkspace : PWORKSPACE->m_szName; - g_pEventManager->postEvent(SHyprIPCEvent{"openwindow", std::format("{:x},{},{},{}", PWINDOW, workspaceID, g_pXWaylandManager->getAppIDClass(PWINDOW), PWINDOW->m_szTitle)}); + g_pEventManager->postEvent(SHyprIPCEvent{"openwindow", std::format("{:x},{},{},{}", PWINDOW, workspaceID, PWINDOW->m_szClass, PWINDOW->m_szTitle)}); EMIT_HOOK_EVENT("openWindow", PWINDOW); // apply data from default decos. Borders, shadows. @@ -705,15 +688,6 @@ void Events::listener_unmapWindow(void* owner, void* data) { g_pProtocolManager->m_pToplevelExportProtocolManager->onWindowUnmap(PWINDOW); if (!PWINDOW->m_bIsX11) { - Debug::log(LOG, "Unregistered late callbacks XDG"); - PWINDOW->hyprListener_setTitleWindow.removeCallback(); - PWINDOW->hyprListener_requestMaximize.removeCallback(); - PWINDOW->hyprListener_requestMinimize.removeCallback(); - PWINDOW->hyprListener_requestMove.removeCallback(); - PWINDOW->hyprListener_requestResize.removeCallback(); - PWINDOW->hyprListener_fullscreenWindow.removeCallback(); - PWINDOW->hyprListener_ackConfigure.removeCallback(); - } else { Debug::log(LOG, "Unregistered late callbacks XWL"); PWINDOW->hyprListener_fullscreenWindow.removeCallback(); PWINDOW->hyprListener_activateX11.removeCallback(); @@ -804,29 +778,15 @@ void Events::listener_unmapWindow(void* owner, void* data) { PWINDOW->onUnmap(); } -void Events::listener_ackConfigure(void* owner, void* data) { - PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); - const auto E = (wlr_xdg_surface_configure*)data; - - // find last matching serial - const auto SERIAL = std::find_if(PWINDOW->m_vPendingSizeAcks.rbegin(), PWINDOW->m_vPendingSizeAcks.rend(), [&](const auto& e) { return e.first == E->serial; }); - - if (SERIAL == PWINDOW->m_vPendingSizeAcks.rend()) - return; - - PWINDOW->m_pPendingSizeAck = *SERIAL; - std::erase_if(PWINDOW->m_vPendingSizeAcks, [&](const auto& el) { return el.first == SERIAL->first; }); -} - void Events::listener_commitWindow(void* owner, void* data) { PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); - if (!PWINDOW->m_bIsX11 && PWINDOW->m_uSurface.xdg->initial_commit) { + if (!PWINDOW->m_bIsX11 && PWINDOW->m_pXDGSurface->initialCommit) { Vector2D predSize = g_pLayoutManager->getCurrentLayout()->predictSizeForNewWindow(PWINDOW); Debug::log(LOG, "Layout predicts size {} for {}", predSize, PWINDOW); - wlr_xdg_toplevel_set_size(PWINDOW->m_uSurface.xdg->toplevel, predSize.x, predSize.y); + PWINDOW->m_pXDGSurface->toplevel->setSize(predSize); return; } @@ -841,8 +801,8 @@ void Events::listener_commitWindow(void* owner, void* data) { } if (!PWINDOW->m_bIsX11 && !PWINDOW->m_bIsFullscreen && PWINDOW->m_bIsFloating) { - const auto MINSIZE = Vector2D{PWINDOW->m_uSurface.xdg->toplevel->current.min_width, PWINDOW->m_uSurface.xdg->toplevel->current.min_height}; - const auto MAXSIZE = Vector2D{PWINDOW->m_uSurface.xdg->toplevel->current.max_width, PWINDOW->m_uSurface.xdg->toplevel->current.max_height}; + const auto MINSIZE = PWINDOW->m_pXDGSurface->toplevel->current.minSize; + const auto MAXSIZE = PWINDOW->m_pXDGSurface->toplevel->current.maxSize; if (MAXSIZE > Vector2D{1, 1}) { const auto REALSIZE = PWINDOW->m_vRealSize.goal(); @@ -861,7 +821,7 @@ void Events::listener_commitWindow(void* owner, void* data) { PWINDOW->m_vRealPosition = PWINDOW->m_vRealPosition.goal() + DELTA / 2.0; PWINDOW->m_vRealSize = newSize; - g_pXWaylandManager->setWindowSize(PWINDOW, newSize, true); + g_pXWaylandManager->setWindowSize(PWINDOW, newSize); g_pHyprRenderer->damageWindow(PWINDOW); } } @@ -918,11 +878,13 @@ void Events::listener_destroyWindow(void* owner, void* data) { PWINDOW->hyprListener_associateX11.removeCallback(); PWINDOW->hyprListener_dissociateX11.removeCallback(); + PWINDOW->listeners = {}; + g_pLayoutManager->getCurrentLayout()->onWindowRemoved(PWINDOW); PWINDOW->m_bReadyToDelete = true; - PWINDOW->m_uSurface.xdg = nullptr; + PWINDOW->m_pXDGSurface.reset(); if (!PWINDOW->m_bFadingOut) { Debug::log(LOG, "Unmapped {} removed instantly", PWINDOW); @@ -936,31 +898,14 @@ void Events::listener_setTitleWindow(void* owner, void* data) { if (!validMapped(PWINDOW)) return; - const auto NEWTITLE = g_pXWaylandManager->getTitle(PWINDOW); - - if (NEWTITLE == PWINDOW->m_szTitle) - return; - - PWINDOW->m_szTitle = NEWTITLE; - g_pEventManager->postEvent(SHyprIPCEvent{"windowtitle", std::format("{:x}", (uintptr_t)PWINDOW.get())}); - EMIT_HOOK_EVENT("windowTitle", PWINDOW); - - if (PWINDOW == g_pCompositor->m_pLastWindow.lock()) { // if it's the active, let's post an event to update others - g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", g_pXWaylandManager->getAppIDClass(PWINDOW) + "," + PWINDOW->m_szTitle}); - g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", std::format("{:x}", (uintptr_t)PWINDOW.get())}); - EMIT_HOOK_EVENT("activeWindow", PWINDOW); - } - - PWINDOW->updateDynamicRules(); - g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW); - PWINDOW->updateToplevel(); - - Debug::log(LOG, "Window {:x} set title to {}", PWINDOW, PWINDOW->m_szTitle); + PWINDOW->onUpdateMeta(); } void Events::listener_fullscreenWindow(void* owner, void* data) { PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); + // x11 only + if (!PWINDOW->m_bIsMapped) { PWINDOW->m_bWantsInitialFullscreen = true; return; @@ -971,41 +916,13 @@ void Events::listener_fullscreenWindow(void* owner, void* data) { bool requestedFullState = false; - if (!PWINDOW->m_bIsX11) { - const auto REQUESTED = &PWINDOW->m_uSurface.xdg->toplevel->requested; + if (!PWINDOW->m_uSurface.xwayland->surface->mapped) + return; - if (REQUESTED->fullscreen && PWINDOW->m_bIsFullscreen) { - const auto PWORKSPACE = PWINDOW->m_pWorkspace; - if (PWORKSPACE->m_efFullscreenMode != FULLSCREEN_FULL) { - // Store that we were maximized - PWINDOW->m_bWasMaximized = true; - g_pCompositor->setWindowFullscreen(PWINDOW, false, FULLSCREEN_MAXIMIZED); - g_pCompositor->setWindowFullscreen(PWINDOW, true, FULLSCREEN_FULL); - } else - PWINDOW->m_bWasMaximized = false; - } else if (REQUESTED->fullscreen != PWINDOW->m_bIsFullscreen && !PWINDOW->m_bFakeFullscreenState) { - g_pCompositor->setWindowFullscreen(PWINDOW, REQUESTED->fullscreen, FULLSCREEN_FULL); - if (PWINDOW->m_bWasMaximized && !REQUESTED->fullscreen) { - // Was maximized before the fullscreen request, return now back to maximized instead of normal - g_pCompositor->setWindowFullscreen(PWINDOW, true, FULLSCREEN_MAXIMIZED); - } - } + if (!PWINDOW->m_bFakeFullscreenState) + g_pCompositor->setWindowFullscreen(PWINDOW, PWINDOW->m_uSurface.xwayland->fullscreen, FULLSCREEN_FULL); - // Disable the maximize flag when we receive a de-fullscreen request - PWINDOW->m_bWasMaximized &= REQUESTED->fullscreen; - - requestedFullState = REQUESTED->fullscreen; - - wlr_xdg_surface_schedule_configure(PWINDOW->m_uSurface.xdg); - } else { - if (!PWINDOW->m_uSurface.xwayland->surface->mapped) - return; - - if (!PWINDOW->m_bFakeFullscreenState) - g_pCompositor->setWindowFullscreen(PWINDOW, PWINDOW->m_uSurface.xwayland->fullscreen, FULLSCREEN_FULL); - - requestedFullState = PWINDOW->m_uSurface.xwayland->fullscreen; - } + requestedFullState = PWINDOW->m_uSurface.xwayland->fullscreen; if (!requestedFullState && PWINDOW->m_bFakeFullscreenState) { g_pXWaylandManager->setWindowFullscreen(PWINDOW, false); // fixes for apps expecting a de-fullscreen (e.g. ff) @@ -1177,7 +1094,7 @@ void Events::listener_associateX11(void* owner, void* data) { PWINDOW->hyprListener_mapWindow.initCallback(&PWINDOW->m_uSurface.xwayland->surface->events.map, &Events::listener_mapWindow, PWINDOW.get(), "XWayland Window"); PWINDOW->hyprListener_commitWindow.initCallback(&PWINDOW->m_uSurface.xwayland->surface->events.commit, &Events::listener_commitWindow, PWINDOW.get(), "XWayland Window"); - PWINDOW->m_pWLSurface.assign(g_pXWaylandManager->getWindowSurface(PWINDOW), PWINDOW); + PWINDOW->m_pWLSurface.assign(PWINDOW->m_uSurface.xwayland->surface, PWINDOW); } void Events::listener_dissociateX11(void* owner, void* data) { @@ -1211,23 +1128,6 @@ void Events::listener_surfaceXWayland(wl_listener* listener, void* data) { PNEWWINDOW->hyprListener_configureX11.initCallback(&XWSURFACE->events.request_configure, &Events::listener_configureX11, PNEWWINDOW.get(), "XWayland Window"); } -void Events::listener_newXDGToplevel(wl_listener* listener, void* data) { - // A window got opened - const auto XDGTOPLEVEL = (wlr_xdg_toplevel*)data; - const auto XDGSURFACE = XDGTOPLEVEL->base; - - Debug::log(LOG, "New XDG Toplevel created. (class: {})", XDGSURFACE->toplevel->app_id ? XDGSURFACE->toplevel->app_id : "null"); - - const auto PNEWWINDOW = g_pCompositor->m_vWindows.emplace_back(CWindow::create()); - PNEWWINDOW->m_uSurface.xdg = XDGSURFACE; - - PNEWWINDOW->hyprListener_mapWindow.initCallback(&XDGSURFACE->surface->events.map, &Events::listener_mapWindow, PNEWWINDOW.get(), "XDG Window"); - PNEWWINDOW->hyprListener_destroyWindow.initCallback(&XDGSURFACE->events.destroy, &Events::listener_destroyWindow, PNEWWINDOW.get(), "XDG Window"); - PNEWWINDOW->hyprListener_commitWindow.initCallback(&XDGSURFACE->surface->events.commit, &Events::listener_commitWindow, PNEWWINDOW.get(), "XDG Window"); - - PNEWWINDOW->m_pWLSurface.assign(g_pXWaylandManager->getWindowSurface(PNEWWINDOW), PNEWWINDOW); -} - void Events::listener_requestMaximize(void* owner, void* data) { PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); @@ -1240,7 +1140,6 @@ void Events::listener_requestMaximize(void* owner, void* data) { g_pCompositor->setWindowFullscreen(PWINDOW, !PWINDOW->m_bIsFullscreen, FULLSCREEN_MAXIMIZED); // this will be rejected if there already is a fullscreen window - wlr_xdg_surface_schedule_configure(PWINDOW->m_uSurface.xdg); } else { if (!PWINDOW->m_bIsMapped || PWINDOW->m_iX11Type != 1) return; @@ -1269,17 +1168,3 @@ void Events::listener_requestMinimize(void* owner, void* data) { EMIT_HOOK_EVENT("minimize", (std::vector{PWINDOW, (int64_t)(1)})); } } - -void Events::listener_requestMove(void* owner, void* data) { - PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); - - // ignore - wlr_xdg_surface_schedule_configure(PWINDOW->m_uSurface.xdg); -} - -void Events::listener_requestResize(void* owner, void* data) { - PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); - - // ignore - wlr_xdg_surface_schedule_configure(PWINDOW->m_uSurface.xdg); -} diff --git a/src/includes.hpp b/src/includes.hpp index c75796b9..dfbe0221 100644 --- a/src/includes.hpp +++ b/src/includes.hpp @@ -52,8 +52,6 @@ extern "C" { #include #include #include -#include -#include #include #include #include diff --git a/src/layout/IHyprLayout.cpp b/src/layout/IHyprLayout.cpp index 6f7252d2..a31180e1 100644 --- a/src/layout/IHyprLayout.cpp +++ b/src/layout/IHyprLayout.cpp @@ -4,6 +4,7 @@ #include "../render/decorations/CHyprGroupBarDecoration.hpp" #include "../config/ConfigValue.hpp" #include "../desktop/Window.hpp" +#include "../protocols/XDGShell.hpp" void IHyprLayout::onWindowCreated(PHLWINDOW pWindow, eDirection direction) { if (pWindow->m_bIsFloating) { @@ -140,8 +141,12 @@ void IHyprLayout::onWindowCreatedFloating(PHLWINDOW pWindow) { // TODO: detect a popup in a more consistent way. if ((desiredGeometry.x == 0 && desiredGeometry.y == 0) || !visible || !pWindow->m_bIsX11) { - // if it's not, fall back to the center placement - pWindow->m_vRealPosition = PMONITOR->vecPosition + Vector2D((PMONITOR->vecSize.x - desiredGeometry.width) / 2.f, (PMONITOR->vecSize.y - desiredGeometry.height) / 2.f); + // if the pos isn't set, fall back to the center placement if it's not a child, otherwise middle of parent if available + if (!pWindow->m_bIsX11 && pWindow->m_pXDGSurface->toplevel->parent && validMapped(pWindow->m_pXDGSurface->toplevel->parent->window)) + pWindow->m_vRealPosition = pWindow->m_pXDGSurface->toplevel->parent->window->m_vRealPosition.goal() + + pWindow->m_pXDGSurface->toplevel->parent->window->m_vRealSize.goal() / 2.F - desiredGeometry.size() / 2.F; + else + pWindow->m_vRealPosition = PMONITOR->vecPosition + desiredGeometry.size() / 2.F; } else { // if it is, we respect where it wants to put itself, but apply monitor offset if outside // most of these are popups @@ -693,7 +698,7 @@ Vector2D IHyprLayout::predictSizeForNewWindow(PHLWINDOW pWindow) { else sizePredicted = predictSizeForNewWindowFloating(pWindow); - Vector2D maxSize = Vector2D{pWindow->m_uSurface.xdg->toplevel->pending.max_width, pWindow->m_uSurface.xdg->toplevel->pending.max_height}; + Vector2D maxSize = pWindow->m_pXDGSurface->toplevel->pending.maxSize; if ((maxSize.x > 0 && maxSize.x < sizePredicted.x) || (maxSize.y > 0 && maxSize.y < sizePredicted.y)) sizePredicted = {}; diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp index f853e16f..e53eb111 100644 --- a/src/managers/ProtocolManager.cpp +++ b/src/managers/ProtocolManager.cpp @@ -29,6 +29,7 @@ #include "../protocols/LayerShell.hpp" #include "../protocols/PresentationTime.hpp" #include "../protocols/core/Seat.hpp" +#include "../protocols/XDGShell.hpp" CProtocolManager::CProtocolManager() { @@ -64,6 +65,7 @@ CProtocolManager::CProtocolManager() { PROTO::tablet = std::make_unique(&zwp_tablet_manager_v2_interface, 1, "TabletV2"); PROTO::layerShell = std::make_unique(&zwlr_layer_shell_v1_interface, 5, "LayerShell"); PROTO::presentation = std::make_unique(&wp_presentation_interface, 1, "Presentation"); + PROTO::xdgShell = std::make_unique(&xdg_wm_base_interface, 6, "XDGShell"); // Old protocol implementations. // TODO: rewrite them to use hyprwayland-scanner. diff --git a/src/managers/XWaylandManager.cpp b/src/managers/XWaylandManager.cpp index 423c45ca..390c6ece 100644 --- a/src/managers/XWaylandManager.cpp +++ b/src/managers/XWaylandManager.cpp @@ -2,6 +2,7 @@ #include "../Compositor.hpp" #include "../events/Events.hpp" #include "../config/ConfigValue.hpp" +#include "../protocols/XDGShell.hpp" #define OUTPUT_MANAGER_VERSION 3 #define OUTPUT_DONE_DEPRECATED_SINCE_VERSION 3 @@ -34,27 +35,32 @@ CHyprXWaylandManager::~CHyprXWaylandManager() { } wlr_surface* CHyprXWaylandManager::getWindowSurface(PHLWINDOW pWindow) { - if (pWindow->m_bIsX11) - return pWindow->m_uSurface.xwayland->surface; - - return pWindow->m_uSurface.xdg->surface; + return pWindow->m_pWLSurface.wlr(); } void CHyprXWaylandManager::activateSurface(wlr_surface* pSurface, bool activate) { if (!pSurface) return; - if (wlr_xdg_surface_try_from_wlr_surface(pSurface)) { - if (const auto PSURF = wlr_xdg_surface_try_from_wlr_surface(pSurface); PSURF && PSURF->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) - wlr_xdg_toplevel_set_activated(PSURF->toplevel, activate); - - } else if (wlr_xwayland_surface_try_from_wlr_surface(pSurface)) { + if (wlr_xwayland_surface_try_from_wlr_surface(pSurface)) { const auto XSURF = wlr_xwayland_surface_try_from_wlr_surface(pSurface); wlr_xwayland_surface_activate(XSURF, activate); if (activate && !XSURF->override_redirect) wlr_xwayland_surface_restack(XSURF, nullptr, XCB_STACK_MODE_ABOVE); } + + // TODO: + // this cannot be nicely done until we rewrite wlr_surface + for (auto& w : g_pCompositor->m_vWindows) { + if (w->m_bIsX11 || !w->m_bIsMapped) + continue; + + if (w->m_pWLSurface.wlr() != pSurface) + continue; + + w->m_pXDGSurface->toplevel->setActive(activate); + } } void CHyprXWaylandManager::activateWindow(PHLWINDOW pWindow, bool activate) { @@ -68,8 +74,8 @@ void CHyprXWaylandManager::activateWindow(PHLWINDOW pWindow, bool activate) { } wlr_xwayland_surface_activate(pWindow->m_uSurface.xwayland, activate); - } else - wlr_xdg_toplevel_set_activated(pWindow->m_uSurface.xdg->toplevel, activate); + } else if (pWindow->m_pXDGSurface && pWindow->m_pXDGSurface->toplevel) + pWindow->m_pXDGSurface->toplevel->setActive(activate); if (activate) { g_pCompositor->m_pLastFocus = getWindowSurface(pWindow); @@ -95,64 +101,15 @@ void CHyprXWaylandManager::getGeometryForWindow(PHLWINDOW pWindow, CBox* pbox) { pbox->width = pWindow->m_uSurface.xwayland->width; pbox->height = pWindow->m_uSurface.xwayland->height; } - } else { - wlr_xdg_surface_get_geometry(pWindow->m_uSurface.xdg, pbox->pWlr()); - pbox->applyFromWlr(); - } -} - -std::string CHyprXWaylandManager::getTitle(PHLWINDOW pWindow) { - try { - if (pWindow->m_bIsX11) { - if (!pWindow->m_bIsMapped) - return ""; - - if (pWindow->m_uSurface.xwayland && pWindow->m_uSurface.xwayland->title) { - return std::string(pWindow->m_uSurface.xwayland->title); - } - } else if (pWindow->m_uSurface.xdg) { - if (pWindow->m_bFadingOut || !pWindow->m_uSurface.xdg) - return ""; - - if (pWindow->m_uSurface.xdg->toplevel && pWindow->m_uSurface.xdg->toplevel->title) { - return std::string(pWindow->m_uSurface.xdg->toplevel->title); - } - } else { - return ""; - } - } catch (...) { Debug::log(ERR, "Error in getTitle (probably null title)"); } - - return ""; -} - -std::string CHyprXWaylandManager::getAppIDClass(PHLWINDOW pWindow) { - try { - if (pWindow->m_bIsX11) { - if (!pWindow->m_bIsMapped) - return ""; - - if (pWindow->m_uSurface.xwayland && pWindow->m_uSurface.xwayland->_class) { - return std::string(pWindow->m_uSurface.xwayland->_class); - } - } else if (pWindow->m_uSurface.xdg) { - if (pWindow->m_bFadingOut || !pWindow->m_uSurface.xdg) - return ""; - - if (pWindow->m_uSurface.xdg->toplevel && pWindow->m_uSurface.xdg->toplevel->app_id) { - return std::string(pWindow->m_uSurface.xdg->toplevel->app_id); - } - } else - return ""; - } catch (std::logic_error& e) { Debug::log(ERR, "Error in getAppIDClass: {}", e.what()); } - - return ""; + } else if (pWindow->m_pXDGSurface) + *pbox = pWindow->m_pXDGSurface->current.geometry; } void CHyprXWaylandManager::sendCloseWindow(PHLWINDOW pWindow) { if (pWindow->m_bIsX11) wlr_xwayland_surface_close(pWindow->m_uSurface.xwayland); - else - wlr_xdg_toplevel_send_close(pWindow->m_uSurface.xdg->toplevel); + else if (pWindow->m_pXDGSurface && pWindow->m_pXDGSurface->toplevel) + pWindow->m_pXDGSurface->toplevel->close(); } void CHyprXWaylandManager::setWindowSize(PHLWINDOW pWindow, Vector2D size, bool force) { @@ -189,23 +146,12 @@ void CHyprXWaylandManager::setWindowSize(PHLWINDOW pWindow, Vector2D size, bool if (pWindow->m_bIsX11) wlr_xwayland_surface_configure(pWindow->m_uSurface.xwayland, windowPos.x, windowPos.y, size.x, size.y); - else - pWindow->m_vPendingSizeAcks.push_back(std::make_pair<>(wlr_xdg_toplevel_set_size(pWindow->m_uSurface.xdg->toplevel, size.x, size.y), size.floor())); -} - -void CHyprXWaylandManager::setWindowStyleTiled(PHLWINDOW pWindow, uint32_t edgez) { - if (pWindow->m_bIsX11) - return; - - wlr_xdg_toplevel_set_tiled(pWindow->m_uSurface.xdg->toplevel, edgez); - wlr_xdg_toplevel_set_maximized(pWindow->m_uSurface.xdg->toplevel, true); + else if (pWindow->m_pXDGSurface->toplevel) + pWindow->m_vPendingSizeAcks.push_back(std::make_pair<>(pWindow->m_pXDGSurface->toplevel->setSize(size), size.floor())); } wlr_surface* CHyprXWaylandManager::surfaceAt(PHLWINDOW pWindow, const Vector2D& client, Vector2D& surface) { - if (pWindow->m_bIsX11) - return wlr_surface_surface_at(pWindow->m_uSurface.xwayland->surface, client.x, client.y, &surface.x, &surface.y); - - return wlr_xdg_surface_surface_at(pWindow->m_uSurface.xdg, client.x, client.y, &surface.x, &surface.y); + return wlr_surface_surface_at(pWindow->m_pWLSurface.wlr(), client.x, client.y, &surface.x, &surface.y); } bool CHyprXWaylandManager::shouldBeFloated(PHLWINDOW pWindow, bool pending) { @@ -251,10 +197,10 @@ bool CHyprXWaylandManager::shouldBeFloated(PHLWINDOW pWindow, bool pending) { if (SIZEHINTS && (pWindow->m_uSurface.xwayland->parent || ((SIZEHINTS->min_width == SIZEHINTS->max_width) && (SIZEHINTS->min_height == SIZEHINTS->max_height)))) return true; } else { - const auto PSTATE = pending ? &pWindow->m_uSurface.xdg->toplevel->pending : &pWindow->m_uSurface.xdg->toplevel->current; + const auto PSTATE = pending ? &pWindow->m_pXDGSurface->toplevel->pending : &pWindow->m_pXDGSurface->toplevel->current; - if ((PSTATE->min_width != 0 && PSTATE->min_height != 0 && (PSTATE->min_width == PSTATE->max_width || PSTATE->min_height == PSTATE->max_height)) || - pWindow->m_uSurface.xdg->toplevel->parent) + if (pWindow->m_pXDGSurface->toplevel->parent || + (PSTATE->minSize.x != 0 && PSTATE->minSize.y != 0 && (PSTATE->minSize.x == PSTATE->maxSize.x || PSTATE->minSize.y == PSTATE->maxSize.y))) return true; } @@ -297,20 +243,19 @@ void CHyprXWaylandManager::checkBorders(PHLWINDOW pWindow) { void CHyprXWaylandManager::setWindowFullscreen(PHLWINDOW pWindow, bool fullscreen) { if (pWindow->m_bIsX11) wlr_xwayland_surface_set_fullscreen(pWindow->m_uSurface.xwayland, fullscreen); - else - wlr_xdg_toplevel_set_fullscreen(pWindow->m_uSurface.xdg->toplevel, fullscreen); + else if (pWindow->m_pXDGSurface && pWindow->m_pXDGSurface->toplevel) + pWindow->m_pXDGSurface->toplevel->setFullscreen(fullscreen); } Vector2D CHyprXWaylandManager::getMaxSizeForWindow(PHLWINDOW pWindow) { if (!validMapped(pWindow)) return Vector2D(99999, 99999); - if ((pWindow->m_bIsX11 && !pWindow->m_uSurface.xwayland->size_hints) || (!pWindow->m_bIsX11 && !pWindow->m_uSurface.xdg->toplevel) || - pWindow->m_sAdditionalConfigData.noMaxSize) + if ((pWindow->m_bIsX11 && !pWindow->m_uSurface.xwayland->size_hints) || (!pWindow->m_bIsX11 && !pWindow->m_pXDGSurface->toplevel) || pWindow->m_sAdditionalConfigData.noMaxSize) return Vector2D(99999, 99999); auto MAXSIZE = pWindow->m_bIsX11 ? Vector2D(pWindow->m_uSurface.xwayland->size_hints->max_width, pWindow->m_uSurface.xwayland->size_hints->max_height) : - Vector2D(pWindow->m_uSurface.xdg->toplevel->current.max_width, pWindow->m_uSurface.xdg->toplevel->current.max_height); + pWindow->m_pXDGSurface->toplevel->current.maxSize; if (MAXSIZE.x < 5) MAXSIZE.x = 99999; @@ -324,11 +269,11 @@ Vector2D CHyprXWaylandManager::getMinSizeForWindow(PHLWINDOW pWindow) { if (!validMapped(pWindow)) return Vector2D(0, 0); - if ((pWindow->m_bIsX11 && !pWindow->m_uSurface.xwayland->size_hints) || (!pWindow->m_bIsX11 && !pWindow->m_uSurface.xdg->toplevel)) + if ((pWindow->m_bIsX11 && !pWindow->m_uSurface.xwayland->size_hints) || (!pWindow->m_bIsX11 && !pWindow->m_pXDGSurface->toplevel)) return Vector2D(0, 0); auto MINSIZE = pWindow->m_bIsX11 ? Vector2D(pWindow->m_uSurface.xwayland->size_hints->min_width, pWindow->m_uSurface.xwayland->size_hints->min_height) : - Vector2D(pWindow->m_uSurface.xdg->toplevel->current.min_width, pWindow->m_uSurface.xdg->toplevel->current.min_height); + pWindow->m_pXDGSurface->toplevel->current.minSize; MINSIZE = MINSIZE.clamp({1, 1}); diff --git a/src/managers/XWaylandManager.hpp b/src/managers/XWaylandManager.hpp index 6edadd71..8889eb61 100644 --- a/src/managers/XWaylandManager.hpp +++ b/src/managers/XWaylandManager.hpp @@ -17,11 +17,8 @@ class CHyprXWaylandManager { void activateSurface(wlr_surface*, bool); void activateWindow(PHLWINDOW, bool); void getGeometryForWindow(PHLWINDOW, CBox*); - std::string getTitle(PHLWINDOW); - std::string getAppIDClass(PHLWINDOW); void sendCloseWindow(PHLWINDOW); void setWindowSize(PHLWINDOW, Vector2D, bool force = false); - void setWindowStyleTiled(PHLWINDOW, uint32_t); void setWindowFullscreen(PHLWINDOW, bool); wlr_surface* surfaceAt(PHLWINDOW, const Vector2D&, Vector2D&); bool shouldBeFloated(PHLWINDOW, bool pending = false); diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index 62d9e687..bb0ab65a 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -15,6 +15,7 @@ #include "../../protocols/VirtualPointer.hpp" #include "../../protocols/LayerShell.hpp" #include "../../protocols/core/Seat.hpp" +#include "../../protocols/XDGShell.hpp" #include "../../devices/Mouse.hpp" #include "../../devices/VirtualPointer.hpp" @@ -387,10 +388,9 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { if (pFoundWindow && !pFoundWindow->m_bIsX11 && surfacePos != Vector2D(-1337, -1337)) { // calc for oversized windows... fucking bullshit. - wlr_box geom; - wlr_xdg_surface_get_geometry(pFoundWindow->m_uSurface.xdg, &geom); + CBox geom = pFoundWindow->m_pXDGSurface->current.geometry; - surfaceLocal = mouseCoords - surfacePos + Vector2D(geom.x, geom.y); + surfaceLocal = mouseCoords - surfacePos + geom.pos(); } if (pFoundWindow && pFoundWindow->m_bIsX11) // for x11 force scale zero diff --git a/src/protocols/ForeignToplevel.cpp b/src/protocols/ForeignToplevel.cpp index 0d2a7e57..40420da7 100644 --- a/src/protocols/ForeignToplevel.cpp +++ b/src/protocols/ForeignToplevel.cpp @@ -91,7 +91,7 @@ void CForeignToplevelList::onClass(PHLWINDOW pWindow) { if (!H || H->closed) return; - H->resource->sendAppId(g_pXWaylandManager->getAppIDClass(pWindow).c_str()); + H->resource->sendAppId(pWindow->m_szClass.c_str()); } void CForeignToplevelList::onUnmap(PHLWINDOW pWindow) { diff --git a/src/protocols/ForeignToplevelWlr.cpp b/src/protocols/ForeignToplevelWlr.cpp index 1762ad2a..782fbc0e 100644 --- a/src/protocols/ForeignToplevelWlr.cpp +++ b/src/protocols/ForeignToplevelWlr.cpp @@ -195,7 +195,7 @@ void CForeignToplevelWlrManager::onMap(PHLWINDOW pWindow) { LOGM(LOG, "Newly mapped window {:016x}", (uintptr_t)pWindow.get()); resource->sendToplevel(NEWHANDLE->resource.get()); - NEWHANDLE->resource->sendAppId(g_pXWaylandManager->getAppIDClass(pWindow).c_str()); + NEWHANDLE->resource->sendAppId(pWindow->m_szClass.c_str()); NEWHANDLE->resource->sendTitle(pWindow->m_szTitle.c_str()); if (const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID); PMONITOR) NEWHANDLE->sendMonitor(PMONITOR); @@ -231,7 +231,7 @@ void CForeignToplevelWlrManager::onClass(PHLWINDOW pWindow) { if (!H || H->closed) return; - H->resource->sendAppId(g_pXWaylandManager->getAppIDClass(pWindow).c_str()); + H->resource->sendAppId(pWindow->m_szClass.c_str()); H->resource->sendDone(); } diff --git a/src/protocols/LayerShell.cpp b/src/protocols/LayerShell.cpp index ced765fd..962b89a3 100644 --- a/src/protocols/LayerShell.cpp +++ b/src/protocols/LayerShell.cpp @@ -1,5 +1,6 @@ #include "LayerShell.hpp" #include "../Compositor.hpp" +#include "XDGShell.hpp" #define LOGM PROTO::layerShell->protoLog @@ -118,14 +119,14 @@ CLayerShellResource::CLayerShellResource(SP resource_, wlr_ }); resource->setGetPopup([this](CZwlrLayerSurfaceV1* r, wl_resource* popup_) { - auto popup = wlr_xdg_popup_from_resource(popup_); + auto popup = CXDGPopupResource::fromResource(popup_); - if (popup->parent) { + if (popup->taken) { r->error(-1, "Parent already exists!"); return; } - popup->parent = surface; + popup->taken = true; events.newPopup.emit(popup); }); diff --git a/src/protocols/XDGShell.cpp b/src/protocols/XDGShell.cpp new file mode 100644 index 00000000..fcb2d63b --- /dev/null +++ b/src/protocols/XDGShell.cpp @@ -0,0 +1,697 @@ +#include "XDGShell.hpp" +#include +#include "../Compositor.hpp" + +#define LOGM PROTO::xdgShell->protoLog + +CXDGPopupResource::CXDGPopupResource(SP resource_, SP owner_, SP surface_, SP positioner) : + surface(surface_), parent(owner_), resource(resource_), positionerRules(positioner) { + if (!good()) + return; + + resource->setData(this); + + resource->setDestroy([this](CXdgPopup* r) { + if (surface && surface->mapped) + surface->events.unmap.emit(); + events.destroy.emit(); + PROTO::xdgShell->destroyResource(this); + }); + resource->setOnDestroy([this](CXdgPopup* r) { + if (surface && surface->mapped) + surface->events.unmap.emit(); + events.destroy.emit(); + PROTO::xdgShell->destroyResource(this); + }); + + resource->setReposition([this](CXdgPopup* r, wl_resource* positionerRes, uint32_t token) { + LOGM(LOG, "Popup {:x} asks for reposition", (uintptr_t)this); + lastRepositionToken = token; + auto pos = CXDGPositionerResource::fromResource(positionerRes); + if (!pos) + return; + positionerRules = CXDGPositionerRules{pos}; + events.reposition.emit(); + }); + + if (parent) + taken = true; +} + +CXDGPopupResource::~CXDGPopupResource() { + events.destroy.emit(); +} + +void CXDGPopupResource::applyPositioning(const CBox& box, const Vector2D& t1coord) { + CBox constraint = box.copy().translate(surface->pending.geometry.pos()); + + geometry = positionerRules.getPosition(constraint, accumulateParentOffset() + t1coord); + + LOGM(LOG, "Popup {:x} gets unconstrained to {} {}", (uintptr_t)this, geometry.pos(), geometry.size()); + + configure(geometry); + + if (lastRepositionToken) + repositioned(); +} + +Vector2D CXDGPopupResource::accumulateParentOffset() { + SP current = parent.lock(); + Vector2D off; + while (current) { + off += current->current.geometry.pos(); + if (current->popup) { + off += current->popup->geometry.pos(); + current = current->popup->parent.lock(); + } else + break; + } + return off; +} + +SP CXDGPopupResource::fromResource(wl_resource* res) { + auto data = (CXDGPopupResource*)(((CXdgPopup*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; +} + +bool CXDGPopupResource::good() { + return resource->resource(); +} + +void CXDGPopupResource::configure(const CBox& box) { + resource->sendConfigure(box.x, box.y, box.w, box.h); + if (surface) + surface->scheduleConfigure(); +} + +void CXDGPopupResource::done() { + resource->sendPopupDone(); +} + +void CXDGPopupResource::repositioned() { + if (!lastRepositionToken) + return; + + LOGM(LOG, "repositioned: sending reposition token {}", lastRepositionToken); + + resource->sendRepositioned(lastRepositionToken); + lastRepositionToken = 0; +} + +CXDGToplevelResource::CXDGToplevelResource(SP resource_, SP owner_) : owner(owner_), resource(resource_) { + if (!good()) + return; + + resource->setData(this); + + resource->setDestroy([this](CXdgToplevel* r) { + events.destroy.emit(); + PROTO::xdgShell->destroyResource(this); + }); + resource->setOnDestroy([this](CXdgToplevel* r) { + events.destroy.emit(); + PROTO::xdgShell->destroyResource(this); + }); + + if (resource->version() >= 5) { + wl_array arr; + wl_array_init(&arr); + auto p = (uint32_t*)wl_array_add(&arr, sizeof(uint32_t)); + *p = XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN; + p = (uint32_t*)wl_array_add(&arr, sizeof(uint32_t)); + *p = XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE; + resource->sendWmCapabilities(&arr); + wl_array_release(&arr); + } + + pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_LEFT); + pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_RIGHT); + pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_TOP); + pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_BOTTOM); + + resource->setSetTitle([this](CXdgToplevel* r, const char* t) { + state.title = t; + events.metadataChanged.emit(); + }); + + resource->setSetAppId([this](CXdgToplevel* r, const char* id) { + state.appid = id; + events.metadataChanged.emit(); + }); + + resource->setSetMaxSize([this](CXdgToplevel* r, int32_t x, int32_t y) { + pending.maxSize = {x, y}; + events.sizeLimitsChanged.emit(); + }); + + resource->setSetMinSize([this](CXdgToplevel* r, int32_t x, int32_t y) { + pending.minSize = {x, y}; + events.sizeLimitsChanged.emit(); + }); + + resource->setSetMaximized([this](CXdgToplevel* r) { + state.requestsMaximize = true; + events.stateChanged.emit(); + state.requestsMaximize.reset(); + }); + + resource->setUnsetMaximized([this](CXdgToplevel* r) { + state.requestsMaximize = false; + events.stateChanged.emit(); + state.requestsMaximize.reset(); + }); + + resource->setSetFullscreen([this](CXdgToplevel* r, wl_resource* output) { + state.requestsFullscreen = true; + events.stateChanged.emit(); + state.requestsFullscreen.reset(); + }); + + resource->setUnsetFullscreen([this](CXdgToplevel* r) { + state.requestsFullscreen = false; + events.stateChanged.emit(); + state.requestsFullscreen.reset(); + }); + + resource->setSetMinimized([this](CXdgToplevel* r) { + state.requestsMinimize = true; + events.stateChanged.emit(); + state.requestsFullscreen.reset(); + }); + + resource->setSetParent([this](CXdgToplevel* r, wl_resource* parentR) { + auto newp = parentR ? CXDGToplevelResource::fromResource(parentR) : nullptr; + parent = newp; + + LOGM(LOG, "Toplevel {:x} sets parent to {:x}", (uintptr_t)this, (uintptr_t)newp.get()); + }); +} + +CXDGToplevelResource::~CXDGToplevelResource() { + events.destroy.emit(); +} + +SP CXDGToplevelResource::fromResource(wl_resource* res) { + auto data = (CXDGToplevelResource*)(((CXdgToplevel*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; +} + +bool CXDGToplevelResource::good() { + return resource->resource(); +} + +uint32_t CXDGToplevelResource::setSize(const Vector2D& size) { + pendingApply.size = size; + applyState(); + return owner->scheduleConfigure(); +} + +uint32_t CXDGToplevelResource::setMaximized(bool maximized) { + bool set = std::find(pendingApply.states.begin(), pendingApply.states.end(), XDG_TOPLEVEL_STATE_MAXIMIZED) != pendingApply.states.end(); + + if (maximized == set) + return owner->scheduledSerial; + + if (maximized && !set) + pendingApply.states.push_back(XDG_TOPLEVEL_STATE_MAXIMIZED); + else if (!maximized && set) + std::erase(pendingApply.states, XDG_TOPLEVEL_STATE_MAXIMIZED); + applyState(); + return owner->scheduleConfigure(); +} + +uint32_t CXDGToplevelResource::setFullscreen(bool fullscreen) { + bool set = std::find(pendingApply.states.begin(), pendingApply.states.end(), XDG_TOPLEVEL_STATE_FULLSCREEN) != pendingApply.states.end(); + + if (fullscreen == set) + return owner->scheduledSerial; + + if (fullscreen && !set) + pendingApply.states.push_back(XDG_TOPLEVEL_STATE_FULLSCREEN); + else if (!fullscreen && set) + std::erase(pendingApply.states, XDG_TOPLEVEL_STATE_FULLSCREEN); + applyState(); + return owner->scheduleConfigure(); +} + +uint32_t CXDGToplevelResource::setActive(bool active) { + bool set = std::find(pendingApply.states.begin(), pendingApply.states.end(), XDG_TOPLEVEL_STATE_ACTIVATED) != pendingApply.states.end(); + + if (active == set) + return owner->scheduledSerial; + + if (active && !set) + pendingApply.states.push_back(XDG_TOPLEVEL_STATE_ACTIVATED); + else if (!active && set) + std::erase(pendingApply.states, XDG_TOPLEVEL_STATE_ACTIVATED); + applyState(); + return owner->scheduleConfigure(); +} + +uint32_t CXDGToplevelResource::setSuspeneded(bool sus) { + bool set = std::find(pendingApply.states.begin(), pendingApply.states.end(), XDG_TOPLEVEL_STATE_SUSPENDED) != pendingApply.states.end(); + + if (sus == set) + return owner->scheduledSerial; + + if (sus && !set) + pendingApply.states.push_back(XDG_TOPLEVEL_STATE_SUSPENDED); + else if (!sus && set) + std::erase(pendingApply.states, XDG_TOPLEVEL_STATE_SUSPENDED); + applyState(); + return owner->scheduleConfigure(); +} + +void CXDGToplevelResource::applyState() { + wl_array arr; + wl_array_init(&arr); + wl_array_add(&arr, pendingApply.states.size() * sizeof(int)); + memcpy(arr.data, pendingApply.states.data(), pendingApply.states.size() * sizeof(int)); + + resource->sendConfigure(pendingApply.size.x, pendingApply.size.y, &arr); + + wl_array_release(&arr); +} + +void CXDGToplevelResource::close() { + resource->sendClose(); +} + +CXDGSurfaceResource::CXDGSurfaceResource(SP resource_, SP owner_, wlr_surface* surface_) : owner(owner_), surface(surface_), resource(resource_) { + if (!good()) + return; + + resource->setData(this); + + resource->setDestroy([this](CXdgSurface* r) { + if (mapped) + events.unmap.emit(); + events.destroy.emit(); + PROTO::xdgShell->destroyResource(this); + }); + resource->setOnDestroy([this](CXdgSurface* r) { + if (mapped) + events.unmap.emit(); + events.destroy.emit(); + PROTO::xdgShell->destroyResource(this); + }); + + hyprListener_surfaceDestroy.initCallback( + &surface->events.destroy, + [this](void* owner, void* data) { + LOGM(WARN, "wl_surface destroyed before its xdg_surface role object"); + hyprListener_surfaceDestroy.removeCallback(); + hyprListener_surfaceCommit.removeCallback(); + + if (mapped) + events.unmap.emit(); + + mapped = false; + surface = nullptr; + events.destroy.emit(); + }, + nullptr, "CXDGSurfaceResource"); + + hyprListener_surfaceCommit.initCallback( + &surface->events.commit, + [this](void* owner, void* data) { + current = pending; + if (toplevel) + toplevel->current = toplevel->pending; + + if (initialCommit && surface->pending.buffer_width > 0 && surface->pending.buffer_height > 0) { + resource->error(-1, "Buffer attached before initial commit"); + return; + } + + if (surface->pending.buffer_width > 0 && surface->pending.buffer_height > 0 && !mapped) { + // this forces apps to not draw CSD. + if (toplevel) + toplevel->setMaximized(true); + + mapped = true; + wlr_surface_map(surface); + events.map.emit(); + return; + } + + if (surface->pending.buffer_width <= 0 && surface->pending.buffer_height <= 0 && mapped) { + mapped = false; + wlr_surface_unmap(surface); + events.unmap.emit(); + return; + } + + events.commit.emit(); + initialCommit = false; + }, + nullptr, "CXDGSurfaceResource"); + + resource->setGetToplevel([this](CXdgSurface* r, uint32_t id) { + const auto RESOURCE = PROTO::xdgShell->m_vToplevels.emplace_back(makeShared(makeShared(r->client(), r->version(), id), self.lock())); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::xdgShell->m_vToplevels.pop_back(); + return; + } + + toplevel = RESOURCE; + toplevel->self = RESOURCE; + + LOGM(LOG, "xdg_surface {:x} gets a toplevel {:x}", (uintptr_t)owner.get(), (uintptr_t)RESOURCE.get()); + + g_pCompositor->m_vWindows.emplace_back(CWindow::create(self.lock())); + + for (auto& p : popups) { + if (!p) + continue; + events.newPopup.emit(p); + } + }); + + resource->setGetPopup([this](CXdgSurface* r, uint32_t id, wl_resource* parentXDG, wl_resource* positionerRes) { + auto parent = parentXDG ? CXDGSurfaceResource::fromResource(parentXDG) : nullptr; + auto positioner = CXDGPositionerResource::fromResource(positionerRes); + const auto RESOURCE = + PROTO::xdgShell->m_vPopups.emplace_back(makeShared(makeShared(r->client(), r->version(), id), parent, self.lock(), positioner)); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::xdgShell->m_vPopups.pop_back(); + return; + } + + popup = RESOURCE; + RESOURCE->self = RESOURCE; + + LOGM(LOG, "xdg_surface {:x} gets a popup {:x} owner {:x}", (uintptr_t)self.get(), (uintptr_t)RESOURCE.get(), (uintptr_t)parent.get()); + + if (!parent) + return; + + parent->popups.emplace_back(RESOURCE); + if (parent->mapped) + parent->events.newPopup.emit(RESOURCE); + }); + + resource->setAckConfigure([this](CXdgSurface* r, uint32_t serial) { + events.ack.emit(serial); + ; // TODO: verify it + }); + + resource->setSetWindowGeometry([this](CXdgSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) { + LOGM(LOG, "xdg_surface {:x} requests geometry {}x{} {}x{}", (uintptr_t)this, x, y, w, h); + pending.geometry = {x, y, w, h}; + }); +} + +CXDGSurfaceResource::~CXDGSurfaceResource() { + events.destroy.emit(); + if (configureSource) + wl_event_source_remove(configureSource); +} + +bool CXDGSurfaceResource::good() { + return resource->resource(); +} + +SP CXDGSurfaceResource::fromResource(wl_resource* res) { + auto data = (CXDGSurfaceResource*)(((CXdgSurface*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; +} + +static void onConfigure(void* data) { + ((CXDGSurfaceResource*)data)->configure(); +} + +uint32_t CXDGSurfaceResource::scheduleConfigure() { + if (configureSource) + return scheduledSerial; + + configureSource = wl_event_loop_add_idle(g_pCompositor->m_sWLEventLoop, onConfigure, this); + scheduledSerial = wl_display_next_serial(g_pCompositor->m_sWLDisplay); + + return scheduledSerial; +} + +void CXDGSurfaceResource::configure() { + configureSource = nullptr; + resource->sendConfigure(scheduledSerial); +} + +CXDGPositionerResource::CXDGPositionerResource(SP resource_, SP owner_) : owner(owner_), resource(resource_) { + if (!good()) + return; + + resource->setData(this); + + resource->setDestroy([this](CXdgPositioner* r) { PROTO::xdgShell->destroyResource(this); }); + resource->setOnDestroy([this](CXdgPositioner* r) { PROTO::xdgShell->destroyResource(this); }); + + resource->setSetSize([this](CXdgPositioner* r, int32_t x, int32_t y) { + if (x <= 0 || y <= 0) { + r->error(XDG_POSITIONER_ERROR_INVALID_INPUT, "Invalid size"); + return; + } + + state.requestedSize = {x, y}; + }); + + resource->setSetAnchorRect([this](CXdgPositioner* r, int32_t x, int32_t y, int32_t w, int32_t h) { + if (w <= 0 || h <= 0) { + r->error(XDG_POSITIONER_ERROR_INVALID_INPUT, "Invalid box"); + return; + } + + state.anchorRect = {x, y, w, h}; + }); + + resource->setSetOffset([this](CXdgPositioner* r, int32_t x, int32_t y) { state.offset = {x, y}; }); + + resource->setSetAnchor([this](CXdgPositioner* r, xdgPositionerAnchor a) { state.anchor = a; }); + + resource->setSetGravity([this](CXdgPositioner* r, xdgPositionerGravity g) { state.gravity = g; }); + + resource->setSetConstraintAdjustment([this](CXdgPositioner* r, xdgPositionerConstraintAdjustment a) { state.constraintAdjustment = (uint32_t)a; }); + + // TODO: support this shit better. The current impl _works_, but is lacking and could be wrong in a few cases. + // doesn't matter _that_ much for now, though. +} + +SP CXDGPositionerResource::fromResource(wl_resource* res) { + auto data = (CXDGPositionerResource*)(((CXdgPositioner*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; +} + +bool CXDGPositionerResource::good() { + return resource->resource(); +} + +CXDGPositionerRules::CXDGPositionerRules(SP positioner) { + state = positioner->state; +} + +static Vector2D pointForAnchor(const CBox& box, xdgPositionerAnchor anchor) { + switch (anchor) { + case XDG_POSITIONER_ANCHOR_TOP: return box.pos() + Vector2D{box.size().x / 2.F, 0}; + case XDG_POSITIONER_ANCHOR_BOTTOM: return box.pos() + Vector2D{box.size().x / 2.F, box.size().y}; + case XDG_POSITIONER_ANCHOR_LEFT: return box.pos() + Vector2D{0, box.size().y / 2.F}; + case XDG_POSITIONER_ANCHOR_RIGHT: return box.pos() + Vector2D{box.size().x, box.size().y / 2.F}; + case XDG_POSITIONER_ANCHOR_TOP_LEFT: return box.pos(); + case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT: return box.pos() + Vector2D{0, box.size().y}; + case XDG_POSITIONER_ANCHOR_TOP_RIGHT: return box.pos() + Vector2D{box.size().x, 0}; + case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT: return box.pos() + Vector2D{box.size().x, box.size().y}; + default: return box.pos(); + } + + return {}; +} + +CBox CXDGPositionerRules::getPosition(const CBox& constraint, const Vector2D& parentCoord) { + + Debug::log(LOG, "GetPosition with constraint {} {} and parent {}", constraint.pos(), constraint.size(), parentCoord); + + CBox predictedBox = {parentCoord + constraint.pos() + pointForAnchor(state.anchorRect, state.anchor) + state.offset, state.requestedSize}; + + bool success = predictedBox.inside(constraint); + + if (success) + return predictedBox.translate(-parentCoord - constraint.pos()); + + if (state.constraintAdjustment & (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y)) { + // attempt to flip + const bool flipX = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X; + const bool flipY = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y; + + CBox test = predictedBox; + success = true; + if (flipX && test.copy().translate(Vector2D{-predictedBox.w - state.anchorRect.w, 0}).expand(-1).inside(constraint)) + test.translate(Vector2D{-predictedBox.w - state.anchorRect.w, 0}); + else if (flipY && test.copy().translate(Vector2D{0, -predictedBox.h - state.anchorRect.h}).expand(-1).inside(constraint)) + test.translate(Vector2D{0, -predictedBox.h - state.anchorRect.h}); + else if (flipX && flipY && test.copy().translate(Vector2D{-predictedBox.w - state.anchorRect.w, -predictedBox.h - state.anchorRect.h}).expand(-1).inside(constraint)) + test.translate(Vector2D{-predictedBox.w - state.anchorRect.w, -predictedBox.h - state.anchorRect.h}); + else + success = false; + + if (success) + return test.translate(-parentCoord - constraint.pos()); + } + + if (state.constraintAdjustment & (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y)) { + // attempt to slide + const bool slideX = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X; + const bool slideY = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y; + + //const bool gravityLeft = state.gravity == XDG_POSITIONER_GRAVITY_NONE || state.gravity == XDG_POSITIONER_GRAVITY_LEFT || state.gravity == XDG_POSITIONER_GRAVITY_TOP_LEFT || state.gravity == XDG_POSITIONER_GRAVITY_BOTTOM_LEFT; + //const bool gravityTop = state.gravity == XDG_POSITIONER_GRAVITY_NONE || state.gravity == XDG_POSITIONER_GRAVITY_TOP || state.gravity == XDG_POSITIONER_GRAVITY_TOP_LEFT || state.gravity == XDG_POSITIONER_GRAVITY_TOP_RIGHT; + + const bool leftEdgeOut = predictedBox.x < constraint.x; + const bool topEdgeOut = predictedBox.y < constraint.y; + const bool rightEdgeOut = predictedBox.x + predictedBox.w > constraint.x + constraint.w; + const bool bottomEdgeOut = predictedBox.y + predictedBox.h > constraint.y + constraint.h; + + CBox test = predictedBox; + + // TODO: this isn't truly conformant. + if (leftEdgeOut && slideX) + test.x = constraint.x; + if (rightEdgeOut && slideX) + test.x = constraint.x + constraint.w - predictedBox.w; + if (topEdgeOut && slideY) + test.y = constraint.y; + if (bottomEdgeOut && slideY) + test.y = constraint.y + constraint.h - predictedBox.y; + + success = test.copy().expand(-1).inside(constraint); + + if (success) + return test.translate(-parentCoord - constraint.pos()); + } + + if (state.constraintAdjustment & (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y)) { + const bool resizeX = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X; + const bool resizeY = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y; + + const bool leftEdgeOut = predictedBox.x < constraint.x; + const bool topEdgeOut = predictedBox.y < constraint.y; + const bool rightEdgeOut = predictedBox.x + predictedBox.w > constraint.x + constraint.w; + const bool bottomEdgeOut = predictedBox.y + predictedBox.h > constraint.y + constraint.h; + + CBox test = predictedBox; + + // TODO: this isn't truly conformant. + if (leftEdgeOut && resizeX) { + test.w = test.x + test.w - constraint.x; + test.x = constraint.x; + } + if (rightEdgeOut && resizeX) + test.w = -(constraint.w + constraint.x - test.w - test.x); + if (topEdgeOut && resizeY) { + test.h = test.y + test.h - constraint.y; + test.y = constraint.y; + } + if (bottomEdgeOut && resizeY) + test.h = -(constraint.h + constraint.y - test.h - test.y); + + success = test.copy().expand(-1).inside(constraint); + + if (success) + return test.translate(parentCoord - constraint.pos()); + } + + LOGM(WARN, "Compositor/client bug: xdg_positioner couldn't find a place"); + + return predictedBox.translate(-parentCoord - constraint.pos()); +} + +CXDGWMBase::CXDGWMBase(SP resource_) : resource(resource_) { + if (!good()) + return; + + resource->setDestroy([this](CXdgWmBase* r) { PROTO::xdgShell->destroyResource(this); }); + resource->setOnDestroy([this](CXdgWmBase* r) { PROTO::xdgShell->destroyResource(this); }); + + pClient = resource->client(); + + resource->setCreatePositioner([this](CXdgWmBase* r, uint32_t id) { + const auto RESOURCE = + PROTO::xdgShell->m_vPositioners.emplace_back(makeShared(makeShared(r->client(), r->version(), id), self.lock())); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::xdgShell->m_vPositioners.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + + positioners.emplace_back(RESOURCE); + + LOGM(LOG, "New xdg_positioner at {:x}", (uintptr_t)RESOURCE.get()); + }); + + resource->setGetXdgSurface([this](CXdgWmBase* r, uint32_t id, wl_resource* surf) { + const auto RESOURCE = PROTO::xdgShell->m_vSurfaces.emplace_back( + makeShared(makeShared(r->client(), r->version(), id), self.lock(), wlr_surface_from_resource(surf))); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::xdgShell->m_vSurfaces.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + + surfaces.emplace_back(RESOURCE); + + LOGM(LOG, "New xdg_surface at {:x}", (uintptr_t)RESOURCE.get()); + }); +} + +bool CXDGWMBase::good() { + return resource->resource(); +} + +wl_client* CXDGWMBase::client() { + return pClient; +} + +CXDGShellProtocol::CXDGShellProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + ; +} + +void CXDGShellProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + const auto RESOURCE = m_vWMBases.emplace_back(makeShared(makeShared(client, ver, id))); + + if (!RESOURCE->good()) { + wl_client_post_no_memory(client); + m_vWMBases.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + + LOGM(LOG, "New xdg_wm_base at {:x}", (uintptr_t)RESOURCE.get()); +} + +void CXDGShellProtocol::destroyResource(CXDGWMBase* resource) { + std::erase_if(m_vWMBases, [&](const auto& other) { return other.get() == resource; }); +} + +void CXDGShellProtocol::destroyResource(CXDGPositionerResource* resource) { + std::erase_if(m_vPositioners, [&](const auto& other) { return other.get() == resource; }); +} + +void CXDGShellProtocol::destroyResource(CXDGSurfaceResource* resource) { + std::erase_if(m_vSurfaces, [&](const auto& other) { return other.get() == resource; }); +} + +void CXDGShellProtocol::destroyResource(CXDGToplevelResource* resource) { + std::erase_if(m_vToplevels, [&](const auto& other) { return other.get() == resource; }); +} + +void CXDGShellProtocol::destroyResource(CXDGPopupResource* resource) { + std::erase_if(m_vPopups, [&](const auto& other) { return other.get() == resource; }); +} diff --git a/src/protocols/XDGShell.hpp b/src/protocols/XDGShell.hpp new file mode 100644 index 00000000..e214b6ac --- /dev/null +++ b/src/protocols/XDGShell.hpp @@ -0,0 +1,256 @@ +#pragma once + +#include +#include +#include +#include +#include "WaylandProtocol.hpp" +#include "xdg-shell.hpp" +#include "../helpers/Vector2D.hpp" +#include "../helpers/Box.hpp" +#include "../helpers/signal/Signal.hpp" + +class CXDGWMBase; +class CXDGPositionerResource; +class CXDGSurfaceResource; +class CXDGToplevelResource; +class CXDGPopupResource; +class CSeatGrab; + +struct SXDGPositionerState { + Vector2D requestedSize; + CBox anchorRect; + xdgPositionerAnchor anchor = XDG_POSITIONER_ANCHOR_NONE; + xdgPositionerGravity gravity = XDG_POSITIONER_GRAVITY_NONE; + uint32_t constraintAdjustment = 0; + Vector2D offset; + bool reactive = false; + Vector2D parentSize; +}; + +class CXDGPositionerRules { + public: + CXDGPositionerRules(SP positioner); + + CBox getPosition(const CBox& constraint, const Vector2D& parentPos); + + private: + SXDGPositionerState state; +}; + +class CXDGPopupResource { + public: + CXDGPopupResource(SP resource_, SP parent_, SP surface_, SP positioner_); + ~CXDGPopupResource(); + + static SP fromResource(wl_resource*); + + bool good(); + + void applyPositioning(const CBox& availableBox, const Vector2D& t1coord /* relative to box */); + + WP surface; + WP parent; + WP self; + + bool taken = false; + + CBox geometry; + + struct { + CSignal reposition; + CSignal destroy; // only the role + } events; + + // schedules a configure event + void configure(const CBox& box); + + void done(); + void repositioned(); + + private: + SP resource; + + uint32_t lastRepositionToken = 0; + + Vector2D accumulateParentOffset(); + + CXDGPositionerRules positionerRules; +}; + +class CXDGToplevelResource { + public: + CXDGToplevelResource(SP resource_, SP owner_); + ~CXDGToplevelResource(); + + static SP fromResource(wl_resource*); + + WP owner; + WP self; + + PHLWINDOWREF window; + + bool good(); + + // schedule a configure event + uint32_t setSize(const Vector2D& size); + uint32_t setMaximized(bool maximized); + uint32_t setFullscreen(bool fullscreen); + uint32_t setActive(bool active); + uint32_t setSuspeneded(bool sus); + + void close(); + + struct { + CSignal sizeLimitsChanged; + CSignal stateChanged; // maximized, fs, minimized, etc. + CSignal metadataChanged; // title, appid + CSignal destroy; // only the role + } events; + + struct { + std::string title; + std::string appid; + + // volatile state: is reset after the stateChanged signal fires + std::optional requestsMaximize; + std::optional requestsFullscreen; + std::optional requestsMinimize; + } state; + + struct { + Vector2D size; + std::vector states; + } pendingApply; + + struct { + Vector2D minSize = {1, 1}; + Vector2D maxSize = {1337420, 694200}; + } pending, current; + + WP parent; + + private: + SP resource; + void applyState(); +}; + +class CXDGSurfaceResource { + public: + CXDGSurfaceResource(SP resource_, SP owner_, wlr_surface* surface_); + ~CXDGSurfaceResource(); + + static SP fromResource(wl_resource*); + + bool good(); + + WP owner; + wlr_surface* surface = nullptr; + + WP toplevel; + WP popup; + + WP self; + + struct { + CBox geometry; + } pending, current; + + struct { + CSignal ack; + CSignal commit; + CSignal map; + CSignal unmap; + CSignal destroy; + CSignal newPopup; // SP + } events; + + bool initialCommit = true; + bool mapped = false; + + uint32_t scheduleConfigure(); + // do not call directly + void configure(); + + private: + SP resource; + + uint32_t lastConfigureSerial = 0; + uint32_t scheduledSerial = 0; + + wl_event_source* configureSource = nullptr; + + // + std::vector> popups; + + DYNLISTENER(surfaceDestroy); + DYNLISTENER(surfaceCommit); + + friend class CXDGPopupResource; + friend class CXDGToplevelResource; +}; + +class CXDGPositionerResource { + public: + CXDGPositionerResource(SP resource_, SP owner_); + + static SP fromResource(wl_resource*); + + bool good(); + + SXDGPositionerState state; + + WP owner; + WP self; + + private: + SP resource; +}; + +class CXDGWMBase { + public: + CXDGWMBase(SP resource_); + + bool good(); + wl_client* client(); + + std::vector> positioners; + std::vector> surfaces; + + WP self; + + private: + SP resource; + wl_client* pClient = nullptr; +}; + +class CXDGShellProtocol : public IWaylandProtocol { + public: + CXDGShellProtocol(const wl_interface* iface, const int& ver, const std::string& name); + + virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); + + private: + void destroyResource(CXDGWMBase* resource); + void destroyResource(CXDGPositionerResource* resource); + void destroyResource(CXDGSurfaceResource* resource); + void destroyResource(CXDGToplevelResource* resource); + void destroyResource(CXDGPopupResource* resource); + + // + std::vector> m_vWMBases; + std::vector> m_vPositioners; + std::vector> m_vSurfaces; + std::vector> m_vToplevels; + std::vector> m_vPopups; + + friend class CXDGWMBase; + friend class CXDGPositionerResource; + friend class CXDGSurfaceResource; + friend class CXDGToplevelResource; + friend class CXDGPopupResource; +}; + +namespace PROTO { + inline UP xdgShell; +}; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 38dba439..1aa246fb 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -10,6 +10,7 @@ #include "../desktop/LayerSurface.hpp" #include "../protocols/SessionLock.hpp" #include "../protocols/LayerShell.hpp" +#include "../protocols/XDGShell.hpp" #include "../protocols/PresentationTime.hpp" extern "C" { @@ -615,9 +616,7 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, CMonitor* pMonitor, timespec if (mode == RENDER_PASS_ALL || mode == RENDER_PASS_POPUP) { if (!pWindow->m_bIsX11) { - CBox geom; - wlr_xdg_surface_get_geometry(pWindow->m_uSurface.xdg, geom.pWlr()); - geom.applyFromWlr(); + CBox geom = pWindow->m_pXDGSurface->current.geometry; renderdata.x -= geom.x; renderdata.y -= geom.y; @@ -643,7 +642,20 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, CMonitor* pMonitor, timespec if (pWindow->m_sAdditionalConfigData.nearestNeighbor.toUnderlying()) g_pHyprOpenGL->m_RenderData.useNearestNeighbor = true; - wlr_xdg_surface_for_each_popup_surface(pWindow->m_uSurface.xdg, renderSurface, &renderdata); + pWindow->m_pPopupHead->breadthfirst( + [](CPopup* popup, void* data) { + if (!popup->m_sWLSurface.wlr()) + return; + auto pos = popup->coordsRelativeToParent(); + auto rd = (SRenderData*)data; + Vector2D oldPos = {rd->x, rd->y}; + rd->x += pos.x; + rd->y += pos.y; + wlr_surface_for_each_surface(popup->m_sWLSurface.wlr(), renderSurface, rd); + rd->x = oldPos.x; + rd->y = oldPos.y; + }, + &renderdata); g_pHyprOpenGL->m_RenderData.useNearestNeighbor = false; @@ -1000,9 +1012,7 @@ void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, wlr_surface* pSurfa if (!main || !pWindow) return; - CBox geom; - wlr_xdg_surface_get_geometry(pWindow->m_uSurface.xdg, geom.pWlr()); - geom.applyFromWlr(); + CBox geom = pWindow->m_pXDGSurface->current.geometry; // ignore X and Y, adjust uv if (geom.x != 0 || geom.y != 0 || geom.width > pWindow->m_vRealSize.value().x || geom.height > pWindow->m_vRealSize.value().y) { @@ -2530,8 +2540,7 @@ void CHyprRenderer::recheckSolitaryForMonitor(CMonitor* pMonitor) { if (PCANDIDATE->m_bIsX11) { surfaceCount = 1; } else { - wlr_xdg_surface_for_each_surface(PCANDIDATE->m_uSurface.xdg, countSubsurfacesIter, &surfaceCount); - wlr_xdg_surface_for_each_popup_surface(PCANDIDATE->m_uSurface.xdg, countSubsurfacesIter, &surfaceCount); + surfaceCount = PCANDIDATE->popupsCount() + PCANDIDATE->surfacesCount(); } if (surfaceCount > 1) From fc72df8e58bc127442a245f2f29a3246acf77cce Mon Sep 17 00:00:00 2001 From: Vaxry Date: Sat, 11 May 2024 01:02:57 +0100 Subject: [PATCH 20/57] seatmgr: Add a grab class --- src/Compositor.cpp | 5 + src/desktop/LayerSurface.cpp | 2 + src/desktop/Popup.cpp | 20 +-- src/desktop/Popup.hpp | 1 + src/managers/SeatManager.cpp | 73 +++++++++++ src/managers/SeatManager.hpp | 37 ++++++ src/managers/input/InputManager.cpp | 26 ++++ src/managers/input/InputManager.hpp | 6 +- src/protocols/FocusGrab.cpp | 193 ++++------------------------ src/protocols/FocusGrab.hpp | 10 +- src/protocols/XDGShell.cpp | 57 +++++++- src/protocols/XDGShell.hpp | 15 ++- 12 files changed, 257 insertions(+), 188 deletions(-) diff --git a/src/Compositor.cpp b/src/Compositor.cpp index c56dd280..4e0a6357 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -1025,6 +1025,11 @@ void CCompositor::focusSurface(wlr_surface* pSurface, PHLWINDOW pWindowOwner) { if (g_pSessionLockManager->isSessionLocked() && !g_pSessionLockManager->isSurfaceSessionLock(pSurface)) return; + if (g_pSeatManager->seatGrab && !g_pSeatManager->seatGrab->accepts(pSurface)) { + Debug::log(LOG, "surface {:x} won't receive kb focus becuase grab rejected it", (uintptr_t)pSurface); + return; + } + const auto PLASTSURF = m_pLastFocus; // Unfocus last surface if should diff --git a/src/desktop/LayerSurface.cpp b/src/desktop/LayerSurface.cpp index cb10079c..759eb09f 100644 --- a/src/desktop/LayerSurface.cpp +++ b/src/desktop/LayerSurface.cpp @@ -136,6 +136,8 @@ void CLayerSurface::onMap() { (g_pSeatManager->mouse.expired() || !g_pInputManager->isConstrained()); if (GRABSFOCUS) { + // TODO: use the new superb really very cool grab + g_pSeatManager->setGrab(nullptr); g_pInputManager->releaseAllMouseButtons(); g_pCompositor->focusSurface(surface.wlr()); diff --git a/src/desktop/Popup.cpp b/src/desktop/Popup.cpp index 605dfdfd..c652d298 100644 --- a/src/desktop/Popup.cpp +++ b/src/desktop/Popup.cpp @@ -45,6 +45,7 @@ void CPopup::initAllSignals() { listeners.reposition = m_pResource->events.reposition.registerListener([this](std::any d) { this->onReposition(); }); listeners.map = m_pResource->surface->events.map.registerListener([this](std::any d) { this->onMap(); }); listeners.unmap = m_pResource->surface->events.unmap.registerListener([this](std::any d) { this->onUnmap(); }); + listeners.dismissed = m_pResource->surface->events.unmap.registerListener([this](std::any d) { this->onUnmap(); }); listeners.destroy = m_pResource->surface->events.destroy.registerListener([this](std::any d) { this->onDestroy(); }); listeners.commit = m_pResource->surface->events.commit.registerListener([this](std::any d) { this->onCommit(); }); listeners.newPopup = m_pResource->surface->events.newPopup.registerListener([this](std::any d) { this->onNewPopup(std::any_cast>(d)); }); @@ -65,8 +66,9 @@ void CPopup::onDestroy() { } void CPopup::onMap() { - m_vLastSize = {m_pResource->surface->surface->current.width, m_pResource->surface->surface->current.height}; - const auto COORDS = coordsGlobal(); + m_vLastSize = {m_pResource->surface->surface->current.width, m_pResource->surface->surface->current.height}; + const auto COORDS = coordsGlobal(); + const auto PMONITOR = g_pCompositor->getMonitorFromVector(COORDS); CBox box; wlr_surface_get_extends(m_sWLSurface.wlr(), box.pWlr()); @@ -79,8 +81,9 @@ void CPopup::onMap() { m_pSubsurfaceHead = std::make_unique(this); - unconstrain(); + //unconstrain(); sendScale(); + wlr_surface_send_enter(m_pResource->surface->surface, PMONITOR->output); if (!m_pLayerOwner.expired() && m_pLayerOwner->layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP) g_pHyprOpenGL->markBlurDirtyForMonitor(g_pCompositor->getMonitorFromID(m_pLayerOwner->layer)); @@ -178,7 +181,6 @@ void CPopup::unconstrain() { CBox box = {PMONITOR->vecPosition.x - COORDS.x, PMONITOR->vecPosition.y - COORDS.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y}; m_pResource->applyPositioning(box, COORDS - PMONITOR->vecPosition); - wlr_surface_send_enter(m_pResource->surface->surface, PMONITOR->output); } Vector2D CPopup::coordsRelativeToParent() { @@ -190,8 +192,6 @@ Vector2D CPopup::coordsRelativeToParent() { CPopup* current = this; offset -= current->m_pResource->surface->current.geometry.pos(); - offset -= m_pResource->surface->current.geometry.pos(); - while (current->m_pParent && current->m_pResource) { offset += {current->m_sWLSurface.wlr()->current.dx, current->m_sWLSurface.wlr()->current.dy}; @@ -293,11 +293,15 @@ CPopup* CPopup::at(const Vector2D& globalCoords, bool allowsInput) { continue; if (!allowsInput) { - const auto BOX = CBox{p->coordsGlobal(), p->size()}; + const Vector2D offset = p->m_pResource ? (p->size() - p->m_pResource->geometry.size()) / 2.F : Vector2D{}; + const Vector2D size = p->m_pResource ? p->m_pResource->geometry.size() : p->size(); + + const auto BOX = CBox{p->coordsGlobal() + offset, size}; if (BOX.containsPoint(globalCoords)) return p; } else { - const auto REGION = CRegion{&p->m_sWLSurface.wlr()->current.input}.translate(p->coordsGlobal()); + const Vector2D offset = p->m_pResource ? (p->size() - p->m_pResource->geometry.size()) / 2.F : Vector2D{}; + const auto REGION = CRegion{&p->m_sWLSurface.wlr()->current.input}.translate(p->coordsGlobal() + offset); if (REGION.containsPoint(globalCoords)) return p; } diff --git a/src/desktop/Popup.hpp b/src/desktop/Popup.hpp index e6c35e68..ba6da55a 100644 --- a/src/desktop/Popup.hpp +++ b/src/desktop/Popup.hpp @@ -68,6 +68,7 @@ class CPopup { CHyprSignalListener map; CHyprSignalListener unmap; CHyprSignalListener commit; + CHyprSignalListener dismissed; CHyprSignalListener reposition; } listeners; diff --git a/src/managers/SeatManager.cpp b/src/managers/SeatManager.cpp index f9d394b5..d962dd84 100644 --- a/src/managers/SeatManager.cpp +++ b/src/managers/SeatManager.cpp @@ -371,6 +371,40 @@ void CSeatManager::sendTouchOrientation(int32_t id, double angle) { } } +void CSeatManager::refocusGrab() { + if (!seatGrab) + return; + + if (seatGrab->surfs.size() > 0) { + // try to find a surf in focus first + const auto MOUSE = g_pInputManager->getMouseCoordsInternal(); + for (auto& s : seatGrab->surfs) { + auto hlSurf = CWLSurface::surfaceFromWlr(s); + if (!hlSurf) + continue; + + auto b = hlSurf->getSurfaceBoxGlobal(); + if (!b.has_value()) + continue; + + if (!b->containsPoint(MOUSE)) + continue; + + if (seatGrab->keyboard) + setKeyboardFocus(s); + if (seatGrab->pointer) + setPointerFocus(s, MOUSE - b->pos()); + return; + } + + wlr_surface* surf = seatGrab->surfs.at(0); + if (seatGrab->keyboard) + setKeyboardFocus(surf); + if (seatGrab->pointer) + setPointerFocus(surf, {}); + } +} + void CSeatManager::onSetCursor(SP seatResource, uint32_t serial, wlr_surface* surf, const Vector2D& hotspot) { if (!state.pointerFocusResource || !seatResource || seatResource->client() != state.pointerFocusResource->client()) { Debug::log(LOG, "[seatmgr] Rejecting a setCursor because the client ain't in focus"); @@ -389,3 +423,42 @@ void CSeatManager::onSetCursor(SP seatResource, uint32_t serial SP CSeatManager::seatResourceForClient(wl_client* client) { return PROTO::seat->seatResourceForClient(client); } + +void CSeatManager::setGrab(SP grab) { + if (seatGrab) { + auto oldGrab = seatGrab; + seatGrab.reset(); + g_pInputManager->refocus(); + if (oldGrab->onEnd) + oldGrab->onEnd(); + } + + if (!grab) + return; + + seatGrab = grab; + + refocusGrab(); +} + +bool CSeatGrab::accepts(wlr_surface* surf) { + return std::find(surfs.begin(), surfs.end(), surf) != surfs.end(); +} + +void CSeatGrab::add(wlr_surface* surf) { + surfs.push_back(surf); +} + +void CSeatGrab::remove(wlr_surface* surf) { + std::erase(surfs, surf); + if ((keyboard && g_pSeatManager->state.keyboardFocus == surf) || (pointer && g_pSeatManager->state.pointerFocus == surf)) + g_pSeatManager->refocusGrab(); +} + +void CSeatGrab::setCallback(std::function onEnd_) { + onEnd = onEnd_; +} + +void CSeatGrab::clear() { + surfs.clear(); +} diff --git a/src/managers/SeatManager.hpp b/src/managers/SeatManager.hpp index ef114024..16f11ffd 100644 --- a/src/managers/SeatManager.hpp +++ b/src/managers/SeatManager.hpp @@ -14,6 +14,37 @@ class CWLSeatResource; class IPointer; class IKeyboard; +/* + A seat grab defines a restricted set of surfaces that can be focused. + Only one grab can be active at a time + + when a grab is removed, refocus() will happen + + Different from a constraint. + + When first set with setGrab, SeatManager will try to find a surface that is at the mouse pointer to focus, + from first added to last added. If none are, first is focused. +*/ +class CSeatGrab { + public: + bool accepts(wlr_surface* surf); + void add(wlr_surface* surf); + void remove(wlr_surface* surf); + void setCallback(std::function onEnd_); + void clear(); + + bool keyboard = false; + bool pointer = false; + bool touch = false; + + bool removeOnInput = true; // on hard input e.g. click outside, remove + + private: + std::vector surfs; // read-only + std::function onEnd; + friend class CSeatManager; +}; + class CSeatManager { public: CSeatManager(); @@ -76,6 +107,9 @@ class CSeatManager { WP mouse; WP keyboard; + void setGrab(SP grab); // nullptr removes + SP seatGrab; + private: struct SSeatResourceContainer { SSeatResourceContainer(SP); @@ -92,6 +126,8 @@ class CSeatManager { void onNewSeatResource(SP resource); SP containerForResource(SP seatResource); + void refocusGrab(); + struct { CHyprSignalListener newSeatResource; } listeners; @@ -101,6 +137,7 @@ class CSeatManager { DYNLISTENER(touchSurfaceDestroy); friend struct SSeatResourceContainer; + friend class CSeatGrab; }; inline UP g_pSeatManager; diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index bb0ab65a..fe19c98f 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -358,6 +358,26 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { if (g_pCompositor->m_pLastMonitor->output->software_cursor_locks > 0) g_pCompositor->scheduleFrameForMonitor(g_pCompositor->m_pLastMonitor.get()); + // grabs + if (g_pSeatManager->seatGrab && !g_pSeatManager->seatGrab->accepts(foundSurface)) { + if (m_bHardInput || refocus) { + g_pSeatManager->setGrab(nullptr); + return; // setGrab will refocus + } else { + // we need to grab the last surface. + foundSurface = g_pSeatManager->state.pointerFocus; + + auto HLSurface = CWLSurface::surfaceFromWlr(foundSurface); + + if (HLSurface) { + const auto BOX = HLSurface->getSurfaceBoxGlobal(); + + if (BOX.has_value()) + surfacePos = BOX->pos(); + } + } + } + if (!foundSurface) { if (!m_bEmptyFocusCursorSet) { if (*PRESIZEONBORDER && *PRESIZECURSORICON && m_eBorderIconDirection != BORDERICON_NONE) { @@ -678,6 +698,12 @@ void CInputManager::processMouseDownNormal(const IPointer::SButtonEvent& e) { if (const auto PMON = g_pCompositor->getMonitorFromVector(mouseCoords); PMON != g_pCompositor->m_pLastMonitor.get() && PMON) g_pCompositor->setActiveMonitor(PMON); + + if (g_pSeatManager->seatGrab && e.state == WL_POINTER_BUTTON_STATE_PRESSED) { + m_bHardInput = true; + simulateMouseMovement(); + m_bHardInput = false; + } } void CInputManager::processMouseDownKill(const IPointer::SButtonEvent& e) { diff --git a/src/managers/input/InputManager.hpp b/src/managers/input/InputManager.hpp index 42c9d3da..85efecb8 100644 --- a/src/managers/input/InputManager.hpp +++ b/src/managers/input/InputManager.hpp @@ -188,10 +188,12 @@ class CInputManager { void releaseAllMouseButtons(); // for some bugs in follow mouse 0 - bool m_bLastFocusOnLS = false; - + bool m_bLastFocusOnLS = false; bool m_bLastFocusOnIMEPopup = false; + // for hard input e.g. clicks + bool m_bHardInput = false; + // for hiding cursor on touch bool m_bLastInputTouch = false; diff --git a/src/protocols/FocusGrab.cpp b/src/protocols/FocusGrab.cpp index 4189ed6e..5f0771e0 100644 --- a/src/protocols/FocusGrab.cpp +++ b/src/protocols/FocusGrab.cpp @@ -7,119 +7,7 @@ #include #include -// static void focus_grab_pointer_enter(wlr_seat_pointer_grab* grab, wlr_surface* surface, double sx, double sy) { -// if (static_cast(grab->data)->isSurfaceComitted(surface)) -// wlr_seat_pointer_enter(grab->seat, surface, sx, sy); -// else -// wlr_seat_pointer_clear_focus(grab->seat); -// } - -// static void focus_grab_pointer_clear_focus(wlr_seat_pointer_grab* grab) { -// wlr_seat_pointer_clear_focus(grab->seat); -// } - -// static void focus_grab_pointer_motion(wlr_seat_pointer_grab* grab, uint32_t time, double sx, double sy) { -// wlr_seat_pointer_send_motion(grab->seat, time, sx, sy); -// } - -// static uint32_t focus_grab_pointer_button(wlr_seat_pointer_grab* grab, uint32_t time, uint32_t button, wl_pointer_button_state state) { -// uint32_t serial = wlr_seat_pointer_send_button(grab->seat, time, button, state); - -// if (serial) -// return serial; -// else { -// static_cast(grab->data)->finish(true); -// return 0; -// } -// } - -// static void focus_grab_pointer_axis(wlr_seat_pointer_grab* grab, uint32_t time, enum wl_pointer_axis orientation, double value, int32_t value_discrete, -// enum wl_pointer_axis_source source, enum wl_pointer_axis_relative_direction relative_direction) { -// wlr_seat_pointer_send_axis(grab->seat, time, orientation, value, value_discrete, source, relative_direction); -// } - -// static void focus_grab_pointer_frame(wlr_seat_pointer_grab* grab) { -// wlr_seat_pointer_send_frame(grab->seat); -// } - -// static void focus_grab_pointer_cancel(wlr_seat_pointer_grab* grab) { -// static_cast(grab->data)->finish(true); -// } - -// static const wlr_pointer_grab_interface focus_grab_pointer_impl = { -// .enter = focus_grab_pointer_enter, -// .clear_focus = focus_grab_pointer_clear_focus, -// .motion = focus_grab_pointer_motion, -// .button = focus_grab_pointer_button, -// .axis = focus_grab_pointer_axis, -// .frame = focus_grab_pointer_frame, -// .cancel = focus_grab_pointer_cancel, -// }; - -// static void focus_grab_keyboard_enter(wlr_seat_keyboard_grab* grab, wlr_surface* surface, const uint32_t keycodes[], size_t num_keycodes, const wlr_keyboard_modifiers* modifiers) { -// if (static_cast(grab->data)->isSurfaceComitted(surface)) -// wlr_seat_keyboard_enter(grab->seat, surface, keycodes, num_keycodes, modifiers); - -// // otherwise the last grabbed window should retain keyboard focus. -// } - -// static void focus_grab_keyboard_clear_focus(wlr_seat_keyboard_grab* grab) { -// static_cast(grab->data)->finish(true); -// } - -// static void focus_grab_keyboard_key(wlr_seat_keyboard_grab* grab, uint32_t time, uint32_t key, uint32_t state) { -// wlr_seat_keyboard_send_key(grab->seat, time, key, state); -// } - -// static void focus_grab_keyboard_modifiers(wlr_seat_keyboard_grab* grab, const wlr_keyboard_modifiers* modifiers) { -// wlr_seat_keyboard_send_modifiers(grab->seat, modifiers); -// } - -// static void focus_grab_keyboard_cancel(wlr_seat_keyboard_grab* grab) { -// static_cast(grab->data)->finish(true); -// } - -// static const wlr_keyboard_grab_interface focus_grab_keyboard_impl = { -// .enter = focus_grab_keyboard_enter, -// .clear_focus = focus_grab_keyboard_clear_focus, -// .key = focus_grab_keyboard_key, -// .modifiers = focus_grab_keyboard_modifiers, -// .cancel = focus_grab_keyboard_cancel, -// }; - -// static uint32_t focus_grab_touch_down(wlr_seat_touch_grab* grab, uint32_t time, wlr_touch_point* point) { -// if (!static_cast(grab->data)->isSurfaceComitted(point->surface)) -// return 0; - -// return wlr_seat_touch_send_down(grab->seat, point->surface, time, point->touch_id, point->sx, point->sy); -// } - -// static void focus_grab_touch_up(wlr_seat_touch_grab* grab, uint32_t time, wlr_touch_point* point) { -// wlr_seat_touch_send_up(grab->seat, time, point->touch_id); -// } - -// static void focus_grab_touch_motion(wlr_seat_touch_grab* grab, uint32_t time, wlr_touch_point* point) { -// wlr_seat_touch_send_motion(grab->seat, time, point->touch_id, point->sx, point->sy); -// } - -// static void focus_grab_touch_enter(wlr_seat_touch_grab* grab, uint32_t time, wlr_touch_point* point) {} - -// static void focus_grab_touch_frame(wlr_seat_touch_grab* grab) { -// wlr_seat_touch_send_frame(grab->seat); -// } - -// static void focus_grab_touch_cancel(wlr_seat_touch_grab* grab) { -// static_cast(grab->data)->finish(true); -// } - -// static const wlr_touch_grab_interface focus_grab_touch_impl = { -// .down = focus_grab_touch_down, -// .up = focus_grab_touch_up, -// .motion = focus_grab_touch_motion, -// .enter = focus_grab_touch_enter, -// .frame = focus_grab_touch_frame, -// .cancel = focus_grab_touch_cancel, -// }; +#define LOGM PROTO::focusGrab->protoLog CFocusGrabSurfaceState::CFocusGrabSurfaceState(CFocusGrab* grab, wlr_surface* surface) { hyprListener_surfaceDestroy.initCallback( @@ -134,14 +22,10 @@ CFocusGrab::CFocusGrab(SP resource_) : resource(resource_) if (!resource->resource()) return; - // m_sPointerGrab.interface = &focus_grab_pointer_impl; - // m_sPointerGrab.data = this; - - // m_sKeyboardGrab.interface = &focus_grab_keyboard_impl; - // m_sKeyboardGrab.data = this; - - // m_sTouchGrab.interface = &focus_grab_touch_impl; - // m_sTouchGrab.data = this; + grab = makeShared(); + grab->keyboard = true; + grab->pointer = true; + grab->setCallback([this]() { finish(true); }); resource->setDestroy([this](CHyprlandFocusGrabV1* pMgr) { PROTO::focusGrab->destroyGrab(this); }); resource->setOnDestroy([this](CHyprlandFocusGrabV1* pMgr) { PROTO::focusGrab->destroyGrab(this); }); @@ -168,21 +52,8 @@ bool CFocusGrab::isSurfaceComitted(wlr_surface* surface) { void CFocusGrab::start() { if (!m_bGrabActive) { - // wlr_seat_pointer_start_grab(g_pCompositor->m_sSeat.seat, &m_sPointerGrab); - // wlr_seat_keyboard_start_grab(g_pCompositor->m_sSeat.seat, &m_sKeyboardGrab); - // wlr_seat_touch_start_grab(g_pCompositor->m_sSeat.seat, &m_sTouchGrab); m_bGrabActive = true; - - // Ensure the grab ends if another grab begins, including from xdg_popup::grab. - - // hyprListener_pointerGrabStarted.initCallback( - // &g_pCompositor->m_sSeat.seat->events.pointer_grab_begin, [this](void*, void*) { finish(true); }, this, "CFocusGrab"); - - // hyprListener_keyboardGrabStarted.initCallback( - // &g_pCompositor->m_sSeat.seat->events.keyboard_grab_begin, [this](void*, void*) { finish(true); }, this, "CFocusGrab"); - - // hyprListener_touchGrabStarted.initCallback( - // &g_pCompositor->m_sSeat.seat->events.touch_grab_begin, [this](void*, void*) { finish(true); }, this, "CFocusGrab"); + g_pSeatManager->setGrab(grab); } // Ensure new surfaces are focused if under the mouse when comitted. @@ -193,59 +64,39 @@ void CFocusGrab::start() { void CFocusGrab::finish(bool sendCleared) { if (m_bGrabActive) { m_bGrabActive = false; - // hyprListener_pointerGrabStarted.removeCallback(); - // hyprListener_keyboardGrabStarted.removeCallback(); - // hyprListener_touchGrabStarted.removeCallback(); - // Only clear grabs that belong to this focus grab. When superseded by another grab - // or xdg_popup grab we might not own the current grab. - - bool hadGrab = false; - // if (g_pCompositor->m_sSeat.seat->pointer_state.grab == &m_sPointerGrab) { - // wlr_seat_pointer_end_grab(g_pCompositor->m_sSeat.seat); - // hadGrab = true; - // } - - // if (g_pCompositor->m_sSeat.seat->keyboard_state.grab == &m_sKeyboardGrab) { - // wlr_seat_keyboard_end_grab(g_pCompositor->m_sSeat.seat); - // hadGrab = true; - // } - - // if (g_pCompositor->m_sSeat.seat->touch_state.grab == &m_sTouchGrab) { - // wlr_seat_touch_end_grab(g_pCompositor->m_sSeat.seat); - // hadGrab = true; - // } + if (g_pSeatManager->seatGrab == grab) { + g_pSeatManager->setGrab(nullptr); + } + grab->clear(); m_mSurfaces.clear(); if (sendCleared) resource->sendCleared(); - - // Ensure surfaces under the mouse when the grab ends get focus. - if (hadGrab) - g_pInputManager->refocus(); } } void CFocusGrab::addSurface(wlr_surface* surface) { auto iter = m_mSurfaces.find(surface); - if (iter == m_mSurfaces.end()) + if (iter == m_mSurfaces.end()) { m_mSurfaces.emplace(surface, std::make_unique(this, surface)); + } } void CFocusGrab::removeSurface(wlr_surface* surface) { auto iter = m_mSurfaces.find(surface); if (iter != m_mSurfaces.end()) { - if (iter->second->state == CFocusGrabSurfaceState::PendingAddition) + if (iter->second->state == CFocusGrabSurfaceState::PendingAddition) { m_mSurfaces.erase(iter); - else + } else iter->second->state = CFocusGrabSurfaceState::PendingRemoval; } } void CFocusGrab::eraseSurface(wlr_surface* surface) { removeSurface(surface); - commit(); + commit(true); } void CFocusGrab::refocusKeyboard() { @@ -264,22 +115,26 @@ void CFocusGrab::refocusKeyboard() { if (surface) g_pCompositor->focusSurface(surface); else - Debug::log(ERR, "CFocusGrab::refocusKeyboard called with no committed surfaces. This should never happen."); + LOGM(ERR, "CFocusGrab::refocusKeyboard called with no committed surfaces. This should never happen."); } -void CFocusGrab::commit() { +void CFocusGrab::commit(bool removeOnly) { auto surfacesChanged = false; auto anyComitted = false; for (auto iter = m_mSurfaces.begin(); iter != m_mSurfaces.end();) { switch (iter->second->state) { case CFocusGrabSurfaceState::PendingRemoval: + grab->remove(iter->first); iter = m_mSurfaces.erase(iter); surfacesChanged = true; continue; case CFocusGrabSurfaceState::PendingAddition: - iter->second->state = CFocusGrabSurfaceState::Comitted; - surfacesChanged = true; - anyComitted = true; + if (!removeOnly) { + iter->second->state = CFocusGrabSurfaceState::Comitted; + grab->add(iter->first); + surfacesChanged = true; + anyComitted = true; + } break; case CFocusGrabSurfaceState::Comitted: anyComitted = true; break; } diff --git a/src/protocols/FocusGrab.hpp b/src/protocols/FocusGrab.hpp index 3e5d36ab..40922c22 100644 --- a/src/protocols/FocusGrab.hpp +++ b/src/protocols/FocusGrab.hpp @@ -8,6 +8,7 @@ #include class CFocusGrab; +class CSeatGrab; class CFocusGrabSurfaceState { public: @@ -40,14 +41,13 @@ class CFocusGrab { void removeSurface(wlr_surface* surface); void eraseSurface(wlr_surface* surface); void refocusKeyboard(); - void commit(); + void commit(bool removeOnly = false); SP resource; std::unordered_map> m_mSurfaces; - // wlr_seat_pointer_grab m_sPointerGrab; - // wlr_seat_keyboard_grab m_sKeyboardGrab; - // wlr_seat_touch_grab m_sTouchGrab; - bool m_bGrabActive = false; + SP grab; + + bool m_bGrabActive = false; DYNLISTENER(pointerGrabStarted); DYNLISTENER(keyboardGrabStarted); diff --git a/src/protocols/XDGShell.cpp b/src/protocols/XDGShell.cpp index fcb2d63b..9777281f 100644 --- a/src/protocols/XDGShell.cpp +++ b/src/protocols/XDGShell.cpp @@ -1,6 +1,8 @@ #include "XDGShell.hpp" #include #include "../Compositor.hpp" +#include "../managers/SeatManager.hpp" +#include "core/Seat.hpp" #define LOGM PROTO::xdgShell->protoLog @@ -14,12 +16,14 @@ CXDGPopupResource::CXDGPopupResource(SP resource_, SPsetDestroy([this](CXdgPopup* r) { if (surface && surface->mapped) surface->events.unmap.emit(); + PROTO::xdgShell->onPopupDestroy(self); events.destroy.emit(); PROTO::xdgShell->destroyResource(this); }); resource->setOnDestroy([this](CXdgPopup* r) { if (surface && surface->mapped) surface->events.unmap.emit(); + PROTO::xdgShell->onPopupDestroy(self); events.destroy.emit(); PROTO::xdgShell->destroyResource(this); }); @@ -34,11 +38,17 @@ CXDGPopupResource::CXDGPopupResource(SP resource_, SPsetGrab([this](CXdgPopup* r, wl_resource* seat, uint32_t serial) { + LOGM(LOG, "xdg_popup {:x} requests grab", (uintptr_t)this); + PROTO::xdgShell->addOrStartGrab(self.lock()); + }); + if (parent) taken = true; } CXDGPopupResource::~CXDGPopupResource() { + PROTO::xdgShell->onPopupDestroy(self); events.destroy.emit(); } @@ -85,6 +95,7 @@ void CXDGPopupResource::configure(const CBox& box) { } void CXDGPopupResource::done() { + events.dismissed.emit(); resource->sendPopupDone(); } @@ -659,7 +670,15 @@ wl_client* CXDGWMBase::client() { } CXDGShellProtocol::CXDGShellProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { - ; + grab = makeShared(); + grab->keyboard = true; + grab->pointer = true; + grab->setCallback([this]() { + for (auto& g : grabbed) { + g->done(); + } + grabbed.clear(); + }); } void CXDGShellProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { @@ -695,3 +714,39 @@ void CXDGShellProtocol::destroyResource(CXDGToplevelResource* resource) { void CXDGShellProtocol::destroyResource(CXDGPopupResource* resource) { std::erase_if(m_vPopups, [&](const auto& other) { return other.get() == resource; }); } + +void CXDGShellProtocol::addOrStartGrab(SP popup) { + if (!grabOwner) { + grabOwner = popup; + grabbed.clear(); + grab->clear(); + grab->add(popup->surface->surface); + if (popup->parent) + grab->add(popup->parent->surface); + g_pSeatManager->setGrab(grab); + grabbed.emplace_back(popup); + return; + } + + grabbed.emplace_back(popup); + + grab->add(popup->surface->surface); + + if (popup->parent) + grab->add(popup->parent->surface); +} + +void CXDGShellProtocol::onPopupDestroy(WP popup) { + if (popup == grabOwner) { + g_pSeatManager->setGrab(nullptr); + for (auto& g : grabbed) { + g->done(); + } + grabbed.clear(); + return; + } + + std::erase(grabbed, popup); + if (popup->surface) + grab->remove(popup->surface->surface); +} diff --git a/src/protocols/XDGShell.hpp b/src/protocols/XDGShell.hpp index e214b6ac..1dbeb209 100644 --- a/src/protocols/XDGShell.hpp +++ b/src/protocols/XDGShell.hpp @@ -59,6 +59,7 @@ class CXDGPopupResource { struct { CSignal reposition; + CSignal dismissed; CSignal destroy; // only the role } events; @@ -198,10 +199,10 @@ class CXDGPositionerResource { bool good(); - SXDGPositionerState state; + SXDGPositionerState state; - WP owner; - WP self; + WP owner; + WP self; private: SP resource; @@ -244,6 +245,14 @@ class CXDGShellProtocol : public IWaylandProtocol { std::vector> m_vToplevels; std::vector> m_vPopups; + // current popup grab + WP grabOwner; + SP grab; + std::vector> grabbed; + + void addOrStartGrab(SP popup); + void onPopupDestroy(WP popup); + friend class CXDGWMBase; friend class CXDGPositionerResource; friend class CXDGSurfaceResource; From 7eeee2c94e882a5df1914234df0571b221f9aa35 Mon Sep 17 00:00:00 2001 From: Vaxry Date: Sat, 11 May 2024 17:13:20 +0100 Subject: [PATCH 21/57] wl-data-device: move to hyprland impl --- src/Compositor.cpp | 10 - src/events/Events.hpp | 10 - src/events/Misc.cpp | 92 ---- src/helpers/WLClasses.hpp | 19 - src/managers/PointerManager.cpp | 4 + src/managers/PointerManager.hpp | 1 + src/managers/ProtocolManager.cpp | 5 +- src/managers/SeatManager.cpp | 38 ++ src/managers/SeatManager.hpp | 12 + src/managers/input/InputManager.cpp | 28 +- src/managers/input/InputManager.hpp | 3 - src/protocols/core/DataDevice.cpp | 655 ++++++++++++++++++++++++++++ src/protocols/core/DataDevice.hpp | 194 ++++++++ src/protocols/types/DataDevice.cpp | 17 + src/protocols/types/DataDevice.hpp | 29 ++ src/render/Renderer.cpp | 17 +- 16 files changed, 963 insertions(+), 171 deletions(-) create mode 100644 src/protocols/core/DataDevice.cpp create mode 100644 src/protocols/core/DataDevice.hpp create mode 100644 src/protocols/types/DataDevice.cpp create mode 100644 src/protocols/types/DataDevice.hpp diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 4e0a6357..efbd997f 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -253,11 +253,6 @@ void CCompositor::initServer() { void CCompositor::initAllSignals() { addWLSignal(&m_sWLRBackend->events.new_output, &Events::listen_newOutput, m_sWLRBackend, "Backend"); addWLSignal(&m_sWLRBackend->events.new_input, &Events::listen_newInput, m_sWLRBackend, "Backend"); - // addWLSignal(&m_sSeat.seat->events.request_set_selection, &Events::listen_requestSetSel, &m_sSeat, "Seat"); - // addWLSignal(&m_sSeat.seat->events.request_start_drag, &Events::listen_requestDrag, &m_sSeat, "Seat"); - // addWLSignal(&m_sSeat.seat->events.start_drag, &Events::listen_startDrag, &m_sSeat, "Seat"); - // addWLSignal(&m_sSeat.seat->events.request_set_selection, &Events::listen_requestSetSel, &m_sSeat, "Seat"); - // addWLSignal(&m_sSeat.seat->events.request_set_primary_selection, &Events::listen_requestSetPrimarySel, &m_sSeat, "Seat"); addWLSignal(&m_sWLRRenderer->events.destroy, &Events::listen_RendererDestroy, m_sWLRRenderer, "WLRRenderer"); if (m_sWRLDRMLeaseMgr) @@ -270,11 +265,6 @@ void CCompositor::initAllSignals() { void CCompositor::removeAllSignals() { removeWLSignal(&Events::listen_newOutput); removeWLSignal(&Events::listen_newInput); - removeWLSignal(&Events::listen_requestSetSel); - removeWLSignal(&Events::listen_requestDrag); - removeWLSignal(&Events::listen_startDrag); - removeWLSignal(&Events::listen_requestSetSel); - removeWLSignal(&Events::listen_requestSetPrimarySel); removeWLSignal(&Events::listen_RendererDestroy); if (m_sWRLDRMLeaseMgr) diff --git a/src/events/Events.hpp b/src/events/Events.hpp index 7cc0eb32..0b757842 100644 --- a/src/events/Events.hpp +++ b/src/events/Events.hpp @@ -59,16 +59,6 @@ namespace Events { LISTENER(readyXWayland); LISTENER(surfaceXWayland); - // Drag & Drop - LISTENER(requestDrag); - LISTENER(startDrag); - DYNLISTENFUNC(destroyDrag); - - DYNLISTENFUNC(mapDragIcon); - DYNLISTENFUNC(unmapDragIcon); - DYNLISTENFUNC(destroyDragIcon); - DYNLISTENFUNC(commitDragIcon); - // Renderer destroy LISTENER(RendererDestroy); diff --git a/src/events/Misc.cpp b/src/events/Misc.cpp index 703dba6a..7152730e 100644 --- a/src/events/Misc.cpp +++ b/src/events/Misc.cpp @@ -25,16 +25,6 @@ void Events::listener_leaseRequest(wl_listener* listener, void* data) { } } -void Events::listener_requestSetPrimarySel(wl_listener* listener, void* data) { - // const auto EVENT = (wlr_seat_request_set_primary_selection_event*)data; - // wlr_seat_set_primary_selection(g_pCompositor->m_sSeat.seat, EVENT->source, EVENT->serial); -} - -void Events::listener_requestSetSel(wl_listener* listener, void* data) { - // const auto EVENT = (wlr_seat_request_set_selection_event*)data; - // wlr_seat_set_selection(g_pCompositor->m_sSeat.seat, EVENT->source, EVENT->serial); -} - void Events::listener_readyXWayland(wl_listener* listener, void* data) { #ifndef NO_XWAYLAND const auto XCBCONNECTION = xcb_connect(g_pXWaylandManager->m_sWLRXWayland->display_name, NULL); @@ -79,88 +69,6 @@ void Events::listener_readyXWayland(wl_listener* listener, void* data) { #endif } -void Events::listener_requestDrag(wl_listener* listener, void* data) { - // const auto E = (wlr_seat_request_start_drag_event*)data; - - // if (!wlr_seat_validate_pointer_grab_serial(g_pCompositor->m_sSeat.seat, E->origin, E->serial)) { - // Debug::log(LOG, "Ignoring drag and drop request: serial mismatch."); - // wlr_data_source_destroy(E->drag->source); - // return; - // } - - // wlr_seat_start_pointer_drag(g_pCompositor->m_sSeat.seat, E->drag, E->serial); -} - -void Events::listener_startDrag(wl_listener* listener, void* data) { - - if (g_pInputManager->m_sDrag.drag) - return; // don't handle multiple drags - - g_pInputManager->m_sDrag.drag = (wlr_drag*)data; - - wlr_drag* wlrDrag = (wlr_drag*)data; - - Debug::log(LOG, "Started drag {:x}", (uintptr_t)wlrDrag); - - wlrDrag->data = data; - - g_pInputManager->m_sDrag.hyprListener_destroy.initCallback(&wlrDrag->events.destroy, &Events::listener_destroyDrag, &g_pInputManager->m_sDrag, "Drag"); - - if (wlrDrag->icon) { - Debug::log(LOG, "Drag started with an icon {:x}", (uintptr_t)wlrDrag->icon); - - g_pInputManager->m_sDrag.dragIcon = wlrDrag->icon; - wlrDrag->icon->data = g_pInputManager->m_sDrag.dragIcon; - - g_pInputManager->m_sDrag.hyprListener_mapIcon.initCallback(&wlrDrag->icon->surface->events.map, &Events::listener_mapDragIcon, &g_pInputManager->m_sDrag, "DragIcon"); - g_pInputManager->m_sDrag.hyprListener_unmapIcon.initCallback(&wlrDrag->icon->surface->events.unmap, &Events::listener_unmapDragIcon, &g_pInputManager->m_sDrag, "DragIcon"); - g_pInputManager->m_sDrag.hyprListener_destroyIcon.initCallback(&wlrDrag->icon->events.destroy, &Events::listener_destroyDragIcon, &g_pInputManager->m_sDrag, "DragIcon"); - g_pInputManager->m_sDrag.hyprListener_commitIcon.initCallback(&wlrDrag->icon->surface->events.commit, &Events::listener_commitDragIcon, &g_pInputManager->m_sDrag, - "DragIcon"); - } -} - -void Events::listener_destroyDrag(void* owner, void* data) { - Debug::log(LOG, "Drag destroyed."); - - if (g_pInputManager->m_sDrag.drag && g_pInputManager->m_sDrag.dragIcon && g_pInputManager->m_sDrag.dragIcon->surface) - g_pHyprRenderer->damageBox(g_pInputManager->m_sDrag.pos.x - 2, g_pInputManager->m_sDrag.pos.y - 2, g_pInputManager->m_sDrag.dragIcon->surface->current.width + 4, - g_pInputManager->m_sDrag.dragIcon->surface->current.height + 4); - - g_pInputManager->m_sDrag.drag = nullptr; - g_pInputManager->m_sDrag.dragIcon = nullptr; - g_pInputManager->m_sDrag.hyprListener_destroy.removeCallback(); - - g_pCompositor->focusWindow(g_pCompositor->m_pLastWindow.lock(), - g_pCompositor->m_pLastWindow.lock() ? g_pXWaylandManager->getWindowSurface(g_pCompositor->m_pLastWindow.lock()) : nullptr); -} - -void Events::listener_mapDragIcon(void* owner, void* data) { - Debug::log(LOG, "Drag icon mapped."); - g_pInputManager->m_sDrag.iconMapped = true; -} - -void Events::listener_unmapDragIcon(void* owner, void* data) { - Debug::log(LOG, "Drag icon unmapped."); - g_pInputManager->m_sDrag.iconMapped = false; -} - -void Events::listener_destroyDragIcon(void* owner, void* data) { - Debug::log(LOG, "Drag icon destroyed."); - - g_pInputManager->m_sDrag.dragIcon = nullptr; - g_pInputManager->m_sDrag.hyprListener_commitIcon.removeCallback(); - g_pInputManager->m_sDrag.hyprListener_destroyIcon.removeCallback(); - g_pInputManager->m_sDrag.hyprListener_mapIcon.removeCallback(); - g_pInputManager->m_sDrag.hyprListener_unmapIcon.removeCallback(); -} - -void Events::listener_commitDragIcon(void* owner, void* data) { - g_pInputManager->updateDragIcon(); - - Debug::log(LOG, "Drag icon committed."); -} - void Events::listener_RendererDestroy(wl_listener* listener, void* data) { Debug::log(LOG, "!!Renderer destroyed!!"); } diff --git a/src/helpers/WLClasses.hpp b/src/helpers/WLClasses.hpp index 7b24de7d..30f5aebf 100644 --- a/src/helpers/WLClasses.hpp +++ b/src/helpers/WLClasses.hpp @@ -58,25 +58,6 @@ struct SExtensionFindingData { wlr_surface** found; }; -struct SDrag { - wlr_drag* drag = nullptr; - - DYNLISTENER(destroy); - - // Icon - - bool iconMapped = false; - - wlr_drag_icon* dragIcon = nullptr; - - Vector2D pos; - - DYNLISTENER(destroyIcon); - DYNLISTENER(mapIcon); - DYNLISTENER(unmapIcon); - DYNLISTENER(commitIcon); -}; - struct SSwipeGesture { PHLWORKSPACE pWorkspaceBegin = nullptr; diff --git a/src/managers/PointerManager.cpp b/src/managers/PointerManager.cpp index 60d11e7f..6a0fb984 100644 --- a/src/managers/PointerManager.cpp +++ b/src/managers/PointerManager.cpp @@ -941,3 +941,7 @@ void CPointerManager::damageCursor(SP pMonitor) { return; } } + +Vector2D CPointerManager::cursorSizeLogical() { + return currentCursorImage.size / currentCursorImage.scale; +} diff --git a/src/managers/PointerManager.hpp b/src/managers/PointerManager.hpp index 71ab0fc2..b6cb0c7a 100644 --- a/src/managers/PointerManager.hpp +++ b/src/managers/PointerManager.hpp @@ -52,6 +52,7 @@ class CPointerManager { // Vector2D position(); + Vector2D cursorSizeLogical(); private: void recheckPointerPosition(); diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp index e53eb111..8167103f 100644 --- a/src/managers/ProtocolManager.cpp +++ b/src/managers/ProtocolManager.cpp @@ -28,13 +28,16 @@ #include "../protocols/Tablet.hpp" #include "../protocols/LayerShell.hpp" #include "../protocols/PresentationTime.hpp" -#include "../protocols/core/Seat.hpp" #include "../protocols/XDGShell.hpp" +#include "../protocols/core/Seat.hpp" +#include "../protocols/core/DataDevice.hpp" + CProtocolManager::CProtocolManager() { // Core PROTO::seat = std::make_unique(&wl_seat_interface, 9, "WLSeat"); + PROTO::data = std::make_unique(&wl_data_device_manager_interface, 3, "WLDataDevice"); // Extensions PROTO::tearing = std::make_unique(&wp_tearing_control_manager_v1_interface, 1, "TearingControl"); diff --git a/src/managers/SeatManager.cpp b/src/managers/SeatManager.cpp index d962dd84..76840bd3 100644 --- a/src/managers/SeatManager.cpp +++ b/src/managers/SeatManager.cpp @@ -1,5 +1,6 @@ #include "SeatManager.hpp" #include "../protocols/core/Seat.hpp" +#include "../protocols/core/DataDevice.hpp" #include "../Compositor.hpp" #include "../devices/IKeyboard.hpp" #include @@ -222,6 +223,8 @@ void CSeatManager::sendPointerMotion(uint32_t timeMs, const Vector2D& local) { p->sendMotion(timeMs, local); } + + lastLocalCoords = local; } void CSeatManager::sendPointerButton(uint32_t timeMs, uint32_t key, wl_pointer_button_state state_) { @@ -424,6 +427,28 @@ SP CSeatManager::seatResourceForClient(wl_client* client) { return PROTO::seat->seatResourceForClient(client); } +void CSeatManager::setCurrentSelection(SP source) { + if (source == selection.currentSelection) { + Debug::log(WARN, "[seat] duplicated setCurrentSelection?"); + return; + } + + selection.destroySelection.reset(); + + if (selection.currentSelection) + selection.currentSelection->cancelled(); + + if (!source) + PROTO::data->setSelection(nullptr); + + selection.currentSelection = source; + + if (source) { + selection.destroySelection = source->events.destroy.registerListener([this](std::any d) { setCurrentSelection(nullptr); }); + PROTO::data->setSelection(source); + } +} + void CSeatManager::setGrab(SP grab) { if (seatGrab) { auto oldGrab = seatGrab; @@ -441,6 +466,19 @@ void CSeatManager::setGrab(SP grab) { refocusGrab(); } +void CSeatManager::resendEnterEvents() { + wlr_surface* kb = state.keyboardFocus; + wlr_surface* pt = state.pointerFocus; + + auto last = lastLocalCoords; + + setKeyboardFocus(nullptr); + setPointerFocus(nullptr, {}); + + setKeyboardFocus(kb); + setPointerFocus(pt, last); +} + bool CSeatGrab::accepts(wlr_surface* surf) { return std::find(surfs.begin(), surfs.end(), surf) != surfs.end(); } diff --git a/src/managers/SeatManager.hpp b/src/managers/SeatManager.hpp index 16f11ffd..81ca663d 100644 --- a/src/managers/SeatManager.hpp +++ b/src/managers/SeatManager.hpp @@ -5,6 +5,7 @@ #include "../macros.hpp" #include "../helpers/signal/Signal.hpp" #include "../helpers/Vector2D.hpp" +#include "../protocols/types/DataDevice.hpp" #include constexpr size_t MAX_SERIAL_STORE_LEN = 100; @@ -72,6 +73,8 @@ class CSeatManager { void sendTouchShape(int32_t id, const Vector2D& shape); void sendTouchOrientation(int32_t id, double angle); + void resendEnterEvents(); + uint32_t nextSerial(SP seatResource); // pops the serial if it was valid, meaning it is consumed. bool serialValid(SP seatResource, uint32_t serial); @@ -103,6 +106,13 @@ class CSeatManager { CSignal setCursor; // SSetCursorEvent } events; + struct { + WP currentSelection; + CHyprSignalListener destroySelection; + } selection; + + void setCurrentSelection(SP source); + // do not write to directly, use set... WP mouse; WP keyboard; @@ -132,6 +142,8 @@ class CSeatManager { CHyprSignalListener newSeatResource; } listeners; + Vector2D lastLocalCoords; + DYNLISTENER(keyboardSurfaceDestroy); DYNLISTENER(pointerSurfaceDestroy); DYNLISTENER(touchSurfaceDestroy); diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index fe19c98f..d74657d9 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -15,6 +15,7 @@ #include "../../protocols/VirtualPointer.hpp" #include "../../protocols/LayerShell.hpp" #include "../../protocols/core/Seat.hpp" +#include "../../protocols/core/DataDevice.hpp" #include "../../protocols/XDGShell.hpp" #include "../../devices/Mouse.hpp" @@ -137,7 +138,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { static auto PRESIZECURSORICON = CConfigValue("general:hover_icon_on_border"); static auto PZOOMFACTOR = CConfigValue("cursor:zoom_factor"); - const auto FOLLOWMOUSE = *PFOLLOWONDND && m_sDrag.drag ? 1 : *PFOLLOWMOUSE; + const auto FOLLOWMOUSE = *PFOLLOWONDND && PROTO::data->dndActive() ? 1 : *PFOLLOWMOUSE; m_pFoundSurfaceToFocus = nullptr; m_pFoundLSToFocus.reset(); @@ -218,10 +219,9 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { Debug::log(ERR, "BUG THIS: Null SURF/CONSTRAINT in mouse refocus. Ignoring constraints. {:x} {:x}", (uintptr_t)SURF, (uintptr_t)CONSTRAINT.get()); } - // update stuff - updateDragIcon(); - - if (!m_sDrag.drag && !m_lCurrentlyHeldButtons.empty() && g_pCompositor->m_pLastFocus && g_pSeatManager->state.pointerFocus) { + // if we are holding a pointer button, + // and we're not dnd-ing, don't refocus. Keep focus on last surface. + if (!PROTO::data->dndActive() && !m_lCurrentlyHeldButtons.empty() && g_pCompositor->m_pLastFocus && g_pSeatManager->state.pointerFocus && !m_bHardInput) { foundSurface = g_pSeatManager->state.pointerFocus; pFoundLayerSurface = g_pCompositor->getLayerSurfaceFromSurface(foundSurface); if (pFoundLayerSurface) { @@ -1356,22 +1356,6 @@ void CInputManager::refocus() { mouseMoveUnified(0, true); } -void CInputManager::updateDragIcon() { - if (!m_sDrag.dragIcon) - return; - - switch (m_sDrag.dragIcon->drag->grab_type) { - case WLR_DRAG_GRAB_KEYBOARD: break; - case WLR_DRAG_GRAB_KEYBOARD_POINTER: { - CBox box = {m_sDrag.pos.x - 2, m_sDrag.pos.y - 2, m_sDrag.dragIcon->surface->current.width + 4, m_sDrag.dragIcon->surface->current.height + 4}; - g_pHyprRenderer->damageBox(&box); - m_sDrag.pos = getMouseCoordsInternal(); - break; - } - default: break; - } -} - void CInputManager::unconstrainMouse() { if (g_pSeatManager->mouse.expired()) return; @@ -1665,7 +1649,7 @@ std::string CInputManager::getNameForNewDevice(std::string internalName) { void CInputManager::releaseAllMouseButtons() { const auto buttonsCopy = m_lCurrentlyHeldButtons; - if (g_pInputManager->m_sDrag.drag) + if (PROTO::data->dndActive()) return; for (auto& mb : buttonsCopy) { diff --git a/src/managers/input/InputManager.hpp b/src/managers/input/InputManager.hpp index 85efecb8..f7d9ae57 100644 --- a/src/managers/input/InputManager.hpp +++ b/src/managers/input/InputManager.hpp @@ -112,7 +112,6 @@ class CInputManager { void setTouchDeviceConfigs(SP dev = nullptr); void setTabletConfigs(); - void updateDragIcon(); void updateCapabilities(); void updateKeyboardsLeds(SP); @@ -143,8 +142,6 @@ class CInputManager { // for refocus to be forced PHLWINDOWREF m_pForcedFocus; - SDrag m_sDrag; - std::vector> m_vKeyboards; std::vector> m_vPointers; std::vector> m_vTouches; diff --git a/src/protocols/core/DataDevice.cpp b/src/protocols/core/DataDevice.cpp new file mode 100644 index 00000000..d332a0be --- /dev/null +++ b/src/protocols/core/DataDevice.cpp @@ -0,0 +1,655 @@ +#include "DataDevice.hpp" +#include +#include "../../managers/SeatManager.hpp" +#include "../../managers/PointerManager.hpp" +#include "../../Compositor.hpp" +#include "Seat.hpp" + +#define LOGM PROTO::data->protoLog + +CWLDataOfferResource::CWLDataOfferResource(SP resource_, SP source_) : source(source_), resource(resource_) { + if (!good()) + return; + + resource->setDestroy([this](CWlDataOffer* r) { + if (!dead) + PROTO::data->completeDrag(); + PROTO::data->destroyResource(this); + }); + resource->setOnDestroy([this](CWlDataOffer* r) { + if (!dead) + PROTO::data->completeDrag(); + PROTO::data->destroyResource(this); + }); + + resource->setAccept([this](CWlDataOffer* r, uint32_t serial, const char* mime) { + if (!source) { + LOGM(WARN, "Possible bug: Accept on an offer w/o a source"); + return; + } + + if (dead) { + LOGM(WARN, "Possible bug: Accept on an offer that's dead"); + return; + } + + LOGM(LOG, "Offer {:x} accepts data from source {:x} with mime {}", (uintptr_t)this, (uintptr_t)source.get(), mime ? mime : "null"); + + source->accepted(mime ? mime : ""); + accepted = mime; + }); + + resource->setReceive([this](CWlDataOffer* r, const char* mime, uint32_t fd) { + if (!source) { + LOGM(WARN, "Possible bug: Receive on an offer w/o a source"); + close(fd); + return; + } + + if (dead) { + LOGM(WARN, "Possible bug: Receive on an offer that's dead"); + close(fd); + return; + } + + LOGM(LOG, "Offer {:x} asks to send data from source {:x}", (uintptr_t)this, (uintptr_t)source.get()); + + if (!accepted) { + LOGM(WARN, "Offer was never accepted, sending accept first"); + source->accepted(mime ? mime : ""); + } + + source->send(mime ? mime : "", fd); + + recvd = true; + + // if (source->hasDnd()) + // PROTO::data->completeDrag(); + }); + + resource->setFinish([this](CWlDataOffer* r) { + dead = true; + if (!source || !recvd || !accepted) + PROTO::data->abortDrag(); + else + PROTO::data->completeDrag(); + }); +} + +bool CWLDataOfferResource::good() { + return resource->resource(); +} + +void CWLDataOfferResource::sendData() { + if (!source) + return; + + resource->sendSourceActions(7); + resource->sendAction(WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE); + + for (auto& m : source->mimes()) { + LOGM(LOG, " | offer {:x} supports mime {}", (uintptr_t)this, m); + resource->sendOffer(m.c_str()); + } +} + +CWLDataSourceResource::CWLDataSourceResource(SP resource_, SP device_) : device(device_), resource(resource_) { + if (!good()) + return; + + resource->setData(this); + + resource->setDestroy([this](CWlDataSource* r) { + events.destroy.emit(); + PROTO::data->onDestroyDataSource(self); + PROTO::data->destroyResource(this); + }); + resource->setOnDestroy([this](CWlDataSource* r) { + events.destroy.emit(); + PROTO::data->onDestroyDataSource(self); + PROTO::data->destroyResource(this); + }); + + resource->setOffer([this](CWlDataSource* r, const char* mime) { mimeTypes.push_back(mime); }); + resource->setSetActions([this](CWlDataSource* r, uint32_t a) { + LOGM(LOG, "DataSource {:x} actions {}", (uintptr_t)this, a); + actions = (wl_data_device_manager_dnd_action)a; + }); +} + +CWLDataSourceResource::~CWLDataSourceResource() { + events.destroy.emit(); + PROTO::data->onDestroyDataSource(self); +} + +SP CWLDataSourceResource::fromResource(wl_resource* res) { + auto data = (CWLDataSourceResource*)(((CWlDataSource*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; +} + +bool CWLDataSourceResource::good() { + return resource->resource(); +} + +void CWLDataSourceResource::accepted(const std::string& mime) { + if (mime.empty()) { + resource->sendTarget(nullptr); + return; + } + + if (std::find(mimeTypes.begin(), mimeTypes.end(), mime) == mimeTypes.end()) { + LOGM(ERR, "Compositor/App bug: CWLDataSourceResource::sendAccepted with non-existent mime"); + return; + } + + resource->sendTarget(mime.c_str()); +} + +std::vector CWLDataSourceResource::mimes() { + return mimeTypes; +} + +void CWLDataSourceResource::send(const std::string& mime, uint32_t fd) { + if (std::find(mimeTypes.begin(), mimeTypes.end(), mime) == mimeTypes.end()) { + LOGM(ERR, "Compositor/App bug: CWLDataSourceResource::sendAskSend with non-existent mime"); + close(fd); + return; + } + + resource->sendSend(mime.c_str(), fd); + close(fd); +} + +void CWLDataSourceResource::cancelled() { + resource->sendCancelled(); +} + +bool CWLDataSourceResource::hasDnd() { + return dnd; +} + +bool CWLDataSourceResource::dndDone() { + return dndSuccess; +} + +void CWLDataSourceResource::error(uint32_t code, const std::string& msg) { + resource->error(code, msg); +} + +void CWLDataSourceResource::sendDndDropPerformed() { + if (resource->version() < 3) + return; + resource->sendDndDropPerformed(); +} + +void CWLDataSourceResource::sendDndFinished() { + if (resource->version() < 3) + return; + resource->sendDndFinished(); +} + +void CWLDataSourceResource::sendDndAction(wl_data_device_manager_dnd_action a) { + if (resource->version() < 3) + return; + resource->sendAction(a); +} + +CWLDataDeviceResource::CWLDataDeviceResource(SP resource_) : resource(resource_) { + if (!good()) + return; + + resource->setRelease([this](CWlDataDevice* r) { PROTO::data->destroyResource(this); }); + resource->setOnDestroy([this](CWlDataDevice* r) { PROTO::data->destroyResource(this); }); + + pClient = resource->client(); + + resource->setSetSelection([this](CWlDataDevice* r, wl_resource* sourceR, uint32_t serial) { + auto source = sourceR ? CWLDataSourceResource::fromResource(sourceR) : CSharedPointer{}; + if (!source) { + LOGM(LOG, "Reset selection received"); + g_pSeatManager->setCurrentSelection(nullptr); + return; + } + + if (source && source->used) + LOGM(WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this."); + + source->markUsed(); + + g_pSeatManager->setCurrentSelection(source); + }); + + resource->setStartDrag([this](CWlDataDevice* r, wl_resource* sourceR, wl_resource* origin, wl_resource* icon, uint32_t serial) { + auto source = CWLDataSourceResource::fromResource(sourceR); + if (!source) { + LOGM(ERR, "No source in drag"); + return; + } + + if (source && source->used) + LOGM(WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this."); + + source->markUsed(); + + source->dnd = true; + + PROTO::data->initiateDrag(source, wlr_surface_from_resource(icon), wlr_surface_from_resource(origin)); + }); +} + +bool CWLDataDeviceResource::good() { + return resource->resource(); +} + +wl_client* CWLDataDeviceResource::client() { + return pClient; +} + +void CWLDataDeviceResource::sendDataOffer(SP offer) { + if (offer) + resource->sendDataOffer(offer->resource.get()); + else + resource->sendDataOfferRaw(nullptr); +} + +void CWLDataDeviceResource::sendEnter(uint32_t serial, wlr_surface* surf, const Vector2D& local, SP offer) { + resource->sendEnterRaw(serial, surf->resource, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y), offer->resource->resource()); +} + +void CWLDataDeviceResource::sendLeave() { + resource->sendLeave(); +} + +void CWLDataDeviceResource::sendMotion(uint32_t timeMs, const Vector2D& local) { + resource->sendMotion(timeMs, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y)); +} + +void CWLDataDeviceResource::sendDrop() { + resource->sendDrop(); +} + +void CWLDataDeviceResource::sendSelection(SP offer) { + if (!offer) + resource->sendSelectionRaw(nullptr); + else + resource->sendSelection(offer->resource.get()); +} + +CWLDataDeviceManagerResource::CWLDataDeviceManagerResource(SP resource_) : resource(resource_) { + if (!good()) + return; + + resource->setOnDestroy([this](CWlDataDeviceManager* r) { PROTO::data->destroyResource(this); }); + + resource->setCreateDataSource([this](CWlDataDeviceManager* r, uint32_t id) { + std::erase_if(sources, [](const auto& e) { return e.expired(); }); + + const auto RESOURCE = PROTO::data->m_vSources.emplace_back(makeShared(makeShared(r->client(), r->version(), id), device.lock())); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::data->m_vSources.pop_back(); + return; + } + + if (!device) + LOGM(WARN, "New data source before a device was created"); + + RESOURCE->self = RESOURCE; + + LOGM(LOG, "New data source bound at {:x}", (uintptr_t)RESOURCE.get()); + }); + + resource->setGetDataDevice([this](CWlDataDeviceManager* r, uint32_t id, wl_resource* seat) { + const auto RESOURCE = PROTO::data->m_vDevices.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::data->m_vDevices.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + + for (auto& s : sources) { + if (!s) + continue; + s->device = RESOURCE; + } + + LOGM(LOG, "New data device bound at {:x}", (uintptr_t)RESOURCE.get()); + }); +} + +bool CWLDataDeviceManagerResource::good() { + return resource->resource(); +} + +CWLDataDeviceProtocol::CWLDataDeviceProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + ; +} + +void CWLDataDeviceProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); + + if (!RESOURCE->good()) { + wl_client_post_no_memory(client); + m_vManagers.pop_back(); + return; + } + + LOGM(LOG, "New datamgr resource bound at {:x}", (uintptr_t)RESOURCE.get()); + + // we need to do it here because protocols come before seatMgr + if (!listeners.onKeyboardFocusChange) + listeners.onKeyboardFocusChange = g_pSeatManager->events.keyboardFocusChange.registerListener([this](std::any d) { this->onKeyboardFocus(); }); +} + +void CWLDataDeviceProtocol::destroyResource(CWLDataDeviceManagerResource* seat) { + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == seat; }); +} + +void CWLDataDeviceProtocol::destroyResource(CWLDataDeviceResource* resource) { + std::erase_if(m_vDevices, [&](const auto& other) { return other.get() == resource; }); +} + +void CWLDataDeviceProtocol::destroyResource(CWLDataSourceResource* resource) { + std::erase_if(m_vSources, [&](const auto& other) { return other.get() == resource; }); +} + +void CWLDataDeviceProtocol::destroyResource(CWLDataOfferResource* resource) { + std::erase_if(m_vOffers, [&](const auto& other) { return other.get() == resource; }); +} + +SP CWLDataDeviceProtocol::dataDeviceForClient(wl_client* c) { + auto it = std::find_if(m_vDevices.begin(), m_vDevices.end(), [c](const auto& e) { return e->client() == c; }); + if (it == m_vDevices.end()) + return nullptr; + return *it; +} + +void CWLDataDeviceProtocol::sendSelectionToDevice(SP dev, SP sel) { + if (!sel) { + dev->sendSelection(nullptr); + return; + } + + const auto OFFER = m_vOffers.emplace_back(makeShared(makeShared(dev->resource->client(), dev->resource->version(), 0), sel)); + + if (!OFFER->good()) { + dev->resource->noMemory(); + m_vOffers.pop_back(); + return; + } + + LOGM(LOG, "New offer {:x} for data source {:x}", (uintptr_t)OFFER.get(), (uintptr_t)sel.get()); + + dev->sendDataOffer(OFFER); + OFFER->sendData(); + dev->sendSelection(OFFER); +} + +void CWLDataDeviceProtocol::onDestroyDataSource(WP source) { + if (dnd.currentSource == source) + abortDrag(); +} + +void CWLDataDeviceProtocol::setSelection(SP source) { + for (auto& o : m_vOffers) { + if (o->source && o->source->hasDnd()) + continue; + o->dead = true; + } + + if (!source) { + LOGM(LOG, "resetting selection"); + + if (!g_pSeatManager->state.keyboardFocusResource) + return; + + auto DESTDEVICE = dataDeviceForClient(g_pSeatManager->state.keyboardFocusResource->client()); + if (DESTDEVICE) + sendSelectionToDevice(DESTDEVICE, nullptr); + + return; + } + + LOGM(LOG, "New selection for data source {:x}", (uintptr_t)source.get()); + + if (!g_pSeatManager->state.keyboardFocusResource) + return; + + auto DESTDEVICE = dataDeviceForClient(g_pSeatManager->state.keyboardFocusResource->client()); + + if (!DESTDEVICE) { + LOGM(LOG, "CWLDataDeviceProtocol::setSelection: cannot send selection to a client without a data_device"); + return; + } + + sendSelectionToDevice(DESTDEVICE, source); +} + +void CWLDataDeviceProtocol::updateSelection() { + if (!g_pSeatManager->state.keyboardFocusResource) + return; + + auto DESTDEVICE = dataDeviceForClient(g_pSeatManager->state.keyboardFocusResource->client()); + + if (!DESTDEVICE) { + LOGM(LOG, "CWLDataDeviceProtocol::onKeyboardFocus: cannot send selection to a client without a data_device"); + return; + } + + sendSelectionToDevice(DESTDEVICE, g_pSeatManager->selection.currentSelection.lock()); +} + +void CWLDataDeviceProtocol::onKeyboardFocus() { + for (auto& o : m_vOffers) { + o->dead = true; + } + + updateSelection(); + updateDrag(); +} + +void CWLDataDeviceProtocol::initiateDrag(WP currentSource, wlr_surface* dragSurface, wlr_surface* origin) { + + if (dnd.currentSource) { + LOGM(WARN, "New drag started while old drag still active??"); + abortDrag(); + } + + g_pInputManager->setCursorImageUntilUnset("grabbing"); + dnd.overriddenCursor = true; + + LOGM(LOG, "initiateDrag: source {:x}, surface: {:x}, origin: {:x}", (uintptr_t)currentSource.get(), (uintptr_t)dragSurface, (uintptr_t)origin); + + currentSource->used = true; + + dnd.currentSource = currentSource; + dnd.originSurface = origin; + dnd.dndSurface = dragSurface; + dnd.hyprListener_dndSurfaceDestroy.initCallback( + &dragSurface->events.destroy, [this](void* owner, void* data) { abortDrag(); }, nullptr, "CWLDataDeviceProtocol::drag"); + dnd.hyprListener_dndSurfaceCommit.initCallback( + &dragSurface->events.commit, + [this](void* owner, void* data) { + if (dnd.dndSurface->pending.buffer_width > 0 && dnd.dndSurface->pending.buffer_height > 0 && !dnd.dndSurface->mapped) { + wlr_surface_map(dnd.dndSurface); + return; + } + + if (dnd.dndSurface->pending.buffer_width <= 0 && dnd.dndSurface->pending.buffer_height <= 0 && dnd.dndSurface->mapped) { + wlr_surface_unmap(dnd.dndSurface); + return; + } + }, + nullptr, "CWLDataDeviceProtocol::drag"); + + dnd.mouseButton = g_pHookSystem->hookDynamic("mouseButton", [this](void* self, SCallbackInfo& info, std::any e) { + auto E = std::any_cast(e); + if (E.state == WL_POINTER_BUTTON_STATE_RELEASED) { + LOGM(LOG, "Dropping drag on mouseUp"); + dropDrag(); + } + }); + + dnd.touchUp = g_pHookSystem->hookDynamic("touchUp", [this](void* self, SCallbackInfo& info, std::any e) { + LOGM(LOG, "Dropping drag on touchUp"); + dropDrag(); + }); + + dnd.mouseMove = g_pHookSystem->hookDynamic("mouseMove", [this](void* self, SCallbackInfo& info, std::any e) { + auto V = std::any_cast(e); + if (dnd.focusedDevice && g_pSeatManager->state.keyboardFocus) { + auto surf = CWLSurface::surfaceFromWlr(g_pSeatManager->state.keyboardFocus); + + if (!surf) + return; + + const auto box = surf->getSurfaceBoxGlobal(); + + if (!box.has_value()) + return; + + dnd.focusedDevice->sendMotion(0 /* this is a hack */, V - box->pos()); + LOGM(LOG, "Drag motion {}", V - box->pos()); + } + }); + + dnd.touchMove = g_pHookSystem->hookDynamic("touchMove", [this](void* self, SCallbackInfo& info, std::any e) { + auto E = std::any_cast(e); + if (dnd.focusedDevice && g_pSeatManager->state.keyboardFocus) { + auto surf = CWLSurface::surfaceFromWlr(g_pSeatManager->state.keyboardFocus); + + if (!surf) + return; + + const auto box = surf->getSurfaceBoxGlobal(); + + if (!box.has_value()) + return; + + dnd.focusedDevice->sendMotion(E.timeMs, E.pos); + LOGM(LOG, "Drag motion {}", E.pos); + } + }); + + // make a new offer, etc + updateDrag(); +} + +void CWLDataDeviceProtocol::updateDrag() { + if (!dnd.currentSource) + return; + + if (dnd.focusedDevice) + dnd.focusedDevice->sendLeave(); + + if (!g_pSeatManager->state.keyboardFocusResource) + return; + + dnd.focusedDevice = dataDeviceForClient(g_pSeatManager->state.keyboardFocusResource->client()); + + if (!dnd.focusedDevice) + return; + + // make a new offer + const auto OFFER = m_vOffers.emplace_back( + makeShared(makeShared(dnd.focusedDevice->resource->client(), dnd.focusedDevice->resource->version(), 0), dnd.currentSource.lock())); + + if (!OFFER->good()) { + dnd.currentSource->resource->noMemory(); + m_vOffers.pop_back(); + return; + } + + LOGM(LOG, "New dnd offer {:x} for data source {:x}", (uintptr_t)OFFER.get(), (uintptr_t)dnd.currentSource.get()); + + dnd.focusedDevice->sendDataOffer(OFFER); + OFFER->sendData(); + dnd.focusedDevice->sendEnter(wl_display_next_serial(g_pCompositor->m_sWLDisplay), g_pSeatManager->state.keyboardFocus, + Vector2D{g_pSeatManager->state.keyboardFocus->current.width, g_pSeatManager->state.keyboardFocus->current.height} / 2.F, OFFER); +} + +void CWLDataDeviceProtocol::resetDndState() { + dnd.dndSurface = nullptr; + dnd.hyprListener_dndSurfaceDestroy.removeCallback(); + dnd.hyprListener_dndSurfaceCommit.removeCallback(); + dnd.mouseButton.reset(); + dnd.mouseMove.reset(); + dnd.touchUp.reset(); + dnd.touchMove.reset(); +} + +void CWLDataDeviceProtocol::dropDrag() { + if (!dnd.focusedDevice || !dnd.currentSource) { + if (dnd.currentSource) + abortDrag(); + return; + } + + dnd.currentSource->sendDndDropPerformed(); + dnd.focusedDevice->sendDrop(); + dnd.focusedDevice->sendLeave(); + + resetDndState(); + + if (dnd.overriddenCursor) + g_pInputManager->unsetCursorImage(); + dnd.overriddenCursor = false; +} + +void CWLDataDeviceProtocol::completeDrag() { + resetDndState(); + + if (!dnd.focusedDevice || !dnd.currentSource) + return; + + dnd.currentSource->sendDndFinished(); + + dnd.focusedDevice.reset(); + dnd.currentSource.reset(); + + g_pSeatManager->resendEnterEvents(); +} + +void CWLDataDeviceProtocol::abortDrag() { + resetDndState(); + + if (dnd.overriddenCursor) + g_pInputManager->unsetCursorImage(); + dnd.overriddenCursor = false; + + if (!dnd.focusedDevice || !dnd.currentSource) + return; + + dnd.focusedDevice->sendLeave(); + dnd.currentSource->cancelled(); + + dnd.focusedDevice.reset(); + dnd.currentSource.reset(); + + g_pSeatManager->resendEnterEvents(); +} + +void CWLDataDeviceProtocol::renderDND(CMonitor* pMonitor, timespec* when) { + if (!dnd.dndSurface || !wlr_surface_get_texture(dnd.dndSurface)) + return; + + const auto POS = g_pInputManager->getMouseCoordsInternal(); + + CBox box = CBox{POS, {dnd.dndSurface->current.width, dnd.dndSurface->current.height}} + .translate(-pMonitor->vecPosition + g_pPointerManager->cursorSizeLogical() / 2.F) + .scale(pMonitor->scale); + g_pHyprOpenGL->renderTexture(wlr_surface_get_texture(dnd.dndSurface), &box, 1.F); + + box = CBox{POS, {dnd.dndSurface->current.width, dnd.dndSurface->current.height}}.translate(g_pPointerManager->cursorSizeLogical() / 2.F); + g_pHyprRenderer->damageBox(&box); + + wlr_surface_send_frame_done(dnd.dndSurface, when); +} + +bool CWLDataDeviceProtocol::dndActive() { + return dnd.currentSource; +} diff --git a/src/protocols/core/DataDevice.hpp b/src/protocols/core/DataDevice.hpp new file mode 100644 index 00000000..3112b720 --- /dev/null +++ b/src/protocols/core/DataDevice.hpp @@ -0,0 +1,194 @@ +#pragma once + +/* + Implementations for: + - wl_data_offer + - wl_data_source + - wl_data_device + - wl_data_device_manager +*/ + +#include +#include +#include +#include "../WaylandProtocol.hpp" +#include +#include "wayland.hpp" +#include "../../helpers/signal/Signal.hpp" +#include "../../helpers/Vector2D.hpp" +#include "../types/DataDevice.hpp" + +class CWLDataDeviceResource; +class CWLDataDeviceManagerResource; +class CWLDataSourceResource; +class CWLDataOfferResource; + +class CMonitor; + +class CWLDataOfferResource { + public: + CWLDataOfferResource(SP resource_, SP source_); + + bool good(); + void sendData(); + + WP source; + + bool dead = false; + bool accepted = false; + bool recvd = false; + + uint32_t actions = 0; + + private: + SP resource; + wl_client* pClient = nullptr; + + friend class CWLDataDeviceResource; +}; + +class CWLDataSourceResource : public IDataSource { + public: + CWLDataSourceResource(SP resource_, SP device_); + ~CWLDataSourceResource(); + static SP fromResource(wl_resource*); + + bool good(); + + virtual std::vector mimes(); + virtual void send(const std::string& mime, uint32_t fd); + virtual void accepted(const std::string& mime); + virtual void cancelled(); + virtual bool hasDnd(); + virtual bool dndDone(); + virtual void error(uint32_t code, const std::string& msg); + + void sendDndDropPerformed(); + void sendDndFinished(); + void sendDndAction(wl_data_device_manager_dnd_action a); + + bool used = false; + bool dnd = false; + bool dndSuccess = false; + + WP device; + WP self; + + std::vector mimeTypes; + uint32_t actions = 0; + + private: + SP resource; + wl_client* pClient = nullptr; + + friend class CWLDataDeviceProtocol; +}; + +class CWLDataDeviceResource { + public: + CWLDataDeviceResource(SP resource_); + + bool good(); + wl_client* client(); + + void sendDataOffer(SP offer); + void sendEnter(uint32_t serial, wlr_surface* surf, const Vector2D& local, SP offer); + void sendLeave(); + void sendMotion(uint32_t timeMs, const Vector2D& local); + void sendDrop(); + void sendSelection(SP offer); + + WP self; + + private: + SP resource; + wl_client* pClient = nullptr; + + friend class CWLDataDeviceProtocol; +}; + +class CWLDataDeviceManagerResource { + public: + CWLDataDeviceManagerResource(SP resource_); + + bool good(); + + WP device; + std::vector> sources; + + private: + SP resource; +}; + +class CWLDataDeviceProtocol : public IWaylandProtocol { + public: + CWLDataDeviceProtocol(const wl_interface* iface, const int& ver, const std::string& name); + + virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); + + // renders and damages the dnd icon, if present + void renderDND(CMonitor* pMonitor, timespec* when); + // for inputmgr to force refocus + // TODO: move handling to seatmgr + bool dndActive(); + + private: + void destroyResource(CWLDataDeviceManagerResource* resource); + void destroyResource(CWLDataDeviceResource* resource); + void destroyResource(CWLDataSourceResource* resource); + void destroyResource(CWLDataOfferResource* resource); + + // + std::vector> m_vManagers; + std::vector> m_vDevices; + std::vector> m_vSources; + std::vector> m_vOffers; + + // + + void onDestroyDataSource(WP source); + void setSelection(SP source); + void sendSelectionToDevice(SP dev, SP sel); + void updateSelection(); + void onKeyboardFocus(); + + struct { + WP focusedDevice; + WP currentSource; + wlr_surface* dndSurface = nullptr; + wlr_surface* originSurface = nullptr; // READ-ONLY + bool overriddenCursor = false; + DYNLISTENER(dndSurfaceDestroy); + DYNLISTENER(dndSurfaceCommit); + + // for ending a dnd + SP mouseMove; + SP mouseButton; + SP touchUp; + SP touchMove; + } dnd; + + void abortDrag(); + void initiateDrag(WP currentSource, wlr_surface* dragSurface, wlr_surface* origin); + void updateDrag(); + void dropDrag(); + void completeDrag(); + void resetDndState(); + + // + SP dataDeviceForClient(wl_client*); + + friend class CSeatManager; + friend class CWLDataDeviceManagerResource; + friend class CWLDataDeviceResource; + friend class CWLDataSourceResource; + friend class CWLDataOfferResource; + + struct { + CHyprSignalListener onKeyboardFocusChange; + } listeners; +}; + +namespace PROTO { + inline UP data; +}; diff --git a/src/protocols/types/DataDevice.cpp b/src/protocols/types/DataDevice.cpp new file mode 100644 index 00000000..47cbda8b --- /dev/null +++ b/src/protocols/types/DataDevice.cpp @@ -0,0 +1,17 @@ +#include "DataDevice.hpp" + +bool IDataSource::hasDnd() { + return false; +} + +bool IDataSource::dndDone() { + return false; +} + +bool IDataSource::used() { + return wasUsed; +} + +void IDataSource::markUsed() { + wasUsed = true; +} diff --git a/src/protocols/types/DataDevice.hpp b/src/protocols/types/DataDevice.hpp new file mode 100644 index 00000000..98a97c14 --- /dev/null +++ b/src/protocols/types/DataDevice.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include +#include "../../helpers/signal/Signal.hpp" + +class IDataSource { + public: + IDataSource() {} + virtual ~IDataSource() {} + + virtual std::vector mimes() = 0; + virtual void send(const std::string& mime, uint32_t fd) = 0; + virtual void accepted(const std::string& mime) = 0; + virtual void cancelled() = 0; + virtual bool hasDnd(); + virtual bool dndDone(); + virtual bool used(); + virtual void markUsed(); + virtual void error(uint32_t code, const std::string& msg) = 0; + + struct { + CSignal destroy; + } events; + + private: + bool wasUsed = false; +}; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 1aa246fb..c5c87ea1 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -12,6 +12,7 @@ #include "../protocols/LayerShell.hpp" #include "../protocols/XDGShell.hpp" #include "../protocols/PresentationTime.hpp" +#include "../protocols/core/DataDevice.hpp" extern "C" { #include @@ -1806,19 +1807,7 @@ void CHyprRenderer::damageMirrorsWith(CMonitor* pMonitor, const CRegion& pRegion } void CHyprRenderer::renderDragIcon(CMonitor* pMonitor, timespec* time) { - if (!(g_pInputManager->m_sDrag.dragIcon && g_pInputManager->m_sDrag.iconMapped && g_pInputManager->m_sDrag.dragIcon->surface)) - return; - - SRenderData renderdata = {pMonitor, time, g_pInputManager->m_sDrag.pos.x, g_pInputManager->m_sDrag.pos.y}; - renderdata.surface = g_pInputManager->m_sDrag.dragIcon->surface; - renderdata.w = g_pInputManager->m_sDrag.dragIcon->surface->current.width; - renderdata.h = g_pInputManager->m_sDrag.dragIcon->surface->current.height; - - wlr_surface_for_each_surface(g_pInputManager->m_sDrag.dragIcon->surface, renderSurface, &renderdata); - - CBox box = {g_pInputManager->m_sDrag.pos.x - 2, g_pInputManager->m_sDrag.pos.y - 2, g_pInputManager->m_sDrag.dragIcon->surface->current.width + 4, - g_pInputManager->m_sDrag.dragIcon->surface->current.height + 4}; - g_pHyprRenderer->damageBox(&box); + PROTO::data->renderDND(pMonitor, time); } DAMAGETRACKINGMODES CHyprRenderer::damageTrackingModeFromStr(const std::string& mode) { @@ -2500,7 +2489,7 @@ void CHyprRenderer::recheckSolitaryForMonitor(CMonitor* pMonitor) { const auto PWORKSPACE = pMonitor->activeWorkspace; - if (!PWORKSPACE || !PWORKSPACE->m_bHasFullscreenWindow || g_pInputManager->m_sDrag.drag || pMonitor->activeSpecialWorkspace || PWORKSPACE->m_fAlpha.value() != 1.f || + if (!PWORKSPACE || !PWORKSPACE->m_bHasFullscreenWindow || PROTO::data->dndActive() || pMonitor->activeSpecialWorkspace || PWORKSPACE->m_fAlpha.value() != 1.f || PWORKSPACE->m_vRenderOffset.value() != Vector2D{}) return; From eed1361f390590d406450c0747e4e2ce40a548fb Mon Sep 17 00:00:00 2001 From: Vaxry Date: Sun, 12 May 2024 15:55:46 +0100 Subject: [PATCH 22/57] wlr-data-device: move to hyprland impl --- CMakeLists.txt | 1 + protocols/meson.build | 1 + protocols/wlr-data-control-unstable-v1.xml | 278 ++++++++++++++++++ src/managers/ProtocolManager.cpp | 2 + src/managers/SeatManager.cpp | 2 + src/protocols/DataDeviceWlr.cpp | 310 +++++++++++++++++++++ src/protocols/DataDeviceWlr.hpp | 121 ++++++++ src/protocols/core/DataDevice.cpp | 2 + 8 files changed, 717 insertions(+) create mode 100644 protocols/wlr-data-control-unstable-v1.xml create mode 100644 src/protocols/DataDeviceWlr.cpp create mode 100644 src/protocols/DataDeviceWlr.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 80483c4f..3cdae161 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -271,6 +271,7 @@ protocolNew("protocols/wlr-virtual-pointer-unstable-v1.xml" "wlr-virtual-pointer protocolNew("protocols/input-method-unstable-v2.xml" "input-method-unstable-v2" true) protocolNew("protocols/wlr-output-management-unstable-v1.xml" "wlr-output-management-unstable-v1" true) protocolNew("protocols/kde-server-decoration.xml" "kde-server-decoration" true) +protocolNew("protocols/wlr-data-control-unstable-v1.xml" "wlr-data-control-unstable-v1" true) protocolNew("subprojects/hyprland-protocols/protocols/hyprland-focus-grab-v1.xml" "hyprland-focus-grab-v1" true) protocolNew("protocols/wlr-layer-shell-unstable-v1.xml" "wlr-layer-shell-unstable-v1" true) protocolNew("staging/tearing-control/tearing-control-v1.xml" "tearing-control-v1" false) diff --git a/protocols/meson.build b/protocols/meson.build index 4b7aa200..adedbf8e 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -42,6 +42,7 @@ new_protocols = [ ['wlr-output-management-unstable-v1.xml'], ['kde-server-decoration.xml'], ['wlr-layer-shell-unstable-v1.xml'], + ['wlr-data-control-unstable-v1.xml'], [hl_protocol_dir, 'protocols/hyprland-focus-grab-v1.xml'], [wl_protocol_dir, 'staging/tearing-control/tearing-control-v1.xml'], [wl_protocol_dir, 'staging/fractional-scale/fractional-scale-v1.xml'], diff --git a/protocols/wlr-data-control-unstable-v1.xml b/protocols/wlr-data-control-unstable-v1.xml new file mode 100644 index 00000000..75e8671b --- /dev/null +++ b/protocols/wlr-data-control-unstable-v1.xml @@ -0,0 +1,278 @@ + + + + Copyright © 2018 Simon Ser + Copyright © 2019 Ivan Molodetskikh + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + This protocol allows a privileged client to control data devices. In + particular, the client will be able to manage the current selection and take + the role of a clipboard manager. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + + + + + This interface is a manager that allows creating per-seat data device + controls. + + + + + Create a new data source. + + + + + + + Create a data device that can be used to manage a seat's selection. + + + + + + + + All objects created by the manager will still remain valid, until their + appropriate destroy request has been called. + + + + + + + This interface allows a client to manage a seat's selection. + + When the seat is destroyed, this object becomes inert. + + + + + This request asks the compositor to set the selection to the data from + the source on behalf of the client. + + The given source may not be used in any further set_selection or + set_primary_selection requests. Attempting to use a previously used + source is a protocol error. + + To unset the selection, set the source to NULL. + + + + + + + Destroys the data device object. + + + + + + The data_offer event introduces a new wlr_data_control_offer object, + which will subsequently be used in either the + wlr_data_control_device.selection event (for the regular clipboard + selections) or the wlr_data_control_device.primary_selection event (for + the primary clipboard selections). Immediately following the + wlr_data_control_device.data_offer event, the new data_offer object + will send out wlr_data_control_offer.offer events to describe the MIME + types it offers. + + + + + + + The selection event is sent out to notify the client of a new + wlr_data_control_offer for the selection for this device. The + wlr_data_control_device.data_offer and the wlr_data_control_offer.offer + events are sent out immediately before this event to introduce the data + offer object. The selection event is sent to a client when a new + selection is set. The wlr_data_control_offer is valid until a new + wlr_data_control_offer or NULL is received. The client must destroy the + previous selection wlr_data_control_offer, if any, upon receiving this + event. + + The first selection event is sent upon binding the + wlr_data_control_device object. + + + + + + + This data control object is no longer valid and should be destroyed by + the client. + + + + + + + + The primary_selection event is sent out to notify the client of a new + wlr_data_control_offer for the primary selection for this device. The + wlr_data_control_device.data_offer and the wlr_data_control_offer.offer + events are sent out immediately before this event to introduce the data + offer object. The primary_selection event is sent to a client when a + new primary selection is set. The wlr_data_control_offer is valid until + a new wlr_data_control_offer or NULL is received. The client must + destroy the previous primary selection wlr_data_control_offer, if any, + upon receiving this event. + + If the compositor supports primary selection, the first + primary_selection event is sent upon binding the + wlr_data_control_device object. + + + + + + + This request asks the compositor to set the primary selection to the + data from the source on behalf of the client. + + The given source may not be used in any further set_selection or + set_primary_selection requests. Attempting to use a previously used + source is a protocol error. + + To unset the primary selection, set the source to NULL. + + The compositor will ignore this request if it does not support primary + selection. + + + + + + + + + + + + The wlr_data_control_source object is the source side of a + wlr_data_control_offer. It is created by the source client in a data + transfer and provides a way to describe the offered data and a way to + respond to requests to transfer the data. + + + + + + + + + This request adds a MIME type to the set of MIME types advertised to + targets. Can be called several times to offer multiple types. + + Calling this after wlr_data_control_device.set_selection is a protocol + error. + + + + + + + Destroys the data source object. + + + + + + Request for data from the client. Send the data as the specified MIME + type over the passed file descriptor, then close it. + + + + + + + + This data source is no longer valid. The data source has been replaced + by another data source. + + The client should clean up and destroy this data source. + + + + + + + A wlr_data_control_offer represents a piece of data offered for transfer + by another client (the source client). The offer describes the different + MIME types that the data can be converted to and provides the mechanism + for transferring the data directly from the source client. + + + + + To transfer the offered data, the client issues this request and + indicates the MIME type it wants to receive. The transfer happens + through the passed file descriptor (typically created with the pipe + system call). The source client writes the data in the MIME type + representation requested and then closes the file descriptor. + + The receiving client reads from the read end of the pipe until EOF and + then closes its end, at which point the transfer is complete. + + This request may happen multiple times for different MIME types. + + + + + + + + Destroys the data offer object. + + + + + + Sent immediately after creating the wlr_data_control_offer object. + One event per offered MIME type. + + + + + diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp index 8167103f..4b03263b 100644 --- a/src/managers/ProtocolManager.cpp +++ b/src/managers/ProtocolManager.cpp @@ -29,6 +29,7 @@ #include "../protocols/LayerShell.hpp" #include "../protocols/PresentationTime.hpp" #include "../protocols/XDGShell.hpp" +#include "../protocols/DataDeviceWlr.hpp" #include "../protocols/core/Seat.hpp" #include "../protocols/core/DataDevice.hpp" @@ -69,6 +70,7 @@ CProtocolManager::CProtocolManager() { PROTO::layerShell = std::make_unique(&zwlr_layer_shell_v1_interface, 5, "LayerShell"); PROTO::presentation = std::make_unique(&wp_presentation_interface, 1, "Presentation"); PROTO::xdgShell = std::make_unique(&xdg_wm_base_interface, 6, "XDGShell"); + PROTO::dataWlr = std::make_unique(&zwlr_data_control_manager_v1_interface, 2, "DataDeviceWlr"); // Old protocol implementations. // TODO: rewrite them to use hyprwayland-scanner. diff --git a/src/managers/SeatManager.cpp b/src/managers/SeatManager.cpp index 76840bd3..a8505610 100644 --- a/src/managers/SeatManager.cpp +++ b/src/managers/SeatManager.cpp @@ -1,6 +1,7 @@ #include "SeatManager.hpp" #include "../protocols/core/Seat.hpp" #include "../protocols/core/DataDevice.hpp" +#include "../protocols/DataDeviceWlr.hpp" #include "../Compositor.hpp" #include "../devices/IKeyboard.hpp" #include @@ -446,6 +447,7 @@ void CSeatManager::setCurrentSelection(SP source) { if (source) { selection.destroySelection = source->events.destroy.registerListener([this](std::any d) { setCurrentSelection(nullptr); }); PROTO::data->setSelection(source); + PROTO::dataWlr->setSelection(source); } } diff --git a/src/protocols/DataDeviceWlr.cpp b/src/protocols/DataDeviceWlr.cpp new file mode 100644 index 00000000..a518b0ae --- /dev/null +++ b/src/protocols/DataDeviceWlr.cpp @@ -0,0 +1,310 @@ +#include "DataDeviceWlr.hpp" +#include +#include "../managers/SeatManager.hpp" +#include "core/Seat.hpp" + +#define LOGM PROTO::dataWlr->protoLog + +CWLRDataOffer::CWLRDataOffer(SP resource_, SP source_) : source(source_), resource(resource_) { + if (!good()) + return; + + resource->setDestroy([this](CZwlrDataControlOfferV1* r) { PROTO::dataWlr->destroyResource(this); }); + resource->setOnDestroy([this](CZwlrDataControlOfferV1* r) { PROTO::dataWlr->destroyResource(this); }); + + resource->setReceive([this](CZwlrDataControlOfferV1* r, const char* mime, int32_t fd) { + if (!source) { + LOGM(WARN, "Possible bug: Receive on an offer w/o a source"); + close(fd); + return; + } + + if (dead) { + LOGM(WARN, "Possible bug: Receive on an offer that's dead"); + close(fd); + return; + } + + LOGM(LOG, "Offer {:x} asks to send data from source {:x}", (uintptr_t)this, (uintptr_t)source.get()); + + source->send(mime, fd); + }); +} + +bool CWLRDataOffer::good() { + return resource->resource(); +} + +void CWLRDataOffer::sendData() { + if (!source) + return; + + for (auto& m : source->mimes()) { + resource->sendOffer(m.c_str()); + } +} + +CWLRDataSource::CWLRDataSource(SP resource_, SP device_) : device(device_), resource(resource_) { + if (!good()) + return; + + resource->setData(this); + + resource->setDestroy([this](CZwlrDataControlSourceV1* r) { + events.destroy.emit(); + PROTO::dataWlr->destroyResource(this); + }); + resource->setOnDestroy([this](CZwlrDataControlSourceV1* r) { + events.destroy.emit(); + PROTO::dataWlr->destroyResource(this); + }); + + resource->setOffer([this](CZwlrDataControlSourceV1* r, const char* mime) { mimeTypes.push_back(mime); }); +} + +CWLRDataSource::~CWLRDataSource() { + events.destroy.emit(); +} + +SP CWLRDataSource::fromResource(wl_resource* res) { + auto data = (CWLRDataSource*)(((CZwlrDataControlSourceV1*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; +} + +bool CWLRDataSource::good() { + return resource->resource(); +} + +std::vector CWLRDataSource::mimes() { + return mimeTypes; +} + +void CWLRDataSource::send(const std::string& mime, uint32_t fd) { + if (std::find(mimeTypes.begin(), mimeTypes.end(), mime) == mimeTypes.end()) { + LOGM(ERR, "Compositor/App bug: CWLRDataSource::sendAskSend with non-existent mime"); + close(fd); + return; + } + + resource->sendSend(mime.c_str(), fd); + close(fd); +} + +void CWLRDataSource::accepted(const std::string& mime) { + if (std::find(mimeTypes.begin(), mimeTypes.end(), mime) == mimeTypes.end()) + LOGM(ERR, "Compositor/App bug: CWLRDataSource::sendAccepted with non-existent mime"); + + // wlr has no accepted +} + +void CWLRDataSource::cancelled() { + resource->sendCancelled(); +} + +void CWLRDataSource::error(uint32_t code, const std::string& msg) { + resource->error(code, msg); +} + +CWLRDataDevice::CWLRDataDevice(SP resource_) : resource(resource_) { + if (!good()) + return; + + pClient = resource->client(); + + resource->setDestroy([this](CZwlrDataControlDeviceV1* r) { PROTO::dataWlr->destroyResource(this); }); + resource->setOnDestroy([this](CZwlrDataControlDeviceV1* r) { PROTO::dataWlr->destroyResource(this); }); + + resource->setSetSelection([this](CZwlrDataControlDeviceV1* r, wl_resource* sourceR) { + auto source = sourceR ? CWLRDataSource::fromResource(sourceR) : CSharedPointer{}; + if (!source) { + LOGM(LOG, "wlr reset selection received"); + g_pSeatManager->setCurrentSelection(nullptr); + return; + } + + if (source && source->used()) + LOGM(WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this."); + + source->markUsed(); + + LOGM(LOG, "wlr manager requests selection to {:x}", (uintptr_t)source.get()); + g_pSeatManager->setCurrentSelection(source); + }); + + resource->setSetPrimarySelection([this](CZwlrDataControlDeviceV1* r, wl_resource* sourceR) { + auto source = sourceR ? CWLRDataSource::fromResource(sourceR) : CSharedPointer{}; + if (!source) { + LOGM(LOG, "wlr reset primary selection received"); + g_pSeatManager->setCurrentSelection(nullptr); + return; + } + + if (source && source->used()) + LOGM(WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this."); + + source->markUsed(); + + LOGM(LOG, "wlr manager requests primary selection to {:x}", (uintptr_t)source.get()); + g_pSeatManager->setCurrentSelection(source); + }); +} + +bool CWLRDataDevice::good() { + return resource->resource(); +} + +wl_client* CWLRDataDevice::client() { + return pClient; +} + +void CWLRDataDevice::sendInitialSelections() { + PROTO::dataWlr->sendSelectionToDevice(self.lock(), g_pSeatManager->selection.currentSelection.lock(), false); + PROTO::dataWlr->sendSelectionToDevice(self.lock(), g_pSeatManager->selection.currentPrimarySelection.lock(), true); +} + +void CWLRDataDevice::sendDataOffer(SP offer) { + resource->sendDataOffer(offer->resource.get()); +} + +void CWLRDataDevice::sendSelection(SP selection) { + resource->sendSelection(selection->resource.get()); +} + +CWLRDataControlManagerResource::CWLRDataControlManagerResource(SP resource_) : resource(resource_) { + if (!good()) + return; + + resource->setDestroy([this](CZwlrDataControlManagerV1* r) { PROTO::dataWlr->destroyResource(this); }); + resource->setOnDestroy([this](CZwlrDataControlManagerV1* r) { PROTO::dataWlr->destroyResource(this); }); + + resource->setGetDataDevice([this](CZwlrDataControlManagerV1* r, uint32_t id, wl_resource* seat) { + const auto RESOURCE = PROTO::dataWlr->m_vDevices.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::dataWlr->m_vDevices.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + device = RESOURCE; + + for (auto& s : sources) { + if (!s) + continue; + s->device = RESOURCE; + } + + RESOURCE->sendInitialSelections(); + + LOGM(LOG, "New wlr data device bound at {:x}", (uintptr_t)RESOURCE.get()); + }); + + resource->setCreateDataSource([this](CZwlrDataControlManagerV1* r, uint32_t id) { + std::erase_if(sources, [](const auto& e) { return e.expired(); }); + + const auto RESOURCE = + PROTO::dataWlr->m_vSources.emplace_back(makeShared(makeShared(r->client(), r->version(), id), device.lock())); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::dataWlr->m_vSources.pop_back(); + return; + } + + if (!device) + LOGM(WARN, "New data source before a device was created"); + + RESOURCE->self = RESOURCE; + + sources.push_back(RESOURCE); + + LOGM(LOG, "New wlr data source bound at {:x}", (uintptr_t)RESOURCE.get()); + }); +} + +bool CWLRDataControlManagerResource::good() { + return resource->resource(); +} + +CDataDeviceWLRProtocol::CDataDeviceWLRProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + ; +} + +void CDataDeviceWLRProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); + + if (!RESOURCE->good()) { + wl_client_post_no_memory(client); + m_vManagers.pop_back(); + return; + } + + LOGM(LOG, "New wlr_data_control_manager at {:x}", (uintptr_t)RESOURCE.get()); +} + +void CDataDeviceWLRProtocol::destroyResource(CWLRDataControlManagerResource* resource) { + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); +} + +void CDataDeviceWLRProtocol::destroyResource(CWLRDataSource* resource) { + std::erase_if(m_vSources, [&](const auto& other) { return other.get() == resource; }); +} + +void CDataDeviceWLRProtocol::destroyResource(CWLRDataDevice* resource) { + std::erase_if(m_vDevices, [&](const auto& other) { return other.get() == resource; }); +} + +void CDataDeviceWLRProtocol::destroyResource(CWLRDataOffer* resource) { + std::erase_if(m_vOffers, [&](const auto& other) { return other.get() == resource; }); +} + +void CDataDeviceWLRProtocol::sendSelectionToDevice(SP dev, SP sel) { + if (!sel) + return; + + const auto OFFER = m_vOffers.emplace_back(makeShared(makeShared(dev->resource->client(), dev->resource->version(), 0), sel)); + + if (!OFFER->good()) { + dev->resource->noMemory(); + m_vOffers.pop_back(); + return; + } + + LOGM(LOG, "New offer {:x} for data source {:x}", (uintptr_t)OFFER.get(), (uintptr_t)sel.get()); + + dev->sendDataOffer(OFFER); + OFFER->sendData(); + dev->sendSelection(OFFER); +} + +void CDataDeviceWLRProtocol::setSelection(SP source) { + for (auto& o : m_vOffers) { + if (o->source && o->source->hasDnd()) + continue; + o->dead = true; + } + + if (!source) { + LOGM(LOG, "resetting selection"); + + for (auto& d : m_vDevices) { + sendSelectionToDevice(d, nullptr); + } + + return; + } + + LOGM(LOG, "New selection for data source {:x}", (uintptr_t)source.get()); + + for (auto& d : m_vDevices) { + sendSelectionToDevice(d, source); + } +} + +SP CDataDeviceWLRProtocol::dataDeviceForClient(wl_client* c) { + auto it = std::find_if(m_vDevices.begin(), m_vDevices.end(), [c](const auto& e) { return e->client() == c; }); + if (it == m_vDevices.end()) + return nullptr; + return *it; +} diff --git a/src/protocols/DataDeviceWlr.hpp b/src/protocols/DataDeviceWlr.hpp new file mode 100644 index 00000000..0b703347 --- /dev/null +++ b/src/protocols/DataDeviceWlr.hpp @@ -0,0 +1,121 @@ +#pragma once + +#include +#include +#include +#include "WaylandProtocol.hpp" +#include "wlr-data-control-unstable-v1.hpp" +#include "types/DataDevice.hpp" + +class CWLRDataControlManagerResource; +class CWLRDataSource; +class CWLRDataDevice; +class CWLRDataOffer; + +class CWLRDataOffer { + public: + CWLRDataOffer(SP resource_, SP source); + + bool good(); + void sendData(); + + bool dead = false; + + WP source; + + private: + SP resource; + + friend class CWLRDataDevice; +}; + +class CWLRDataSource : public IDataSource { + public: + CWLRDataSource(SP resource_, SP device_); + ~CWLRDataSource(); + static SP fromResource(wl_resource*); + + bool good(); + + virtual std::vector mimes(); + virtual void send(const std::string& mime, uint32_t fd); + virtual void accepted(const std::string& mime); + virtual void cancelled(); + virtual void error(uint32_t code, const std::string& msg); + + std::vector mimeTypes; + WP self; + WP device; + + private: + SP resource; +}; + +class CWLRDataDevice { + public: + CWLRDataDevice(SP resource_); + + bool good(); + wl_client* client(); + void sendInitialSelections(); + + void sendDataOffer(SP offer); + void sendSelection(SP selection); + + WP self; + + private: + SP resource; + wl_client* pClient = nullptr; + + friend class CDataDeviceWLRProtocol; +}; + +class CWLRDataControlManagerResource { + public: + CWLRDataControlManagerResource(SP resource_); + + bool good(); + + WP device; + std::vector> sources; + + private: + SP resource; +}; + +class CDataDeviceWLRProtocol : public IWaylandProtocol { + public: + CDataDeviceWLRProtocol(const wl_interface* iface, const int& ver, const std::string& name); + + virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); + + private: + void destroyResource(CWLRDataControlManagerResource* resource); + void destroyResource(CWLRDataSource* resource); + void destroyResource(CWLRDataDevice* resource); + void destroyResource(CWLRDataOffer* resource); + + // + std::vector> m_vManagers; + std::vector> m_vSources; + std::vector> m_vDevices; + std::vector> m_vOffers; + + // + void setSelection(SP source); + void sendSelectionToDevice(SP dev, SP sel); + + // + SP dataDeviceForClient(wl_client*); + + friend class CSeatManager; + friend class CWLRDataControlManagerResource; + friend class CWLRDataSource; + friend class CWLRDataDevice; + friend class CWLRDataOffer; +}; + +namespace PROTO { + inline UP dataWlr; +}; diff --git a/src/protocols/core/DataDevice.cpp b/src/protocols/core/DataDevice.cpp index d332a0be..2b9503b1 100644 --- a/src/protocols/core/DataDevice.cpp +++ b/src/protocols/core/DataDevice.cpp @@ -297,6 +297,8 @@ CWLDataDeviceManagerResource::CWLDataDeviceManagerResource(SPself = RESOURCE; + sources.push_back(RESOURCE); + LOGM(LOG, "New data source bound at {:x}", (uintptr_t)RESOURCE.get()); }); From 3eeaea5be9324121678774761c0226fe98bf7e5b Mon Sep 17 00:00:00 2001 From: Mihai Fufezan Date: Sun, 12 May 2024 20:26:42 +0300 Subject: [PATCH 23/57] Meson: add wayland.xml proto --- protocols/meson.build | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/protocols/meson.build b/protocols/meson.build index adedbf8e..d583c466 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -24,7 +24,6 @@ hyprwayland_scanner = find_program( ) protocols = [ - [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], [wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'], [wl_protocol_dir, 'unstable/text-input/text-input-unstable-v1.xml'], ['wlr-screencopy-unstable-v1.xml'], @@ -99,12 +98,27 @@ foreach p : new_protocols ) endforeach -wayland_server = dependency('wayland-server', version: '>=1.20.0') +wayland_server_dep = dependency('wayland-server', version: '>=1.20.0') +wayland_server_dir = wayland_server_dep.get_variable('pkgdatadir') + +wl_server_protos = [ + wayland_server_dir / 'wayland.xml' +] +wl_server_protos_gen = [] +foreach p : wl_server_protos + wl_server_protos_gen += custom_target( + p.underscorify(), + input: p, + install: false, + output: ['@BASENAME@.cpp', '@BASENAME@.hpp'], + command: [hyprwayland_scanner, '--wayland-enums', '@INPUT@', '@OUTDIR@'], + ) +endforeach lib_server_protos = static_library( 'server_protos', - wl_protos_src + wl_protos_headers + new_wl_protos, - dependencies: wayland_server.partial_dependency(compile_args: true), + wl_protos_src + wl_protos_headers + new_wl_protos + wl_server_protos_gen, + dependencies: wayland_server_dep.partial_dependency(compile_args: true), ) server_protos = declare_dependency( From 94c20a186372aace78b188842848b873eb3ebbd7 Mon Sep 17 00:00:00 2001 From: Vaxry Date: Mon, 13 May 2024 21:47:59 +0100 Subject: [PATCH 24/57] primary-selection: move to hyprland impl --- CMakeLists.txt | 1 + protocols/meson.build | 1 + src/config/ConfigManager.cpp | 1 + src/managers/ProtocolManager.cpp | 2 + src/managers/SeatManager.cpp | 26 ++- src/managers/SeatManager.hpp | 3 + src/protocols/DataDeviceWlr.cpp | 36 ++- src/protocols/DataDeviceWlr.hpp | 8 +- src/protocols/PrimarySelection.cpp | 338 +++++++++++++++++++++++++++++ src/protocols/PrimarySelection.hpp | 127 +++++++++++ 10 files changed, 529 insertions(+), 14 deletions(-) create mode 100644 src/protocols/PrimarySelection.cpp create mode 100644 src/protocols/PrimarySelection.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3cdae161..9897185f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -293,6 +293,7 @@ protocolNew("staging/ext-session-lock/ext-session-lock-v1.xml" "ext-session-lock protocolNew("stable/tablet/tablet-v2.xml" "tablet-v2" false) protocolNew("stable/presentation-time/presentation-time.xml" "presentation-time" false) protocolNew("stable/xdg-shell/xdg-shell.xml" "xdg-shell" false) +protocolNew("unstable/primary-selection/primary-selection-unstable-v1.xml" "primary-selection-unstable-v1" false) protocolWayland() diff --git a/protocols/meson.build b/protocols/meson.build index d583c466..6b0b4d18 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -62,6 +62,7 @@ new_protocols = [ [wl_protocol_dir, 'stable/tablet/tablet-v2.xml'], [wl_protocol_dir, 'stable/presentation-time/presentation-time.xml'], [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], + [wl_protocol_dir, 'unstable/primary-selection/primary-selection-unstable-v1.xml'], ] wl_protos_src = [] diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 15d5ae81..bec651b2 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -346,6 +346,7 @@ CConfigManager::CConfigManager() { m_pConfig->addConfigValue("misc:background_color", Hyprlang::INT{0xff111111}); m_pConfig->addConfigValue("misc:new_window_takes_over_fullscreen", Hyprlang::INT{0}); m_pConfig->addConfigValue("misc:initial_workspace_tracking", Hyprlang::INT{1}); + m_pConfig->addConfigValue("misc:middle_click_paste", Hyprlang::INT{1}); m_pConfig->addConfigValue("group:insert_after_current", Hyprlang::INT{1}); m_pConfig->addConfigValue("group:focus_removed_window", Hyprlang::INT{1}); diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp index 4b03263b..c43e4c56 100644 --- a/src/managers/ProtocolManager.cpp +++ b/src/managers/ProtocolManager.cpp @@ -30,6 +30,7 @@ #include "../protocols/PresentationTime.hpp" #include "../protocols/XDGShell.hpp" #include "../protocols/DataDeviceWlr.hpp" +#include "../protocols/PrimarySelection.hpp" #include "../protocols/core/Seat.hpp" #include "../protocols/core/DataDevice.hpp" @@ -71,6 +72,7 @@ CProtocolManager::CProtocolManager() { PROTO::presentation = std::make_unique(&wp_presentation_interface, 1, "Presentation"); PROTO::xdgShell = std::make_unique(&xdg_wm_base_interface, 6, "XDGShell"); PROTO::dataWlr = std::make_unique(&zwlr_data_control_manager_v1_interface, 2, "DataDeviceWlr"); + PROTO::primarySelection = std::make_unique(&zwp_primary_selection_device_manager_v1_interface, 1, "PrimarySelection"); // Old protocol implementations. // TODO: rewrite them to use hyprwayland-scanner. diff --git a/src/managers/SeatManager.cpp b/src/managers/SeatManager.cpp index a8505610..3e2d595f 100644 --- a/src/managers/SeatManager.cpp +++ b/src/managers/SeatManager.cpp @@ -2,6 +2,7 @@ #include "../protocols/core/Seat.hpp" #include "../protocols/core/DataDevice.hpp" #include "../protocols/DataDeviceWlr.hpp" +#include "../protocols/PrimarySelection.hpp" #include "../Compositor.hpp" #include "../devices/IKeyboard.hpp" #include @@ -447,7 +448,30 @@ void CSeatManager::setCurrentSelection(SP source) { if (source) { selection.destroySelection = source->events.destroy.registerListener([this](std::any d) { setCurrentSelection(nullptr); }); PROTO::data->setSelection(source); - PROTO::dataWlr->setSelection(source); + PROTO::dataWlr->setSelection(source, false); + } +} + +void CSeatManager::setCurrentPrimarySelection(SP source) { + if (source == selection.currentPrimarySelection) { + Debug::log(WARN, "[seat] duplicated setCurrentPrimarySelection?"); + return; + } + + selection.destroyPrimarySelection.reset(); + + if (selection.currentPrimarySelection) + selection.currentPrimarySelection->cancelled(); + + if (!source) + PROTO::primarySelection->setSelection(nullptr); + + selection.currentPrimarySelection = source; + + if (source) { + selection.destroyPrimarySelection = source->events.destroy.registerListener([this](std::any d) { setCurrentPrimarySelection(nullptr); }); + PROTO::primarySelection->setSelection(source); + PROTO::dataWlr->setSelection(source, true); } } diff --git a/src/managers/SeatManager.hpp b/src/managers/SeatManager.hpp index 81ca663d..f4efda70 100644 --- a/src/managers/SeatManager.hpp +++ b/src/managers/SeatManager.hpp @@ -109,9 +109,12 @@ class CSeatManager { struct { WP currentSelection; CHyprSignalListener destroySelection; + WP currentPrimarySelection; + CHyprSignalListener destroyPrimarySelection; } selection; void setCurrentSelection(SP source); + void setCurrentPrimarySelection(SP source); // do not write to directly, use set... WP mouse; diff --git a/src/protocols/DataDeviceWlr.cpp b/src/protocols/DataDeviceWlr.cpp index a518b0ae..c039d3b4 100644 --- a/src/protocols/DataDeviceWlr.cpp +++ b/src/protocols/DataDeviceWlr.cpp @@ -145,7 +145,7 @@ CWLRDataDevice::CWLRDataDevice(SP resource_) : resourc source->markUsed(); LOGM(LOG, "wlr manager requests primary selection to {:x}", (uintptr_t)source.get()); - g_pSeatManager->setCurrentSelection(source); + g_pSeatManager->setCurrentPrimarySelection(source); }); } @@ -170,6 +170,10 @@ void CWLRDataDevice::sendSelection(SP selection) { resource->sendSelection(selection->resource.get()); } +void CWLRDataDevice::sendPrimarySelection(SP selection) { + resource->sendPrimarySelection(selection->resource.get()); +} + CWLRDataControlManagerResource::CWLRDataControlManagerResource(SP resource_) : resource(resource_) { if (!good()) return; @@ -259,9 +263,14 @@ void CDataDeviceWLRProtocol::destroyResource(CWLRDataOffer* resource) { std::erase_if(m_vOffers, [&](const auto& other) { return other.get() == resource; }); } -void CDataDeviceWLRProtocol::sendSelectionToDevice(SP dev, SP sel) { - if (!sel) +void CDataDeviceWLRProtocol::sendSelectionToDevice(SP dev, SP sel, bool primary) { + if (!sel) { + if (primary) + dev->resource->sendPrimarySelectionRaw(nullptr); + else + dev->resource->sendSelectionRaw(nullptr); return; + } const auto OFFER = m_vOffers.emplace_back(makeShared(makeShared(dev->resource->client(), dev->resource->version(), 0), sel)); @@ -271,34 +280,41 @@ void CDataDeviceWLRProtocol::sendSelectionToDevice(SP dev, SPprimary = primary; + + LOGM(LOG, "New {}offer {:x} for data source {:x}", primary ? "primary " : " ", (uintptr_t)OFFER.get(), (uintptr_t)sel.get()); dev->sendDataOffer(OFFER); OFFER->sendData(); - dev->sendSelection(OFFER); + if (primary) + dev->sendPrimarySelection(OFFER); + else + dev->sendSelection(OFFER); } -void CDataDeviceWLRProtocol::setSelection(SP source) { +void CDataDeviceWLRProtocol::setSelection(SP source, bool primary) { for (auto& o : m_vOffers) { if (o->source && o->source->hasDnd()) continue; + if (o->primary != primary) + continue; o->dead = true; } if (!source) { - LOGM(LOG, "resetting selection"); + LOGM(LOG, "resetting {}selection", primary ? "primary " : " "); for (auto& d : m_vDevices) { - sendSelectionToDevice(d, nullptr); + sendSelectionToDevice(d, nullptr, primary); } return; } - LOGM(LOG, "New selection for data source {:x}", (uintptr_t)source.get()); + LOGM(LOG, "New {}selection for data source {:x}", primary ? "primary" : "", (uintptr_t)source.get()); for (auto& d : m_vDevices) { - sendSelectionToDevice(d, source); + sendSelectionToDevice(d, source, primary); } } diff --git a/src/protocols/DataDeviceWlr.hpp b/src/protocols/DataDeviceWlr.hpp index 0b703347..193e918c 100644 --- a/src/protocols/DataDeviceWlr.hpp +++ b/src/protocols/DataDeviceWlr.hpp @@ -19,7 +19,8 @@ class CWLRDataOffer { bool good(); void sendData(); - bool dead = false; + bool dead = false; + bool primary = false; WP source; @@ -61,6 +62,7 @@ class CWLRDataDevice { void sendDataOffer(SP offer); void sendSelection(SP selection); + void sendPrimarySelection(SP selection); WP self; @@ -103,8 +105,8 @@ class CDataDeviceWLRProtocol : public IWaylandProtocol { std::vector> m_vOffers; // - void setSelection(SP source); - void sendSelectionToDevice(SP dev, SP sel); + void setSelection(SP source, bool primary); + void sendSelectionToDevice(SP dev, SP sel, bool primary); // SP dataDeviceForClient(wl_client*); diff --git a/src/protocols/PrimarySelection.cpp b/src/protocols/PrimarySelection.cpp new file mode 100644 index 00000000..78eb8d63 --- /dev/null +++ b/src/protocols/PrimarySelection.cpp @@ -0,0 +1,338 @@ +#include "PrimarySelection.hpp" +#include +#include "../managers/SeatManager.hpp" +#include "core/Seat.hpp" +#include "../config/ConfigValue.hpp" + +#define LOGM PROTO::primarySelection->protoLog + +CPrimarySelectionOffer::CPrimarySelectionOffer(SP resource_, SP source_) : source(source_), resource(resource_) { + if (!good()) + return; + + resource->setDestroy([this](CZwpPrimarySelectionOfferV1* r) { PROTO::primarySelection->destroyResource(this); }); + resource->setOnDestroy([this](CZwpPrimarySelectionOfferV1* r) { PROTO::primarySelection->destroyResource(this); }); + + resource->setReceive([this](CZwpPrimarySelectionOfferV1* r, const char* mime, int32_t fd) { + if (!source) { + LOGM(WARN, "Possible bug: Receive on an offer w/o a source"); + close(fd); + return; + } + + if (dead) { + LOGM(WARN, "Possible bug: Receive on an offer that's dead"); + close(fd); + return; + } + + LOGM(LOG, "Offer {:x} asks to send data from source {:x}", (uintptr_t)this, (uintptr_t)source.get()); + + source->send(mime, fd); + }); +} + +bool CPrimarySelectionOffer::good() { + return resource->resource(); +} + +void CPrimarySelectionOffer::sendData() { + if (!source) + return; + + for (auto& m : source->mimes()) { + resource->sendOffer(m.c_str()); + } +} + +CPrimarySelectionSource::CPrimarySelectionSource(SP resource_, SP device_) : device(device_), resource(resource_) { + if (!good()) + return; + + resource->setData(this); + + resource->setDestroy([this](CZwpPrimarySelectionSourceV1* r) { + events.destroy.emit(); + PROTO::primarySelection->destroyResource(this); + }); + resource->setOnDestroy([this](CZwpPrimarySelectionSourceV1* r) { + events.destroy.emit(); + PROTO::primarySelection->destroyResource(this); + }); + + resource->setOffer([this](CZwpPrimarySelectionSourceV1* r, const char* mime) { mimeTypes.push_back(mime); }); +} + +CPrimarySelectionSource::~CPrimarySelectionSource() { + events.destroy.emit(); +} + +SP CPrimarySelectionSource::fromResource(wl_resource* res) { + auto data = (CPrimarySelectionSource*)(((CZwpPrimarySelectionSourceV1*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; +} + +bool CPrimarySelectionSource::good() { + return resource->resource(); +} + +std::vector CPrimarySelectionSource::mimes() { + return mimeTypes; +} + +void CPrimarySelectionSource::send(const std::string& mime, uint32_t fd) { + if (std::find(mimeTypes.begin(), mimeTypes.end(), mime) == mimeTypes.end()) { + LOGM(ERR, "Compositor/App bug: CPrimarySelectionSource::sendAskSend with non-existent mime"); + close(fd); + return; + } + + resource->sendSend(mime.c_str(), fd); + close(fd); +} + +void CPrimarySelectionSource::accepted(const std::string& mime) { + if (std::find(mimeTypes.begin(), mimeTypes.end(), mime) == mimeTypes.end()) + LOGM(ERR, "Compositor/App bug: CPrimarySelectionSource::sendAccepted with non-existent mime"); + + // primary sel has no accepted +} + +void CPrimarySelectionSource::cancelled() { + resource->sendCancelled(); +} + +void CPrimarySelectionSource::error(uint32_t code, const std::string& msg) { + resource->error(code, msg); +} + +CPrimarySelectionDevice::CPrimarySelectionDevice(SP resource_) : resource(resource_) { + if (!good()) + return; + + pClient = resource->client(); + + resource->setDestroy([this](CZwpPrimarySelectionDeviceV1* r) { PROTO::primarySelection->destroyResource(this); }); + resource->setOnDestroy([this](CZwpPrimarySelectionDeviceV1* r) { PROTO::primarySelection->destroyResource(this); }); + + resource->setSetSelection([this](CZwpPrimarySelectionDeviceV1* r, wl_resource* sourceR, uint32_t serial) { + static auto PPRIMARYSEL = CConfigValue("misc:middle_click_paste"); + + if (!*PPRIMARYSEL) { + LOGM(LOG, "Ignoring primary selection: disabled in config"); + g_pSeatManager->setCurrentPrimarySelection(nullptr); + return; + } + + auto source = sourceR ? CPrimarySelectionSource::fromResource(sourceR) : CSharedPointer{}; + if (!source) { + LOGM(LOG, "wlr reset selection received"); + g_pSeatManager->setCurrentPrimarySelection(nullptr); + return; + } + + if (source && source->used()) + LOGM(WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this."); + + source->markUsed(); + + LOGM(LOG, "wlr manager requests selection to {:x}", (uintptr_t)source.get()); + g_pSeatManager->setCurrentPrimarySelection(source); + }); +} + +bool CPrimarySelectionDevice::good() { + return resource->resource(); +} + +wl_client* CPrimarySelectionDevice::client() { + return pClient; +} + +void CPrimarySelectionDevice::sendDataOffer(SP offer) { + resource->sendDataOffer(offer->resource.get()); +} + +void CPrimarySelectionDevice::sendSelection(SP selection) { + if (!selection) + resource->sendSelectionRaw(nullptr); + else + resource->sendSelection(selection->resource.get()); +} + +CPrimarySelectionManager::CPrimarySelectionManager(SP resource_) : resource(resource_) { + if (!good()) + return; + + resource->setOnDestroy([this](CZwpPrimarySelectionDeviceManagerV1* r) { PROTO::primarySelection->destroyResource(this); }); + + resource->setGetDevice([this](CZwpPrimarySelectionDeviceManagerV1* r, uint32_t id, wl_resource* seat) { + const auto RESOURCE = + PROTO::primarySelection->m_vDevices.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::primarySelection->m_vDevices.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + device = RESOURCE; + + for (auto& s : sources) { + if (!s) + continue; + s->device = RESOURCE; + } + + LOGM(LOG, "New primary selection data device bound at {:x}", (uintptr_t)RESOURCE.get()); + }); + + resource->setCreateSource([this](CZwpPrimarySelectionDeviceManagerV1* r, uint32_t id) { + std::erase_if(sources, [](const auto& e) { return e.expired(); }); + + const auto RESOURCE = PROTO::primarySelection->m_vSources.emplace_back( + makeShared(makeShared(r->client(), r->version(), id), device.lock())); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::primarySelection->m_vSources.pop_back(); + return; + } + + if (!device) + LOGM(WARN, "New data source before a device was created"); + + RESOURCE->self = RESOURCE; + + sources.push_back(RESOURCE); + + LOGM(LOG, "New primary selection data source bound at {:x}", (uintptr_t)RESOURCE.get()); + }); +} + +bool CPrimarySelectionManager::good() { + return resource->resource(); +} + +CPrimarySelectionProtocol::CPrimarySelectionProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + ; +} + +void CPrimarySelectionProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); + + if (!RESOURCE->good()) { + wl_client_post_no_memory(client); + m_vManagers.pop_back(); + return; + } + + LOGM(LOG, "New primary_seletion_manager at {:x}", (uintptr_t)RESOURCE.get()); + + // we need to do it here because protocols come before seatMgr + if (!listeners.onPointerFocusChange) + listeners.onPointerFocusChange = g_pSeatManager->events.pointerFocusChange.registerListener([this](std::any d) { this->onPointerFocus(); }); +} + +void CPrimarySelectionProtocol::destroyResource(CPrimarySelectionManager* resource) { + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); +} + +void CPrimarySelectionProtocol::destroyResource(CPrimarySelectionSource* resource) { + std::erase_if(m_vSources, [&](const auto& other) { return other.get() == resource; }); +} + +void CPrimarySelectionProtocol::destroyResource(CPrimarySelectionDevice* resource) { + std::erase_if(m_vDevices, [&](const auto& other) { return other.get() == resource; }); +} + +void CPrimarySelectionProtocol::destroyResource(CPrimarySelectionOffer* resource) { + std::erase_if(m_vOffers, [&](const auto& other) { return other.get() == resource; }); +} + +void CPrimarySelectionProtocol::sendSelectionToDevice(SP dev, SP sel) { + if (!sel) { + dev->sendSelection(nullptr); + return; + } + + const auto OFFER = + m_vOffers.emplace_back(makeShared(makeShared(dev->resource->client(), dev->resource->version(), 0), sel)); + + if (!OFFER->good()) { + dev->resource->noMemory(); + m_vOffers.pop_back(); + return; + } + + LOGM(LOG, "New offer {:x} for data source {:x}", (uintptr_t)OFFER.get(), (uintptr_t)sel.get()); + + dev->sendDataOffer(OFFER); + OFFER->sendData(); + dev->sendSelection(OFFER); +} + +void CPrimarySelectionProtocol::setSelection(SP source) { + for (auto& o : m_vOffers) { + if (o->source && o->source->hasDnd()) + continue; + o->dead = true; + } + + if (!source) { + LOGM(LOG, "resetting selection"); + + if (!g_pSeatManager->state.pointerFocusResource) + return; + + auto DESTDEVICE = dataDeviceForClient(g_pSeatManager->state.pointerFocusResource->client()); + if (DESTDEVICE) + sendSelectionToDevice(DESTDEVICE, nullptr); + + return; + } + + LOGM(LOG, "New selection for data source {:x}", (uintptr_t)source.get()); + + if (!g_pSeatManager->state.pointerFocusResource) + return; + + auto DESTDEVICE = dataDeviceForClient(g_pSeatManager->state.pointerFocusResource->client()); + + if (!DESTDEVICE) { + LOGM(LOG, "CWLDataDeviceProtocol::setSelection: cannot send selection to a client without a data_device"); + return; + } + + sendSelectionToDevice(DESTDEVICE, source); +} + +void CPrimarySelectionProtocol::updateSelection() { + if (!g_pSeatManager->state.pointerFocusResource) + return; + + auto DESTDEVICE = dataDeviceForClient(g_pSeatManager->state.pointerFocusResource->client()); + + if (!DESTDEVICE) { + LOGM(LOG, "CPrimarySelectionProtocol::updateSelection: cannot send selection to a client without a data_device"); + return; + } + + sendSelectionToDevice(DESTDEVICE, g_pSeatManager->selection.currentPrimarySelection.lock()); +} + +void CPrimarySelectionProtocol::onPointerFocus() { + for (auto& o : m_vOffers) { + o->dead = true; + } + + updateSelection(); +} + +SP CPrimarySelectionProtocol::dataDeviceForClient(wl_client* c) { + auto it = std::find_if(m_vDevices.begin(), m_vDevices.end(), [c](const auto& e) { return e->client() == c; }); + if (it == m_vDevices.end()) + return nullptr; + return *it; +} diff --git a/src/protocols/PrimarySelection.hpp b/src/protocols/PrimarySelection.hpp new file mode 100644 index 00000000..c33a00e8 --- /dev/null +++ b/src/protocols/PrimarySelection.hpp @@ -0,0 +1,127 @@ +#pragma once + +#include +#include +#include +#include "WaylandProtocol.hpp" +#include "primary-selection-unstable-v1.hpp" +#include "types/DataDevice.hpp" + +class CPrimarySelectionOffer; +class CPrimarySelectionSource; +class CPrimarySelectionDevice; +class CPrimarySelectionManager; + +class CPrimarySelectionOffer { + public: + CPrimarySelectionOffer(SP resource_, SP source_); + + bool good(); + void sendData(); + + bool dead = false; + + WP source; + + private: + SP resource; + + friend class CPrimarySelectionDevice; +}; + +class CPrimarySelectionSource : public IDataSource { + public: + CPrimarySelectionSource(SP resource_, SP device_); + ~CPrimarySelectionSource(); + + static SP fromResource(wl_resource*); + + bool good(); + + virtual std::vector mimes(); + virtual void send(const std::string& mime, uint32_t fd); + virtual void accepted(const std::string& mime); + virtual void cancelled(); + virtual void error(uint32_t code, const std::string& msg); + + std::vector mimeTypes; + WP self; + WP device; + + private: + SP resource; +}; + +class CPrimarySelectionDevice { + public: + CPrimarySelectionDevice(SP resource_); + + bool good(); + wl_client* client(); + + void sendDataOffer(SP offer); + void sendSelection(SP selection); + + WP self; + + private: + SP resource; + wl_client* pClient = nullptr; + + friend class CPrimarySelectionProtocol; +}; + +class CPrimarySelectionManager { + public: + CPrimarySelectionManager(SP resource_); + + bool good(); + + WP device; + std::vector> sources; + + private: + SP resource; +}; + +class CPrimarySelectionProtocol : public IWaylandProtocol { + public: + CPrimarySelectionProtocol(const wl_interface* iface, const int& ver, const std::string& name); + + virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); + + private: + void destroyResource(CPrimarySelectionManager* resource); + void destroyResource(CPrimarySelectionDevice* resource); + void destroyResource(CPrimarySelectionSource* resource); + void destroyResource(CPrimarySelectionOffer* resource); + + // + std::vector> m_vManagers; + std::vector> m_vDevices; + std::vector> m_vSources; + std::vector> m_vOffers; + + // + void setSelection(SP source); + void sendSelectionToDevice(SP dev, SP sel); + void updateSelection(); + void onPointerFocus(); + + // + SP dataDeviceForClient(wl_client*); + + friend class CPrimarySelectionManager; + friend class CPrimarySelectionDevice; + friend class CPrimarySelectionSource; + friend class CPrimarySelectionOffer; + friend class CSeatManager; + + struct { + CHyprSignalListener onPointerFocusChange; + } listeners; +}; + +namespace PROTO { + inline UP primarySelection; +}; From 31890026ea9f3ce75dbcfdf060239fc8aa6c144c Mon Sep 17 00:00:00 2001 From: Sungyoon Cho Date: Wed, 15 May 2024 20:17:56 +0900 Subject: [PATCH 25/57] wl_seat: send frame event after pointer leave (#6074) --- src/managers/SeatManager.cpp | 17 ++++++++++++++++- src/managers/SeatManager.hpp | 1 + 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/managers/SeatManager.cpp b/src/managers/SeatManager.cpp index 3e2d595f..b9590c42 100644 --- a/src/managers/SeatManager.cpp +++ b/src/managers/SeatManager.cpp @@ -186,10 +186,13 @@ void CSeatManager::setPointerFocus(wlr_surface* surf, const Vector2D& local) { } } + auto lastPointerFocusResource = state.pointerFocusResource; + state.pointerFocusResource.reset(); state.pointerFocus = surf; if (!surf) { + sendPointerFrame(lastPointerFocusResource); events.pointerFocusChange.emit(); return; } @@ -209,6 +212,11 @@ void CSeatManager::setPointerFocus(wlr_surface* surf, const Vector2D& local) { } } + if (state.pointerFocusResource != lastPointerFocusResource) + sendPointerFrame(lastPointerFocusResource); + + sendPointerFrame(); + hyprListener_pointerSurfaceDestroy.initCallback( &surf->events.destroy, [this](void* owner, void* data) { setPointerFocus(nullptr, {}); }, nullptr, "CSeatManager"); @@ -245,7 +253,14 @@ void CSeatManager::sendPointerFrame() { if (!state.pointerFocusResource) return; - for (auto& p : state.pointerFocusResource->pointers) { + sendPointerFrame(state.pointerFocusResource); +} + +void CSeatManager::sendPointerFrame(WP pResource) { + if (!pResource) + return; + + for (auto& p : pResource->pointers) { if (!p) continue; diff --git a/src/managers/SeatManager.hpp b/src/managers/SeatManager.hpp index f4efda70..cf5e9216 100644 --- a/src/managers/SeatManager.hpp +++ b/src/managers/SeatManager.hpp @@ -63,6 +63,7 @@ class CSeatManager { void sendPointerMotion(uint32_t timeMs, const Vector2D& local); void sendPointerButton(uint32_t timeMs, uint32_t key, wl_pointer_button_state state); void sendPointerFrame(); + void sendPointerFrame(WP pResource); void sendPointerAxis(uint32_t timeMs, wl_pointer_axis axis, double value, int32_t discrete, wl_pointer_axis_source source, wl_pointer_axis_relative_direction relative); void sendTouchDown(wlr_surface* surf, uint32_t timeMs, int32_t id, const Vector2D& local); From 7fbe05a250fb81b4a9cd531460b72b038f37e88f Mon Sep 17 00:00:00 2001 From: Vaxry Date: Wed, 15 May 2024 16:22:45 +0100 Subject: [PATCH 26/57] inputmgr: send pointer motion on ffm != 1 fixes #6077 --- src/managers/input/InputManager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index d74657d9..02072229 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -471,6 +471,9 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { if (FOLLOWMOUSE != 0 || pFoundWindow == g_pCompositor->m_pLastWindow) g_pSeatManager->setPointerFocus(foundSurface, surfaceLocal); + if (g_pSeatManager->state.pointerFocus == foundSurface) + g_pSeatManager->sendPointerMotion(time, surfaceLocal); + m_bLastFocusOnLS = false; return; // don't enter any new surfaces } else { From 3381e2b55b9eaf8d30344c301aacfc664519688b Mon Sep 17 00:00:00 2001 From: Vaxry Date: Wed, 15 May 2024 16:25:56 +0100 Subject: [PATCH 27/57] datadevice: guard surface in dnd for null fixes #6076 --- src/protocols/core/DataDevice.cpp | 34 ++++++++++++++++--------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/protocols/core/DataDevice.cpp b/src/protocols/core/DataDevice.cpp index 2b9503b1..354259fe 100644 --- a/src/protocols/core/DataDevice.cpp +++ b/src/protocols/core/DataDevice.cpp @@ -233,7 +233,7 @@ CWLDataDeviceResource::CWLDataDeviceResource(SP resource_) : reso source->dnd = true; - PROTO::data->initiateDrag(source, wlr_surface_from_resource(icon), wlr_surface_from_resource(origin)); + PROTO::data->initiateDrag(source, icon ? wlr_surface_from_resource(icon) : nullptr, wlr_surface_from_resource(origin)); }); } @@ -471,22 +471,24 @@ void CWLDataDeviceProtocol::initiateDrag(WP currentSource dnd.currentSource = currentSource; dnd.originSurface = origin; dnd.dndSurface = dragSurface; - dnd.hyprListener_dndSurfaceDestroy.initCallback( - &dragSurface->events.destroy, [this](void* owner, void* data) { abortDrag(); }, nullptr, "CWLDataDeviceProtocol::drag"); - dnd.hyprListener_dndSurfaceCommit.initCallback( - &dragSurface->events.commit, - [this](void* owner, void* data) { - if (dnd.dndSurface->pending.buffer_width > 0 && dnd.dndSurface->pending.buffer_height > 0 && !dnd.dndSurface->mapped) { - wlr_surface_map(dnd.dndSurface); - return; - } + if (dragSurface) { + dnd.hyprListener_dndSurfaceDestroy.initCallback( + &dragSurface->events.destroy, [this](void* owner, void* data) { abortDrag(); }, nullptr, "CWLDataDeviceProtocol::drag"); + dnd.hyprListener_dndSurfaceCommit.initCallback( + &dragSurface->events.commit, + [this](void* owner, void* data) { + if (dnd.dndSurface->pending.buffer_width > 0 && dnd.dndSurface->pending.buffer_height > 0 && !dnd.dndSurface->mapped) { + wlr_surface_map(dnd.dndSurface); + return; + } - if (dnd.dndSurface->pending.buffer_width <= 0 && dnd.dndSurface->pending.buffer_height <= 0 && dnd.dndSurface->mapped) { - wlr_surface_unmap(dnd.dndSurface); - return; - } - }, - nullptr, "CWLDataDeviceProtocol::drag"); + if (dnd.dndSurface->pending.buffer_width <= 0 && dnd.dndSurface->pending.buffer_height <= 0 && dnd.dndSurface->mapped) { + wlr_surface_unmap(dnd.dndSurface); + return; + } + }, + nullptr, "CWLDataDeviceProtocol::drag"); + } dnd.mouseButton = g_pHookSystem->hookDynamic("mouseButton", [this](void* self, SCallbackInfo& info, std::any e) { auto E = std::any_cast(e); From 3fe5280ce93a7c216f47e8626e69ea015e875395 Mon Sep 17 00:00:00 2001 From: Raphael Tannous <107765121+rofe33@users.noreply.github.com> Date: Wed, 15 May 2024 18:54:23 +0300 Subject: [PATCH 28/57] hyprctl: return exitStatus in requestHyprpaper() and request() (#6083) --- hyprctl/main.cpp | 58 ++++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/hyprctl/main.cpp b/hyprctl/main.cpp index 097ef5d8..e9d7583c 100644 --- a/hyprctl/main.cpp +++ b/hyprctl/main.cpp @@ -90,24 +90,24 @@ std::vector instances() { return result; } -void request(std::string arg, int minArgs = 0) { +int request(std::string arg, int minArgs = 0) { const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0); const auto ARGS = std::count(arg.begin(), arg.end(), ' '); if (ARGS < minArgs) { std::cout << "Not enough arguments, expected at least " << minArgs; - return; + return -1; } if (SERVERSOCKET < 0) { std::cout << "Couldn't open a socket (1)"; - return; + return 1; } if (instanceSignature.empty()) { std::cout << "HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)"; - return; + return 2; } const std::string USERID = std::to_string(getpwuid(getuid())->pw_uid); @@ -121,14 +121,14 @@ void request(std::string arg, int minArgs = 0) { if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, SUN_LEN(&serverAddress)) < 0) { std::cout << "Couldn't connect to " << socketPath << ". (3)"; - return; + return 3; } auto sizeWritten = write(SERVERSOCKET, arg.c_str(), arg.length()); if (sizeWritten < 0) { std::cout << "Couldn't write (4)"; - return; + return 4; } std::string reply = ""; @@ -138,7 +138,7 @@ void request(std::string arg, int minArgs = 0) { if (sizeWritten < 0) { std::cout << "Couldn't read (5)"; - return; + return 5; } reply += std::string(buffer, sizeWritten); @@ -147,7 +147,7 @@ void request(std::string arg, int minArgs = 0) { sizeWritten = read(SERVERSOCKET, buffer, 8192); if (sizeWritten < 0) { std::cout << "Couldn't read (5)"; - return; + return 5; } reply += std::string(buffer, sizeWritten); } @@ -155,19 +155,21 @@ void request(std::string arg, int minArgs = 0) { close(SERVERSOCKET); std::cout << reply; + + return 0; } -void requestHyprpaper(std::string arg) { +int requestHyprpaper(std::string arg) { const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0); if (SERVERSOCKET < 0) { std::cout << "Couldn't open a socket (1)"; - return; + return 1; } if (instanceSignature.empty()) { std::cout << "HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)"; - return; + return 2; } sockaddr_un serverAddress = {0}; @@ -181,7 +183,7 @@ void requestHyprpaper(std::string arg) { if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, SUN_LEN(&serverAddress)) < 0) { std::cout << "Couldn't connect to " << socketPath << ". (3)"; - return; + return 3; } arg = arg.substr(arg.find_first_of('/') + 1); // strip flags @@ -191,7 +193,7 @@ void requestHyprpaper(std::string arg) { if (sizeWritten < 0) { std::cout << "Couldn't write (4)"; - return; + return 4; } char buffer[8192] = {0}; @@ -200,12 +202,14 @@ void requestHyprpaper(std::string arg) { if (sizeWritten < 0) { std::cout << "Couldn't read (5)"; - return; + return 5; } close(SERVERSOCKET); std::cout << std::string(buffer); + + return 0; } void batchRequest(std::string arg, bool json) { @@ -384,33 +388,33 @@ int main(int argc, char** argv) { if (fullRequest.contains("/--batch")) batchRequest(fullRequest, json); else if (fullRequest.contains("/hyprpaper")) - requestHyprpaper(fullRequest); + exitStatus = requestHyprpaper(fullRequest); else if (fullRequest.contains("/switchxkblayout")) - request(fullRequest, 2); + exitStatus = request(fullRequest, 2); else if (fullRequest.contains("/seterror")) - request(fullRequest, 1); + exitStatus = request(fullRequest, 1); else if (fullRequest.contains("/setprop")) - request(fullRequest, 3); + exitStatus = request(fullRequest, 3); else if (fullRequest.contains("/plugin")) - request(fullRequest, 1); + exitStatus = request(fullRequest, 1); else if (fullRequest.contains("/dismissnotify")) - request(fullRequest, 0); + exitStatus = request(fullRequest, 0); else if (fullRequest.contains("/notify")) - request(fullRequest, 2); + exitStatus = request(fullRequest, 2); else if (fullRequest.contains("/output")) - request(fullRequest, 2); + exitStatus = request(fullRequest, 2); else if (fullRequest.contains("/setcursor")) - request(fullRequest, 1); + exitStatus = request(fullRequest, 1); else if (fullRequest.contains("/dispatch")) - request(fullRequest, 1); + exitStatus = request(fullRequest, 1); else if (fullRequest.contains("/keyword")) - request(fullRequest, 2); + exitStatus = request(fullRequest, 2); else if (fullRequest.contains("/decorations")) - request(fullRequest, 1); + exitStatus = request(fullRequest, 1); else if (fullRequest.contains("/--help")) std::cout << USAGE << std::endl; else { - request(fullRequest); + exitStatus = request(fullRequest); } std::cout << std::endl; From b9c58b6e75f7d0141d8ba88fb3f6f022fed0877a Mon Sep 17 00:00:00 2001 From: Vaxry Date: Wed, 15 May 2024 17:38:02 +0100 Subject: [PATCH 29/57] seat: send enter/leave events to all bound wl_seats for a client fixes #6069 Will not send anything beyond enter/leave. If you depend on multiple seats sending you motion, button, etc, events, fix your app. --- src/managers/SeatManager.cpp | 96 ++++++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 37 deletions(-) diff --git a/src/managers/SeatManager.cpp b/src/managers/SeatManager.cpp index b9590c42..360b5088 100644 --- a/src/managers/SeatManager.cpp +++ b/src/managers/SeatManager.cpp @@ -6,6 +6,7 @@ #include "../Compositor.hpp" #include "../devices/IKeyboard.hpp" #include +#include CSeatManager::CSeatManager() { listeners.newSeatResource = PROTO::seat->events.newSeatResource.registerListener([this](std::any res) { onNewSeatResource(std::any_cast>(res)); }); @@ -104,11 +105,23 @@ void CSeatManager::setKeyboardFocus(wlr_surface* surf) { hyprListener_keyboardSurfaceDestroy.removeCallback(); if (state.keyboardFocusResource) { - for (auto& k : state.keyboardFocusResource->keyboards) { - if (!k) + // we will iterate over all bound wl_seat + // resources here, because some idiotic apps (e.g. those based on smithay) + // tend to bind wl_seat twice. + // I can't be arsed to actually pass all events to all seat resources, so we will + // only pass enter and leave. + // If you have an issue with that, fix your app. + auto client = state.keyboardFocusResource->client(); + for (auto& s : seatResources) { + if (s->resource->client() != client) continue; - k->sendLeave(); + for (auto& k : s->resource->keyboards) { + if (!k) + continue; + + k->sendLeave(); + } } } @@ -121,18 +134,17 @@ void CSeatManager::setKeyboardFocus(wlr_surface* surf) { } auto client = wl_resource_get_client(surf->resource); - for (auto& r : seatResources) { - if (r->resource->client() == client) { - state.keyboardFocusResource = r->resource; - for (auto& k : state.keyboardFocusResource->keyboards) { - if (!k) - continue; + for (auto& r : seatResources | std::views::reverse) { + if (r->resource->client() != client) + continue; - k->sendEnter(surf); - k->sendMods(keyboard->wlr()->modifiers.depressed, keyboard->wlr()->modifiers.latched, keyboard->wlr()->modifiers.locked, keyboard->wlr()->modifiers.group); - } + state.keyboardFocusResource = r->resource; + for (auto& k : r->resource->keyboards) { + if (!k) + continue; - break; + k->sendEnter(surf); + k->sendMods(keyboard->wlr()->modifiers.depressed, keyboard->wlr()->modifiers.latched, keyboard->wlr()->modifiers.locked, keyboard->wlr()->modifiers.group); } } @@ -178,11 +190,17 @@ void CSeatManager::setPointerFocus(wlr_surface* surf, const Vector2D& local) { hyprListener_pointerSurfaceDestroy.removeCallback(); if (state.pointerFocusResource) { - for (auto& p : state.pointerFocusResource->pointers) { - if (!p) + auto client = state.pointerFocusResource->client(); + for (auto& s : seatResources) { + if (s->resource->client() != client) continue; - p->sendLeave(); + for (auto& p : s->resource->pointers) { + if (!p) + continue; + + p->sendLeave(); + } } } @@ -198,17 +216,16 @@ void CSeatManager::setPointerFocus(wlr_surface* surf, const Vector2D& local) { } auto client = wl_resource_get_client(surf->resource); - for (auto& r : seatResources) { - if (r->resource->client() == client) { - state.pointerFocusResource = r->resource; - for (auto& p : state.pointerFocusResource->pointers) { - if (!p) - continue; + for (auto& r : seatResources | std::views::reverse) { + if (r->resource->client() != client) + continue; - p->sendEnter(surf, local); - } + state.pointerFocusResource = r->resource; + for (auto& p : r->resource->pointers) { + if (!p) + continue; - break; + p->sendEnter(surf, local); } } @@ -290,11 +307,17 @@ void CSeatManager::sendTouchDown(wlr_surface* surf, uint32_t timeMs, int32_t id, hyprListener_touchSurfaceDestroy.removeCallback(); if (state.touchFocusResource) { - for (auto& t : state.touchFocusResource->touches) { - if (!t) + auto client = state.touchFocusResource->client(); + for (auto& s : seatResources) { + if (s->resource->client() != client) continue; - t->sendUp(timeMs, id); + for (auto& t : s->resource->touches) { + if (!t) + continue; + + t->sendUp(timeMs, id); + } } } @@ -307,17 +330,16 @@ void CSeatManager::sendTouchDown(wlr_surface* surf, uint32_t timeMs, int32_t id, } auto client = wl_resource_get_client(surf->resource); - for (auto& r : seatResources) { - if (r->resource->client() == client) { - state.touchFocusResource = r->resource; - for (auto& t : state.touchFocusResource->touches) { - if (!t) - continue; + for (auto& r : seatResources | std::views::reverse) { + if (r->resource->client() != client) + continue; - t->sendDown(surf, timeMs, id, local); - } + state.touchFocusResource = r->resource; + for (auto& t : r->resource->touches) { + if (!t) + continue; - break; + t->sendDown(surf, timeMs, id, local); } } From a8522db683ae260f16f8e7778561d8a54906beb1 Mon Sep 17 00:00:00 2001 From: JManch <61563764+JManch@users.noreply.github.com> Date: Wed, 15 May 2024 21:03:51 +0100 Subject: [PATCH 30/57] keybinds: fix empty on monitor for new workspaces (#6089) --- src/helpers/MiscFunctions.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/helpers/MiscFunctions.cpp b/src/helpers/MiscFunctions.cpp index 4855553a..7382d24c 100644 --- a/src/helpers/MiscFunctions.cpp +++ b/src/helpers/MiscFunctions.cpp @@ -284,16 +284,24 @@ int getWorkspaceIDFromString(const std::string& in, std::string& outName) { } else if (in.starts_with("empty")) { const bool same_mon = in.substr(5).contains("m"); const bool next = in.substr(5).contains("n"); - if (same_mon || next) { - if (!g_pCompositor->m_pLastMonitor) { - Debug::log(ERR, "Empty monitor workspace on monitor null!"); - return WORKSPACE_INVALID; + if ((same_mon || next) && !g_pCompositor->m_pLastMonitor) { + Debug::log(ERR, "Empty monitor workspace on monitor null!"); + return WORKSPACE_INVALID; + } + + std::set invalidWSes; + if (same_mon) { + for (auto& rule : g_pConfigManager->getAllWorkspaceRules()) { + const auto PMONITOR = g_pCompositor->getMonitorFromName(rule.monitor); + if (PMONITOR && (PMONITOR->ID != g_pCompositor->m_pLastMonitor->ID)) + invalidWSes.insert(rule.workspaceId); } } + int id = next ? g_pCompositor->m_pLastMonitor->activeWorkspaceID() : 0; while (++id < INT_MAX) { const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(id); - if (!PWORKSPACE || (g_pCompositor->getWindowsOnWorkspace(id) == 0 && (!same_mon || PWORKSPACE->m_iMonitorID == g_pCompositor->m_pLastMonitor->ID))) + if (!invalidWSes.contains(id) && (!PWORKSPACE || g_pCompositor->getWindowsOnWorkspace(id) == 0)) return id; } } else if (in.starts_with("prev")) { From 9eec4cb670f815c794a56005fd77020d34b6899d Mon Sep 17 00:00:00 2001 From: Vaxry Date: Wed, 15 May 2024 23:01:48 +0100 Subject: [PATCH 31/57] sysd: add missing header ref #6094 --- src/helpers/SdDaemon.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helpers/SdDaemon.cpp b/src/helpers/SdDaemon.cpp index d5df3121..497101e4 100644 --- a/src/helpers/SdDaemon.cpp +++ b/src/helpers/SdDaemon.cpp @@ -7,6 +7,7 @@ #include #include #include +#include namespace Systemd { int SdBooted(void) { From 7e8c0b7f30817e0fc217ef769d4be7d406fa2374 Mon Sep 17 00:00:00 2001 From: Vaxry Date: Wed, 15 May 2024 23:13:45 +0100 Subject: [PATCH 32/57] seat: send axis_stop events after axis events fixes #6090 --- src/managers/SeatManager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/managers/SeatManager.cpp b/src/managers/SeatManager.cpp index 360b5088..2c7bfe39 100644 --- a/src/managers/SeatManager.cpp +++ b/src/managers/SeatManager.cpp @@ -297,6 +297,9 @@ void CSeatManager::sendPointerAxis(uint32_t timeMs, wl_pointer_axis axis, double p->sendAxis(timeMs, axis, value); p->sendAxisSource(source); p->sendAxisRelativeDirection(axis, relative); + + if (value == 0) + p->sendAxisStop(timeMs, axis); } } From de9798fcf9494eb082bd168175390c0d47b8478b Mon Sep 17 00:00:00 2001 From: Vaxry Date: Thu, 16 May 2024 00:55:55 +0100 Subject: [PATCH 33/57] configmgr: shadow exec rules when window is unmapped fixes #6091 --- src/config/ConfigManager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index bec651b2..9d8a538c 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -1041,6 +1041,9 @@ std::vector CConfigManager::getMatchingRules(PHLWINDOW pWindow, boo if (!valid(pWindow)) return std::vector(); + // if the window is unmapped, don't process exec rules yet. + shadowExec = shadowExec || !pWindow->m_bIsMapped; + std::vector returns; std::string title = pWindow->m_szTitle; From ca0833c9ed6d6034b360d0ea83f3ac942ffb45b5 Mon Sep 17 00:00:00 2001 From: zakk4223 Date: Thu, 16 May 2024 06:38:10 -0400 Subject: [PATCH 34/57] decoration: Stacked group tabs (#5886) * Stacked group tabs * Fix index when creating groupbar title textures * Changes for stacked dnd * formatting * Don't remove internal horizontal padding when calculating stacked bar width --- src/config/ConfigManager.cpp | 1 + .../decorations/CHyprGroupBarDecoration.cpp | 86 +++++++++++++------ .../decorations/CHyprGroupBarDecoration.hpp | 1 + 3 files changed, 60 insertions(+), 28 deletions(-) diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 9d8a538c..b0c44408 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -359,6 +359,7 @@ CConfigManager::CConfigManager() { m_pConfig->addConfigValue("group:groupbar:render_titles", Hyprlang::INT{1}); m_pConfig->addConfigValue("group:groupbar:scrolling", Hyprlang::INT{1}); m_pConfig->addConfigValue("group:groupbar:text_color", Hyprlang::INT{0xffffffff}); + m_pConfig->addConfigValue("group:groupbar:stacked", Hyprlang::INT{0}); m_pConfig->addConfigValue("debug:int", Hyprlang::INT{0}); m_pConfig->addConfigValue("debug:log_damage", Hyprlang::INT{0}); diff --git a/src/render/decorations/CHyprGroupBarDecoration.cpp b/src/render/decorations/CHyprGroupBarDecoration.cpp index 451701b4..318999ed 100644 --- a/src/render/decorations/CHyprGroupBarDecoration.cpp +++ b/src/render/decorations/CHyprGroupBarDecoration.cpp @@ -1,6 +1,7 @@ #include "CHyprGroupBarDecoration.hpp" #include "../../Compositor.hpp" #include "../../config/ConfigValue.hpp" +#include "managers/LayoutManager.hpp" #include #include @@ -12,6 +13,7 @@ static CTexture m_tGradientLockedInactive; constexpr int BAR_INDICATOR_HEIGHT = 3; constexpr int BAR_PADDING_OUTER_VERT = 2; +constexpr int BAR_PADDING_OUTER_HORZ = 2; constexpr int BAR_TEXT_PAD = 2; constexpr int BAR_HORIZONTAL_PADDING = 2; @@ -32,6 +34,7 @@ SDecorationPositioningInfo CHyprGroupBarDecoration::getPositioningInfo() { static auto PRENDERTITLES = CConfigValue("group:groupbar:render_titles"); static auto PGRADIENTS = CConfigValue("group:groupbar:gradients"); static auto PPRIORITY = CConfigValue("group:groupbar:priority"); + static auto PSTACKED = CConfigValue("group:groupbar:stacked"); SDecorationPositioningInfo info; info.policy = DECORATION_POSITION_STICKY; @@ -39,16 +42,20 @@ SDecorationPositioningInfo CHyprGroupBarDecoration::getPositioningInfo() { info.priority = *PPRIORITY; info.reserved = true; - if (*PENABLED && m_pWindow->m_sSpecialRenderData.decorate) - info.desiredExtents = {{0, BAR_PADDING_OUTER_VERT * 2 + BAR_INDICATOR_HEIGHT + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0) + 2}, {0, 0}}; - else + if (*PENABLED && m_pWindow->m_sSpecialRenderData.decorate) { + if (*PSTACKED) { + const auto ONEBARHEIGHT = BAR_PADDING_OUTER_VERT + BAR_INDICATOR_HEIGHT + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0); + info.desiredExtents = {{0, (ONEBARHEIGHT * m_dwGroupMembers.size()) + 2 + BAR_PADDING_OUTER_VERT}, {0, 0}}; + } else + info.desiredExtents = {{0, BAR_PADDING_OUTER_VERT * 2 + BAR_INDICATOR_HEIGHT + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0) + 2}, {0, 0}}; + } else info.desiredExtents = {{0, 0}, {0, 0}}; - return info; } void CHyprGroupBarDecoration::onPositioningReply(const SDecorationPositioningReply& reply) { m_bAssignedBox = reply.assignedGeometry; + g_pLayoutManager->getCurrentLayout()->recalculateWindow(m_pWindow.lock()); } eDecorationType CHyprGroupBarDecoration::getDecorationType() { @@ -96,24 +103,31 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a) { static auto PTITLEFONTSIZE = CConfigValue("group:groupbar:font_size"); static auto PHEIGHT = CConfigValue("group:groupbar:height"); static auto PGRADIENTS = CConfigValue("group:groupbar:gradients"); + static auto PSTACKED = CConfigValue("group:groupbar:stacked"); if (!*PENABLED || !m_pWindow->m_sSpecialRenderData.decorate) return; const auto ASSIGNEDBOX = assignedBoxGlobal(); - m_fBarWidth = (ASSIGNEDBOX.w - BAR_HORIZONTAL_PADDING * (barsToDraw - 1)) / barsToDraw; + const auto ONEBARHEIGHT = BAR_PADDING_OUTER_VERT + BAR_INDICATOR_HEIGHT + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0); + m_fBarWidth = *PSTACKED ? ASSIGNEDBOX.w : (ASSIGNEDBOX.w - BAR_HORIZONTAL_PADDING * (barsToDraw - 1)) / barsToDraw; + m_fBarHeight = *PSTACKED ? ((ASSIGNEDBOX.h - 2 - BAR_PADDING_OUTER_VERT) - BAR_PADDING_OUTER_VERT * (barsToDraw)) / barsToDraw : ASSIGNEDBOX.h - BAR_PADDING_OUTER_VERT; - const auto DESIREDHEIGHT = BAR_PADDING_OUTER_VERT * 2 + BAR_INDICATOR_HEIGHT + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0) + 2; - if (DESIREDHEIGHT != ASSIGNEDBOX.h) + const auto DESIREDHEIGHT = *PSTACKED ? (ONEBARHEIGHT * m_dwGroupMembers.size()) + 2 + BAR_PADDING_OUTER_VERT : BAR_PADDING_OUTER_VERT * 2 + ONEBARHEIGHT; + if (DESIREDHEIGHT != ASSIGNEDBOX.h) { g_pDecorationPositioner->repositionDeco(this); + } int xoff = 0; + int yoff = 0; for (int i = 0; i < barsToDraw; ++i) { - CBox rect = {ASSIGNEDBOX.x + xoff - pMonitor->vecPosition.x + m_pWindow->m_vFloatingOffset.x, - ASSIGNEDBOX.y + ASSIGNEDBOX.h - BAR_INDICATOR_HEIGHT - BAR_PADDING_OUTER_VERT - pMonitor->vecPosition.y + m_pWindow->m_vFloatingOffset.y, m_fBarWidth, - BAR_INDICATOR_HEIGHT}; + const auto WINDOWINDEX = *PSTACKED ? m_dwGroupMembers.size() - i - 1 : i; + + CBox rect = {ASSIGNEDBOX.x + xoff - pMonitor->vecPosition.x + m_pWindow->m_vFloatingOffset.x, + ASSIGNEDBOX.y + ASSIGNEDBOX.h - yoff - BAR_INDICATOR_HEIGHT - BAR_PADDING_OUTER_VERT - pMonitor->vecPosition.y + m_pWindow->m_vFloatingOffset.y, m_fBarWidth, + BAR_INDICATOR_HEIGHT}; if (rect.width <= 0 || rect.height <= 0) break; @@ -133,38 +147,43 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a) { const auto* const PCOLACTIVE = GROUPLOCKED ? GROUPCOLACTIVELOCKED : GROUPCOLACTIVE; const auto* const PCOLINACTIVE = GROUPLOCKED ? GROUPCOLINACTIVELOCKED : GROUPCOLINACTIVE; - CColor color = m_dwGroupMembers[i].lock() == g_pCompositor->m_pLastWindow.lock() ? PCOLACTIVE->m_vColors[0] : PCOLINACTIVE->m_vColors[0]; + CColor color = m_dwGroupMembers[WINDOWINDEX].lock() == g_pCompositor->m_pLastWindow.lock() ? PCOLACTIVE->m_vColors[0] : PCOLINACTIVE->m_vColors[0]; color.a *= a; g_pHyprOpenGL->renderRect(&rect, color); rect = {ASSIGNEDBOX.x + xoff - pMonitor->vecPosition.x + m_pWindow->m_vFloatingOffset.x, - ASSIGNEDBOX.y - pMonitor->vecPosition.y + m_pWindow->m_vFloatingOffset.y + BAR_PADDING_OUTER_VERT, m_fBarWidth, - ASSIGNEDBOX.h - BAR_INDICATOR_HEIGHT - BAR_PADDING_OUTER_VERT * 2}; + ASSIGNEDBOX.y + ASSIGNEDBOX.h - yoff - ONEBARHEIGHT - pMonitor->vecPosition.y + m_pWindow->m_vFloatingOffset.y, m_fBarWidth, + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0)}; rect.scale(pMonitor->scale); if (*PGRADIENTS) { - const auto& GRADIENTTEX = (m_dwGroupMembers[i].lock() == g_pCompositor->m_pLastWindow.lock() ? (GROUPLOCKED ? m_tGradientLockedActive : m_tGradientActive) : - (GROUPLOCKED ? m_tGradientLockedInactive : m_tGradientInactive)); + const auto& GRADIENTTEX = + (m_dwGroupMembers[WINDOWINDEX].lock() == g_pCompositor->m_pLastWindow.lock() ? (GROUPLOCKED ? m_tGradientLockedActive : m_tGradientActive) : + (GROUPLOCKED ? m_tGradientLockedInactive : m_tGradientInactive)); if (GRADIENTTEX.m_iTexID != 0) g_pHyprOpenGL->renderTexture(GRADIENTTEX, &rect, 1.0); } if (*PRENDERTITLES) { - CTitleTex* pTitleTex = textureFromTitle(m_dwGroupMembers[i]->m_szTitle); + CTitleTex* pTitleTex = textureFromTitle(m_dwGroupMembers[WINDOWINDEX]->m_szTitle); if (!pTitleTex) - pTitleTex = m_sTitleTexs.titleTexs - .emplace_back(std::make_unique( - m_dwGroupMembers[i].lock(), Vector2D{m_fBarWidth * pMonitor->scale, (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale}, pMonitor->scale)) - .get(); + pTitleTex = + m_sTitleTexs.titleTexs + .emplace_back(std::make_unique(m_dwGroupMembers[WINDOWINDEX].lock(), + Vector2D{m_fBarWidth * pMonitor->scale, (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale}, pMonitor->scale)) + .get(); - rect.y += (ASSIGNEDBOX.h / 2.0 - (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) / 2.0) * pMonitor->scale; + rect.y += (*PHEIGHT / 2.0 - (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) / 2.0) * pMonitor->scale; rect.height = (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale; g_pHyprOpenGL->renderTexture(pTitleTex->tex, &rect, 1.f); } - xoff += BAR_HORIZONTAL_PADDING + m_fBarWidth; + if (*PSTACKED) + yoff += ONEBARHEIGHT; + else + xoff += BAR_HORIZONTAL_PADDING + m_fBarWidth; } if (*PRENDERTITLES) @@ -335,13 +354,18 @@ void refreshGroupBarGradients() { } bool CHyprGroupBarDecoration::onBeginWindowDragOnDeco(const Vector2D& pos) { + static auto PSTACKED = CConfigValue("group:groupbar:stacked"); if (m_pWindow.lock() == m_pWindow->m_sGroupData.pNextWindow.lock()) return false; const float BARRELATIVEX = pos.x - assignedBoxGlobal().x; - const int WINDOWINDEX = (BARRELATIVEX) / (m_fBarWidth + BAR_HORIZONTAL_PADDING); + const float BARRELATIVEY = pos.y - assignedBoxGlobal().y; + const int WINDOWINDEX = *PSTACKED ? (BARRELATIVEY / (m_fBarHeight + BAR_PADDING_OUTER_VERT)) : (BARRELATIVEX) / (m_fBarWidth + BAR_HORIZONTAL_PADDING); - if (BARRELATIVEX - (m_fBarWidth + BAR_HORIZONTAL_PADDING) * WINDOWINDEX > m_fBarWidth) + if (!*PSTACKED && (BARRELATIVEX - (m_fBarWidth + BAR_HORIZONTAL_PADDING) * WINDOWINDEX > m_fBarWidth)) + return false; + + if (*PSTACKED && (BARRELATIVEY - (m_fBarHeight + BAR_PADDING_OUTER_VERT) * WINDOWINDEX < BAR_PADDING_OUTER_VERT)) return false; PHLWINDOW pWindow = m_pWindow->getGroupWindowByIndex(WINDOWINDEX); @@ -364,11 +388,13 @@ bool CHyprGroupBarDecoration::onBeginWindowDragOnDeco(const Vector2D& pos) { } bool CHyprGroupBarDecoration::onEndWindowDragOnDeco(const Vector2D& pos, PHLWINDOW pDraggedWindow) { + static auto PSTACKED = CConfigValue("group:groupbar:stacked"); if (!pDraggedWindow->canBeGroupedInto(m_pWindow.lock())) return false; - const float BARRELATIVEX = pos.x - assignedBoxGlobal().x - m_fBarWidth / 2; - const int WINDOWINDEX = BARRELATIVEX < 0 ? -1 : (BARRELATIVEX) / (m_fBarWidth + BAR_HORIZONTAL_PADDING); + const float BARRELATIVE = *PSTACKED ? pos.y - assignedBoxGlobal().y - (m_fBarHeight + BAR_PADDING_OUTER_VERT) / 2 : pos.x - assignedBoxGlobal().x - m_fBarWidth / 2; + const float BARSIZE = *PSTACKED ? m_fBarHeight + BAR_PADDING_OUTER_VERT : m_fBarWidth + BAR_HORIZONTAL_PADDING; + const int WINDOWINDEX = BARRELATIVE < 0 ? -1 : BARRELATIVE / BARSIZE; PHLWINDOW pWindowInsertAfter = m_pWindow->getGroupWindowByIndex(WINDOWINDEX); PHLWINDOW pWindowInsertEnd = pWindowInsertAfter->m_sGroupData.pNextWindow.lock(); @@ -423,11 +449,13 @@ bool CHyprGroupBarDecoration::onEndWindowDragOnDeco(const Vector2D& pos, PHLWIND } bool CHyprGroupBarDecoration::onMouseButtonOnDeco(const Vector2D& pos, const IPointer::SButtonEvent& e) { + static auto PSTACKED = CConfigValue("group:groupbar:stacked"); if (m_pWindow->m_bIsFullscreen && m_pWindow->m_pWorkspace->m_efFullscreenMode == FULLSCREEN_FULL) return true; const float BARRELATIVEX = pos.x - assignedBoxGlobal().x; - const int WINDOWINDEX = (BARRELATIVEX) / (m_fBarWidth + BAR_HORIZONTAL_PADDING); + const float BARRELATIVEY = pos.y - assignedBoxGlobal().y; + const int WINDOWINDEX = *PSTACKED ? (BARRELATIVEY / (m_fBarHeight + BAR_PADDING_OUTER_VERT)) : (BARRELATIVEX) / (m_fBarWidth + BAR_HORIZONTAL_PADDING); static auto PFOLLOWMOUSE = CConfigValue("input:follow_mouse"); // close window on middle click @@ -446,7 +474,9 @@ bool CHyprGroupBarDecoration::onMouseButtonOnDeco(const Vector2D& pos, const IPo return true; // click on padding - if (BARRELATIVEX - (m_fBarWidth + BAR_HORIZONTAL_PADDING) * WINDOWINDEX > m_fBarWidth) { + const auto TABPAD = !*PSTACKED && (BARRELATIVEX - (m_fBarWidth + BAR_HORIZONTAL_PADDING) * WINDOWINDEX > m_fBarWidth); + const auto STACKPAD = *PSTACKED && (BARRELATIVEY - (m_fBarHeight + BAR_PADDING_OUTER_VERT) * WINDOWINDEX < BAR_PADDING_OUTER_VERT); + if (TABPAD || STACKPAD) { if (!g_pCompositor->isWindowActive(m_pWindow.lock())) g_pCompositor->focusWindow(m_pWindow.lock()); return true; diff --git a/src/render/decorations/CHyprGroupBarDecoration.hpp b/src/render/decorations/CHyprGroupBarDecoration.hpp index 3fe653cc..e3f553c5 100644 --- a/src/render/decorations/CHyprGroupBarDecoration.hpp +++ b/src/render/decorations/CHyprGroupBarDecoration.hpp @@ -54,6 +54,7 @@ class CHyprGroupBarDecoration : public IHyprWindowDecoration { std::deque m_dwGroupMembers; float m_fBarWidth; + float m_fBarHeight; CTitleTex* textureFromTitle(const std::string&); void invalidateTextures(); From d693c448366131de9e5729690a6d894fd1adff5e Mon Sep 17 00:00:00 2001 From: Gabriel Ford Date: Thu, 16 May 2024 10:48:30 +0000 Subject: [PATCH 35/57] keybinds: add keybind combos and add Left and Right mod distinction. (#5966) --- src/config/ConfigManager.cpp | 24 ++++++++-- src/managers/KeybindManager.cpp | 84 ++++++++++++++++++++++++++------- src/managers/KeybindManager.hpp | 43 +++++++++++------ 3 files changed, 117 insertions(+), 34 deletions(-) diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index b0c44408..ac5c101e 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -6,6 +6,7 @@ #include "helpers/VarList.hpp" #include "../protocols/LayerShell.hpp" +#include #include #include #include @@ -18,6 +19,7 @@ #include #include #include +#include extern "C" char** environ; @@ -1908,6 +1910,7 @@ std::optional CConfigManager::handleBind(const std::string& command bool nonConsuming = false; bool transparent = false; bool ignoreMods = false; + bool multiKey = false; const auto BINDARGS = command.substr(4); for (auto& arg : BINDARGS) { @@ -1925,6 +1928,8 @@ std::optional CConfigManager::handleBind(const std::string& command transparent = true; } else if (arg == 'i') { ignoreMods = true; + } else if (arg == 's') { + multiKey = true; } else { return "bind: invalid flag"; } @@ -1943,10 +1948,21 @@ std::optional CConfigManager::handleBind(const std::string& command else if ((ARGS.size() > 4 && !mouse) || (ARGS.size() > 3 && mouse)) return "bind: too many args"; + std::set KEYSYMS; + std::set MODS; + + if (multiKey) { + for (auto splitKey : CVarList(ARGS[1], 8, '&')) { + KEYSYMS.insert(xkb_keysym_from_name(splitKey.c_str(), XKB_KEYSYM_CASE_INSENSITIVE)); + } + for (auto splitMod : CVarList(ARGS[0], 8, '&')) { + MODS.insert(xkb_keysym_from_name(splitMod.c_str(), XKB_KEYSYM_CASE_INSENSITIVE)); + } + } const auto MOD = g_pKeybindManager->stringToModMask(ARGS[0]); const auto MODSTR = ARGS[0]; - const auto KEY = ARGS[1]; + const auto KEY = multiKey ? "" : ARGS[1]; auto HANDLER = ARGS[2]; @@ -1970,7 +1986,7 @@ std::optional CConfigManager::handleBind(const std::string& command return "Invalid mod, requested mod \"" + MODSTR + "\" is not a valid mod."; } - if (KEY != "") { + if ((KEY != "") || multiKey) { SParsedKey parsedKey = parseKey(KEY); if (parsedKey.catchAll && m_szCurrentSubmap == "") { @@ -1978,8 +1994,8 @@ std::optional CConfigManager::handleBind(const std::string& command return "Invalid catchall, catchall keybinds are only allowed in submaps."; } - g_pKeybindManager->addKeybind(SKeybind{parsedKey.key, parsedKey.keycode, parsedKey.catchAll, MOD, HANDLER, COMMAND, locked, m_szCurrentSubmap, release, repeat, mouse, - nonConsuming, transparent, ignoreMods}); + g_pKeybindManager->addKeybind(SKeybind{parsedKey.key, KEYSYMS, parsedKey.keycode, parsedKey.catchAll, MOD, MODS, HANDLER, COMMAND, locked, m_szCurrentSubmap, release, + repeat, mouse, nonConsuming, transparent, ignoreMods, multiKey}); } return {}; diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp index ead83fa8..ea064031 100644 --- a/src/managers/KeybindManager.cpp +++ b/src/managers/KeybindManager.cpp @@ -8,7 +8,9 @@ #include "../devices/IKeyboard.hpp" #include "../managers/SeatManager.hpp" +#include #include +#include #include #include @@ -536,8 +538,48 @@ int repeatKeyHandler(void* data) { return 0; } +eMultiKeyCase CKeybindManager::mkKeysymSetMatches(const std::set keybindKeysyms, const std::set pressedKeysyms) { + // Returns whether two sets of keysyms are equal, partially equal, or not + // matching. (Partially matching means that pressed is a subset of bound) + + std::set boundKeysNotPressed; + std::set pressedKeysNotBound; + + std::set_difference(keybindKeysyms.begin(), keybindKeysyms.end(), pressedKeysyms.begin(), pressedKeysyms.end(), + std::inserter(boundKeysNotPressed, boundKeysNotPressed.begin())); + std::set_difference(pressedKeysyms.begin(), pressedKeysyms.end(), keybindKeysyms.begin(), keybindKeysyms.end(), + std::inserter(pressedKeysNotBound, pressedKeysNotBound.begin())); + + if (boundKeysNotPressed.empty() && pressedKeysNotBound.empty()) + return MK_FULL_MATCH; + + if (boundKeysNotPressed.size() && pressedKeysNotBound.empty()) + return MK_PARTIAL_MATCH; + + return MK_NO_MATCH; +} + +eMultiKeyCase CKeybindManager::mkBindMatches(const SKeybind keybind) { + if (mkKeysymSetMatches(keybind.sMkMods, m_sMkMods) != MK_FULL_MATCH) + return MK_NO_MATCH; + + return mkKeysymSetMatches(keybind.sMkKeys, m_sMkKeys); +} + bool CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWithMods& key, bool pressed) { - bool found = false; + bool found = false; + + if (pressed) { + if (keycodeToModifier(key.keycode)) + m_sMkMods.insert(key.keysym); + else + m_sMkKeys.insert(key.keysym); + } else { + if (keycodeToModifier(key.keycode)) + m_sMkMods.erase(key.keysym); + else + m_sMkKeys.erase(key.keysym); + } static auto PDISABLEINHIBIT = CConfigValue("binds:disable_keybind_grabbing"); @@ -559,7 +601,13 @@ bool CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWi if (!IGNORECONDITIONS && ((modmask != k.modmask && !k.ignoreMods) || k.submap != m_szCurrentSelectedSubmap || k.shadowed)) continue; - if (!key.keyName.empty()) { + if (k.multiKey) { + switch (mkBindMatches(k)) { + case MK_NO_MATCH: continue; + case MK_PARTIAL_MATCH: found = true; continue; + case MK_FULL_MATCH: found = true; + } + } else if (!key.keyName.empty()) { if (key.keyName != k.key) continue; } else if (k.keycode != 0) { @@ -672,25 +720,29 @@ void CKeybindManager::shadowKeybinds(const xkb_keysym_t& doesntHave, const uint3 if (k.handler == "global" || k.transparent) continue; // can't be shadowed - const auto KBKEY = xkb_keysym_from_name(k.key.c_str(), XKB_KEYSYM_CASE_INSENSITIVE); - const auto KBKEYUPPER = xkb_keysym_to_upper(KBKEY); + if (k.multiKey && (mkBindMatches(k) == MK_FULL_MATCH)) + shadow = true; + else { + const auto KBKEY = xkb_keysym_from_name(k.key.c_str(), XKB_KEYSYM_CASE_INSENSITIVE); + const auto KBKEYUPPER = xkb_keysym_to_upper(KBKEY); - for (auto& pk : m_dPressedKeys) { - if ((pk.keysym != 0 && (pk.keysym == KBKEY || pk.keysym == KBKEYUPPER))) { - shadow = true; + for (auto& pk : m_dPressedKeys) { + if ((pk.keysym != 0 && (pk.keysym == KBKEY || pk.keysym == KBKEYUPPER))) { + shadow = true; - if (pk.keysym == doesntHave && doesntHave != 0) { - shadow = false; - break; + if (pk.keysym == doesntHave && doesntHave != 0) { + shadow = false; + break; + } } - } - if (pk.keycode != 0 && pk.keycode == k.keycode) { - shadow = true; + if (pk.keycode != 0 && pk.keycode == k.keycode) { + shadow = true; - if (pk.keycode == doesntHaveCode && doesntHaveCode != 0) { - shadow = false; - break; + if (pk.keycode == doesntHaveCode && doesntHaveCode != 0) { + shadow = false; + break; + } } } } diff --git a/src/managers/KeybindManager.hpp b/src/managers/KeybindManager.hpp index da98a749..a2025bb0 100644 --- a/src/managers/KeybindManager.hpp +++ b/src/managers/KeybindManager.hpp @@ -2,6 +2,7 @@ #include "../defines.hpp" #include +#include #include "../Compositor.hpp" #include #include @@ -13,20 +14,23 @@ class CPluginSystem; class IKeyboard; struct SKeybind { - std::string key = ""; - uint32_t keycode = 0; - bool catchAll = false; - uint32_t modmask = 0; - std::string handler = ""; - std::string arg = ""; - bool locked = false; - std::string submap = ""; - bool release = false; - bool repeat = false; - bool mouse = false; - bool nonConsuming = false; - bool transparent = false; - bool ignoreMods = false; + std::string key = ""; + std::set sMkKeys = {}; + uint32_t keycode = 0; + bool catchAll = false; + uint32_t modmask = 0; + std::set sMkMods = {}; + std::string handler = ""; + std::string arg = ""; + bool locked = false; + std::string submap = ""; + bool release = false; + bool repeat = false; + bool mouse = false; + bool nonConsuming = false; + bool transparent = false; + bool ignoreMods = false; + bool multiKey = false; // DO NOT INITIALIZE bool shadowed = false; @@ -57,6 +61,12 @@ struct SParsedKey { bool catchAll = false; }; +enum eMultiKeyCase { + MK_NO_MATCH = 0, + MK_PARTIAL_MATCH, + MK_FULL_MATCH +}; + class CKeybindManager { public: CKeybindManager(); @@ -105,6 +115,11 @@ class CKeybindManager { bool handleKeybinds(const uint32_t, const SPressedKeyWithMods&, bool); + std::set m_sMkKeys = {}; + std::set m_sMkMods = {}; + eMultiKeyCase mkBindMatches(const SKeybind); + eMultiKeyCase mkKeysymSetMatches(const std::set, const std::set); + bool handleInternalKeybinds(xkb_keysym_t); bool handleVT(xkb_keysym_t); From 3ac0e7ead1434c172f276a12f81da1eeb0dc4491 Mon Sep 17 00:00:00 2001 From: Agent00Ming <107314235+Agent00Ming@users.noreply.github.com> Date: Thu, 16 May 2024 08:30:55 -0400 Subject: [PATCH 36/57] seat: Send discrete event when axis source is scroll wheel (#6103) modified: src/managers/SeatManager.cpp modified: src/managers/input/InputManager.cpp Co-authored-by: Agent_00Ming --- src/managers/SeatManager.cpp | 3 +++ src/managers/input/InputManager.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/managers/SeatManager.cpp b/src/managers/SeatManager.cpp index 2c7bfe39..2087122a 100644 --- a/src/managers/SeatManager.cpp +++ b/src/managers/SeatManager.cpp @@ -298,6 +298,9 @@ void CSeatManager::sendPointerAxis(uint32_t timeMs, wl_pointer_axis axis, double p->sendAxisSource(source); p->sendAxisRelativeDirection(axis, relative); + if (source == 0) + p->sendAxisDiscrete(axis, discrete); + if (value == 0) p->sendAxisStop(timeMs, axis); } diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index 02072229..58e5ad17 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -774,7 +774,7 @@ void CInputManager::onMouseWheel(IPointer::SAxisEvent e) { } } - g_pSeatManager->sendPointerAxis(e.timeMs, e.axis, factor * e.delta, std::round(factor * e.deltaDiscrete), e.source, WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL); + g_pSeatManager->sendPointerAxis(e.timeMs, e.axis, factor * e.delta, std::round(factor * e.deltaDiscrete / 120), e.source, WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL); } Vector2D CInputManager::getMouseCoordsInternal() { From a2643e11a0a0e5e01ffb51b8ae41cf1ef458102e Mon Sep 17 00:00:00 2001 From: Vaxry Date: Thu, 16 May 2024 18:35:48 +0100 Subject: [PATCH 37/57] build: bump hw-s dep to 0.3.8 --- CMakeLists.txt | 2 +- protocols/meson.build | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9897185f..ab3576c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,7 +110,7 @@ pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-server wayland-client wayland-cursor wayland-protocols cairo pango pangocairo pixman-1 libdrm libinput hwdata libseat libdisplay-info libliftoff libudev gbm - hyprwayland-scanner>=0.3.7 hyprlang>=0.3.2 hyprcursor>=0.1.7 + hyprwayland-scanner>=0.3.8 hyprlang>=0.3.2 hyprcursor>=0.1.7 ) file(GLOB_RECURSE SRCFILES "src/*.cpp") diff --git a/protocols/meson.build b/protocols/meson.build index 6b0b4d18..2c331e4b 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -17,7 +17,7 @@ wayland_scanner = find_program( wayland_scanner_dep.get_variable('wayland_scanner'), native: true, ) -hyprwayland_scanner_dep = dependency('hyprwayland-scanner', version: '>=0.3.5', native: true) +hyprwayland_scanner_dep = dependency('hyprwayland-scanner', version: '>=0.3.8', native: true) hyprwayland_scanner = find_program( hyprwayland_scanner_dep.get_variable('hyprwayland_scanner'), native: true, From abbe71d26dd0299071e7a86a287c49d7fb185677 Mon Sep 17 00:00:00 2001 From: Vaxry Date: Thu, 16 May 2024 19:34:36 +0100 Subject: [PATCH 38/57] pointer: don't update hw cursors on disabled displays --- src/managers/PointerManager.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/managers/PointerManager.cpp b/src/managers/PointerManager.cpp index 6a0fb984..d565ad6d 100644 --- a/src/managers/PointerManager.cpp +++ b/src/managers/PointerManager.cpp @@ -309,6 +309,11 @@ void CPointerManager::resetCursorImage(bool apply) { return; for (auto& ms : monitorStates) { + if (!ms->monitor || !ms->monitor->m_bEnabled || !ms->monitor->dpmsStatus) { + Debug::log(TRACE, "Not updating hw cursors: disabled / dpms off display"); + continue; + } + if (ms->cursorFrontBuffer) { if (ms->monitor->output->impl->set_cursor) ms->monitor->output->impl->set_cursor(ms->monitor->output, nullptr, 0, 0); @@ -324,6 +329,11 @@ void CPointerManager::updateCursorBackend() { for (auto& m : g_pCompositor->m_vMonitors) { auto state = stateFor(m); + if (!m->m_bEnabled || !m->dpmsStatus) { + Debug::log(TRACE, "Not updating hw cursors: disabled / dpms off display"); + continue; + } + if (state->softwareLocks > 0 || *PNOHW || !attemptHardwareCursor(state)) { Debug::log(TRACE, "Output {} rejected hardware cursors, falling back to sw", m->szName); state->box = getCursorBoxLogicalForMonitor(state->monitor.lock()); From 7173f0c9e722b52eebcc231a118df738c8e32013 Mon Sep 17 00:00:00 2001 From: Mihai Fufezan Date: Fri, 17 May 2024 00:03:23 +0300 Subject: [PATCH 39/57] flake.lock: update nix/overlays: remove merged wayland-protocols overlay Fixes #6061 --- flake.lock | 30 +++++++++++++++--------------- nix/overlays.nix | 11 ----------- 2 files changed, 15 insertions(+), 26 deletions(-) diff --git a/flake.lock b/flake.lock index 98dda5bd..ca05387b 100644 --- a/flake.lock +++ b/flake.lock @@ -13,11 +13,11 @@ ] }, "locked": { - "lastModified": 1713612213, - "narHash": "sha256-zJboXgWNpNhKyNF8H/3UYzWkx7w00TOCGKi3cwi+tsw=", + "lastModified": 1715791817, + "narHash": "sha256-J069Uhv/gCMFLX1dSh2f+9ZTM09r1Nv3oUfocCnWKow=", "owner": "hyprwm", "repo": "hyprcursor", - "rev": "cab4746180f210a3c1dd3d53e45c510e309e90e1", + "rev": "7c3aa03dffb53921e583ade3d4ae3f487e390e7e", "type": "github" }, "original": { @@ -61,11 +61,11 @@ ] }, "locked": { - "lastModified": 1713121246, - "narHash": "sha256-502X0Q0fhN6tJK7iEUA8CghONKSatW/Mqj4Wappd++0=", + "lastModified": 1715791527, + "narHash": "sha256-HhQ4zvGHrRjR63ltySSeg+x+0jb0lepiutWdnFhLRoo=", "owner": "hyprwm", "repo": "hyprlang", - "rev": "78fcaa27ae9e1d782faa3ff06c8ea55ddce63706", + "rev": "969cb076e5b76f2e823aeca1937a3e1f159812ee", "type": "github" }, "original": { @@ -84,11 +84,11 @@ ] }, "locked": { - "lastModified": 1715608589, - "narHash": "sha256-vimNaLjLcoNIvBhF37GaB6PRYEvKMamY3UnDE9M5MW8=", + "lastModified": 1715879663, + "narHash": "sha256-/DwglRvj4XF4ECdNtrCIbthleszAZBwOiXG5A6r0K/c=", "owner": "hyprwm", "repo": "hyprwayland-scanner", - "rev": "65c2636484e5cb00583b8a7446c3fb657f568883", + "rev": "f5181a068c1b06f2db51f6222e50a0c665a2b0c3", "type": "github" }, "original": { @@ -99,11 +99,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1715534503, - "narHash": "sha256-5ZSVkFadZbFP1THataCaSf0JH2cAH3S29hU9rrxTEqk=", + "lastModified": 1715787315, + "narHash": "sha256-cYApT0NXJfqBkKcci7D9Kr4CBYZKOQKDYA23q8XNuWg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "2057814051972fa1453ddfb0d98badbea9b83c06", + "rev": "33d1e753c82ffc557b4a585c77de43d4c922ebb5", "type": "github" }, "original": { @@ -152,11 +152,11 @@ ] }, "locked": { - "lastModified": 1714662532, - "narHash": "sha256-Pj2xGSYhapYbXL7sk7TTlOtCZcTfPQoL3fPbZeg7L4Y=", + "lastModified": 1715788457, + "narHash": "sha256-32HOkjSIyANphV0p5gIwP4ONU/CcinhwOyVFB+tL/d0=", "owner": "hyprwm", "repo": "xdg-desktop-portal-hyprland", - "rev": "1f228ba2f1f254195c0b571302b37482861abee3", + "rev": "af7c87a32f5d67eb2ada908a6a700f4e74831943", "type": "github" }, "original": { diff --git a/nix/overlays.nix b/nix/overlays.nix index 50d9f9d9..fbd7e9fc 100644 --- a/nix/overlays.nix +++ b/nix/overlays.nix @@ -24,7 +24,6 @@ in { inputs.hyprcursor.overlays.default inputs.hyprlang.overlays.default inputs.hyprwayland-scanner.overlays.default - self.overlays.wayland-protocols self.overlays.xwayland # Hyprland packages themselves @@ -73,14 +72,4 @@ in { ''; }); }; - - wayland-protocols = final: prev: { - wayland-protocols = prev.wayland-protocols.overrideAttrs (self: super: { - version = "1.35"; - src = prev.fetchurl { - url = "https://gitlab.freedesktop.org/wayland/${super.pname}/-/releases/${self.version}/downloads/${super.pname}-${self.version}.tar.xz"; - hash = "sha256-N6JxaigTPcgZNBxWiinSHoy3ITDlwSah/PyfQsI9las="; - }; - }); - }; } From a66cfe0fbed7fb4dc69383e107c2bf3b1e7cd80a Mon Sep 17 00:00:00 2001 From: Yaroslav Date: Fri, 17 May 2024 03:34:03 +0500 Subject: [PATCH 40/57] CMake: use add_custom_command for generating protocols (#6104) This fixes an issue with build error in case of e.g. $ rm protocols/*.{c,h} $ cmake --build build The workaround was to touch CMakeLists.txt. With this PR, protocol files are properly regenerated with no extra efforts. Also, resolve hyprwayland-scanner dependency via cmake instead ofpkg-config. --- CMakeLists.txt | 118 ++++++++++++++++++++++++++----------------------- 1 file changed, 63 insertions(+), 55 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ab3576c1..b30591d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,9 +110,11 @@ pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-server wayland-client wayland-cursor wayland-protocols cairo pango pangocairo pixman-1 libdrm libinput hwdata libseat libdisplay-info libliftoff libudev gbm - hyprwayland-scanner>=0.3.8 hyprlang>=0.3.2 hyprcursor>=0.1.7 + hyprlang>=0.3.2 hyprcursor>=0.1.7 ) +find_package(hyprwayland-scanner 0.3.8 REQUIRED) + file(GLOB_RECURSE SRCFILES "src/*.cpp") set(TRACY_CPP_FILES "") @@ -210,42 +212,48 @@ target_link_libraries(Hyprland rt PkgConfig::deps) function(protocol protoPath protoName external) if (external) - execute_process( - COMMAND ${WaylandScanner} server-header ${protoPath} protocols/${protoName}-protocol.h - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) - execute_process( - COMMAND ${WaylandScanner} private-code ${protoPath} protocols/${protoName}-protocol.c - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) - target_sources(Hyprland PRIVATE protocols/${protoName}-protocol.c) + set(path ${CMAKE_SOURCE_DIR}/${protoPath}) else() - execute_process( - COMMAND ${WaylandScanner} server-header ${WAYLAND_PROTOCOLS_DIR}/${protoPath} protocols/${protoName}-protocol.h - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) - execute_process( - COMMAND ${WaylandScanner} private-code ${WAYLAND_PROTOCOLS_DIR}/${protoPath} protocols/${protoName}-protocol.c - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) - target_sources(Hyprland PRIVATE protocols/${protoName}-protocol.c) + set(path ${WAYLAND_PROTOCOLS_DIR}/${protoPath}) endif() + + add_custom_command( + OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.h + COMMAND ${WaylandScanner} server-header ${path} protocols/${protoName}-protocol.h + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + ) + add_custom_command( + OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.c + COMMAND ${WaylandScanner} private-code ${path} protocols/${protoName}-protocol.c + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + ) + target_sources(Hyprland PRIVATE ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.h) + target_sources(Hyprland PRIVATE ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.c) endfunction() function(protocolNew protoPath protoName external) if (external) - execute_process( - COMMAND hyprwayland-scanner ${protoPath} ${CMAKE_SOURCE_DIR}/protocols/ - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) - target_sources(Hyprland PRIVATE protocols/${protoName}.cpp) + set(path ${CMAKE_SOURCE_DIR}/${protoPath}) else() - execute_process( - COMMAND hyprwayland-scanner ${WAYLAND_PROTOCOLS_DIR}/${protoPath} ${CMAKE_SOURCE_DIR}/protocols/ - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) - target_sources(Hyprland PRIVATE protocols/${protoName}.cpp) + set(path ${WAYLAND_PROTOCOLS_DIR}/${protoPath}) endif() + add_custom_command( + OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}.cpp + ${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp + COMMAND hyprwayland-scanner ${path}/${protoName}.xml ${CMAKE_SOURCE_DIR}/protocols/ + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + ) + target_sources(Hyprland PRIVATE protocols/${protoName}.cpp) + target_sources(Hyprland PRIVATE protocols/${protoName}.hpp) endfunction() function(protocolWayland) - execute_process( + add_custom_command( + OUTPUT ${CMAKE_SOURCE_DIR}/protocols/wayland.cpp + ${CMAKE_SOURCE_DIR}/protocols/wayland.hpp COMMAND hyprwayland-scanner --wayland-enums ${WAYLAND_SERVER_DIR}/wayland.xml ${CMAKE_SOURCE_DIR}/protocols/ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) target_sources(Hyprland PRIVATE protocols/wayland.cpp) + target_sources(Hyprland PRIVATE protocols/wayland.hpp) endfunction() target_link_libraries(Hyprland @@ -263,37 +271,37 @@ protocol("subprojects/hyprland-protocols/protocols/hyprland-toplevel-export-v1.x protocol("unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml" "linux-dmabuf-unstable-v1" false) protocol("unstable/text-input/text-input-unstable-v1.xml" "text-input-unstable-v1" false) -protocolNew("protocols/wlr-gamma-control-unstable-v1.xml" "wlr-gamma-control-unstable-v1" true) -protocolNew("protocols/wlr-foreign-toplevel-management-unstable-v1.xml" "wlr-foreign-toplevel-management-unstable-v1" true) -protocolNew("protocols/wlr-output-power-management-unstable-v1.xml" "wlr-output-power-management-unstable-v1" true) -protocolNew("protocols/virtual-keyboard-unstable-v1.xml" "virtual-keyboard-unstable-v1" true) -protocolNew("protocols/wlr-virtual-pointer-unstable-v1.xml" "wlr-virtual-pointer-unstable-v1" true) -protocolNew("protocols/input-method-unstable-v2.xml" "input-method-unstable-v2" true) -protocolNew("protocols/wlr-output-management-unstable-v1.xml" "wlr-output-management-unstable-v1" true) -protocolNew("protocols/kde-server-decoration.xml" "kde-server-decoration" true) -protocolNew("protocols/wlr-data-control-unstable-v1.xml" "wlr-data-control-unstable-v1" true) -protocolNew("subprojects/hyprland-protocols/protocols/hyprland-focus-grab-v1.xml" "hyprland-focus-grab-v1" true) -protocolNew("protocols/wlr-layer-shell-unstable-v1.xml" "wlr-layer-shell-unstable-v1" true) -protocolNew("staging/tearing-control/tearing-control-v1.xml" "tearing-control-v1" false) -protocolNew("staging/fractional-scale/fractional-scale-v1.xml" "fractional-scale-v1" false) -protocolNew("unstable/xdg-output/xdg-output-unstable-v1.xml" "xdg-output-unstable-v1" false) -protocolNew("staging/cursor-shape/cursor-shape-v1.xml" "cursor-shape-v1" false) -protocolNew("unstable/idle-inhibit/idle-inhibit-unstable-v1.xml" "idle-inhibit-unstable-v1" false) -protocolNew("unstable/relative-pointer/relative-pointer-unstable-v1.xml" "relative-pointer-unstable-v1" false) -protocolNew("unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" "xdg-decoration-unstable-v1" false) -protocolNew("staging/alpha-modifier/alpha-modifier-v1.xml" "alpha-modifier-v1" false) -protocolNew("staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml" "ext-foreign-toplevel-list-v1" false) -protocolNew("unstable/pointer-gestures/pointer-gestures-unstable-v1.xml" "pointer-gestures-unstable-v1" false) -protocolNew("unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml" "keyboard-shortcuts-inhibit-unstable-v1" false) -protocolNew("unstable/text-input/text-input-unstable-v3.xml" "text-input-unstable-v3" false) -protocolNew("unstable/pointer-constraints/pointer-constraints-unstable-v1.xml" "pointer-constraints-unstable-v1" false) -protocolNew("staging/xdg-activation/xdg-activation-v1.xml" "xdg-activation-v1" false) -protocolNew("staging/ext-idle-notify/ext-idle-notify-v1.xml" "ext-idle-notify-v1" false) -protocolNew("staging/ext-session-lock/ext-session-lock-v1.xml" "ext-session-lock-v1" false) -protocolNew("stable/tablet/tablet-v2.xml" "tablet-v2" false) -protocolNew("stable/presentation-time/presentation-time.xml" "presentation-time" false) -protocolNew("stable/xdg-shell/xdg-shell.xml" "xdg-shell" false) -protocolNew("unstable/primary-selection/primary-selection-unstable-v1.xml" "primary-selection-unstable-v1" false) +protocolNew("protocols" "wlr-gamma-control-unstable-v1" true) +protocolNew("protocols" "wlr-foreign-toplevel-management-unstable-v1" true) +protocolNew("protocols" "wlr-output-power-management-unstable-v1" true) +protocolNew("protocols" "virtual-keyboard-unstable-v1" true) +protocolNew("protocols" "wlr-virtual-pointer-unstable-v1" true) +protocolNew("protocols" "input-method-unstable-v2" true) +protocolNew("protocols" "wlr-output-management-unstable-v1" true) +protocolNew("protocols" "kde-server-decoration" true) +protocolNew("protocols" "wlr-data-control-unstable-v1" true) +protocolNew("subprojects/hyprland-protocols/protocols" "hyprland-focus-grab-v1" true) +protocolNew("protocols" "wlr-layer-shell-unstable-v1" true) +protocolNew("staging/tearing-control" "tearing-control-v1" false) +protocolNew("staging/fractional-scale" "fractional-scale-v1" false) +protocolNew("unstable/xdg-output" "xdg-output-unstable-v1" false) +protocolNew("staging/cursor-shape" "cursor-shape-v1" false) +protocolNew("unstable/idle-inhibit" "idle-inhibit-unstable-v1" false) +protocolNew("unstable/relative-pointer" "relative-pointer-unstable-v1" false) +protocolNew("unstable/xdg-decoration" "xdg-decoration-unstable-v1" false) +protocolNew("staging/alpha-modifier" "alpha-modifier-v1" false) +protocolNew("staging/ext-foreign-toplevel-list" "ext-foreign-toplevel-list-v1" false) +protocolNew("unstable/pointer-gestures" "pointer-gestures-unstable-v1" false) +protocolNew("unstable/keyboard-shortcuts-inhibit" "keyboard-shortcuts-inhibit-unstable-v1" false) +protocolNew("unstable/text-input" "text-input-unstable-v3" false) +protocolNew("unstable/pointer-constraints" "pointer-constraints-unstable-v1" false) +protocolNew("staging/xdg-activation" "xdg-activation-v1" false) +protocolNew("staging/ext-idle-notify" "ext-idle-notify-v1" false) +protocolNew("staging/ext-session-lock" "ext-session-lock-v1" false) +protocolNew("stable/tablet" "tablet-v2" false) +protocolNew("stable/presentation-time" "presentation-time" false) +protocolNew("stable/xdg-shell" "xdg-shell" false) +protocolNew("unstable/primary-selection" "primary-selection-unstable-v1" false) protocolWayland() From f91431465b910b1acfc352d2425310beb34b7598 Mon Sep 17 00:00:00 2001 From: Yaroslav Date: Fri, 17 May 2024 15:06:31 +0500 Subject: [PATCH 41/57] cmake: make gprof optional for debug builds (#6120) This fixes the debug build on musl systems, as -pg option is specific to glibc. Now we can build the project on such systems with -DUSE_GPROF=OFF --- CMakeLists.txt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b30591d9..43927fbe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -126,6 +126,8 @@ endif() add_executable(Hyprland ${SRCFILES} ${TRACY_CPP_FILES}) add_dependencies(Hyprland wlroots-hyprland) +set(USE_GPROF ON) + if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG) message(STATUS "Setting debug flags") @@ -151,8 +153,12 @@ if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG) endif() endif() - add_compile_options(-pg -no-pie -fno-builtin) - add_link_options(-pg -no-pie -fno-builtin) + add_compile_options(-fno-pie -fno-builtin) + add_link_options(-no-pie -fno-builtin) + if(USE_GPROF) + add_compile_options(-pg) + add_link_options(-pg) + endif() endif() check_include_file("execinfo.h" EXECINFOH) From f21b6fe576b1068598d6c19fbbfd3f0659d5498d Mon Sep 17 00:00:00 2001 From: Vaxry Date: Fri, 17 May 2024 14:51:06 +0100 Subject: [PATCH 42/57] tablet: avoid null deref on an empty cursor set fixes#6116 --- src/protocols/Tablet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protocols/Tablet.cpp b/src/protocols/Tablet.cpp index 7c54a116..54c55176 100644 --- a/src/protocols/Tablet.cpp +++ b/src/protocols/Tablet.cpp @@ -164,7 +164,7 @@ CTabletToolV2Resource::CTabletToolV2Resource(SP resource_, SP< if (!g_pSeatManager->state.pointerFocusResource || g_pSeatManager->state.pointerFocusResource->client() != r->client()) return; - g_pInputManager->processMouseRequest(CSeatManager::SSetCursorEvent{wlr_surface_from_resource(surf), {hot_x, hot_y}}); + g_pInputManager->processMouseRequest(CSeatManager::SSetCursorEvent{surf ? wlr_surface_from_resource(surf) : nullptr, {hot_x, hot_y}}); }); } From 0cb8fbe18e45936ef3e3c6f82fcd200affc9fb13 Mon Sep 17 00:00:00 2001 From: Tuur Vanhoutte <4633209+zjeffer@users.noreply.github.com> Date: Fri, 17 May 2024 20:06:51 +0200 Subject: [PATCH 43/57] error: Add option to change position of HyprError bar (#3241) (#6111) --- src/config/ConfigManager.cpp | 1 + src/hyprerror/HyprError.cpp | 15 +++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index ac5c101e..3dc36899 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -374,6 +374,7 @@ CConfigManager::CConfigManager() { m_pConfig->addConfigValue("debug:manual_crash", Hyprlang::INT{0}); m_pConfig->addConfigValue("debug:suppress_errors", Hyprlang::INT{0}); m_pConfig->addConfigValue("debug:error_limit", Hyprlang::INT{5}); + m_pConfig->addConfigValue("debug:error_position", Hyprlang::INT{0}); m_pConfig->addConfigValue("debug:watchdog_timeout", Hyprlang::INT{5}); m_pConfig->addConfigValue("debug:disable_scale_checks", Hyprlang::INT{0}); m_pConfig->addConfigValue("debug:colored_stdout_logs", Hyprlang::INT{1}); diff --git a/src/hyprerror/HyprError.cpp b/src/hyprerror/HyprError.cpp index 4c7b6731..e7e2ff0d 100644 --- a/src/hyprerror/HyprError.cpp +++ b/src/hyprerror/HyprError.cpp @@ -58,8 +58,11 @@ void CHyprError::createQueued() { cairo_paint(CAIRO); cairo_restore(CAIRO); - const auto LINECOUNT = Hyprlang::INT{1} + std::count(m_szQueued.begin(), m_szQueued.end(), '\n'); - static auto LINELIMIT = CConfigValue("debug:error_limit"); + const auto LINECOUNT = Hyprlang::INT{1} + std::count(m_szQueued.begin(), m_szQueued.end(), '\n'); + static auto LINELIMIT = CConfigValue("debug:error_limit"); + static auto BAR_POSITION = CConfigValue("debug:error_position"); + + const bool TOPBAR = *BAR_POSITION == 0; const auto VISLINECOUNT = std::min(LINECOUNT, *LINELIMIT); const auto EXTRALINES = (VISLINECOUNT < LINECOUNT) ? 1 : 0; @@ -68,11 +71,11 @@ void CHyprError::createQueued() { const double PAD = 10 * SCALE; - const double X = PAD; - const double Y = PAD; const double WIDTH = PMONITOR->vecPixelSize.x - PAD * 2; const double HEIGHT = (FONTSIZE + 2 * (FONTSIZE / 10.0)) * (VISLINECOUNT + EXTRALINES) + 3; const double RADIUS = PAD > HEIGHT / 2 ? HEIGHT / 2 - 1 : PAD; + const double X = PAD; + const double Y = TOPBAR ? PAD : PMONITOR->vecPixelSize.y - HEIGHT - PAD; m_bDamageBox = {0, 0, (int)PMONITOR->vecPixelSize.x, (int)HEIGHT + (int)PAD * 2}; @@ -96,9 +99,9 @@ void CHyprError::createQueued() { cairo_set_font_size(CAIRO, FONTSIZE); cairo_set_source_rgba(CAIRO, textColor.r, textColor.g, textColor.b, textColor.a); - float yoffset = FONTSIZE; + float yoffset = TOPBAR ? FONTSIZE : Y - PAD + FONTSIZE; int renderedcnt = 0; - while (m_szQueued != "" && renderedcnt < VISLINECOUNT) { + while (!m_szQueued.empty() && renderedcnt < VISLINECOUNT) { std::string current = m_szQueued.substr(0, m_szQueued.find('\n')); if (const auto NEWLPOS = m_szQueued.find('\n'); NEWLPOS != std::string::npos) m_szQueued = m_szQueued.substr(NEWLPOS + 1); From 23cd4c7998cc4308e5db9b64994ebd6b27c38877 Mon Sep 17 00:00:00 2001 From: Vaxry Date: Fri, 17 May 2024 19:28:33 +0100 Subject: [PATCH 44/57] seat: update keymap/repeat info on keymap events from keebs fixes #6114 --- src/managers/SeatManager.cpp | 11 ++++++++--- src/managers/SeatManager.hpp | 1 + src/managers/input/InputManager.cpp | 3 +++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/managers/SeatManager.cpp b/src/managers/SeatManager.cpp index 2087122a..bbbe01d4 100644 --- a/src/managers/SeatManager.cpp +++ b/src/managers/SeatManager.cpp @@ -86,10 +86,15 @@ void CSeatManager::setKeyboard(SP KEEB) { keyboard->active = false; keyboard = KEEB; - if (KEEB) { + if (KEEB) KEEB->active = true; - PROTO::seat->updateRepeatInfo(KEEB->wlr()->repeat_info.rate, KEEB->wlr()->repeat_info.delay); - } + + updateActiveKeyboardData(); +} + +void CSeatManager::updateActiveKeyboardData() { + if (keyboard) + PROTO::seat->updateRepeatInfo(keyboard->wlr()->repeat_info.rate, keyboard->wlr()->repeat_info.delay); PROTO::seat->updateKeymap(); } diff --git a/src/managers/SeatManager.hpp b/src/managers/SeatManager.hpp index cf5e9216..fee3efa9 100644 --- a/src/managers/SeatManager.hpp +++ b/src/managers/SeatManager.hpp @@ -54,6 +54,7 @@ class CSeatManager { void setMouse(SP mouse); void setKeyboard(SP keeb); + void updateActiveKeyboardData(); // updates the clients with the keymap and repeat info void setKeyboardFocus(wlr_surface* surf); void sendKeyboardKey(uint32_t timeMs, uint32_t key, wl_keyboard_key_state state); diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index 58e5ad17..e05520f4 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -839,6 +839,9 @@ void CInputManager::setupKeyboard(SP keeb) { auto PKEEB = ((IKeyboard*)owner)->self.lock(); const auto LAYOUT = PKEEB->getActiveLayout(); + if (PKEEB == g_pSeatManager->keyboard) + g_pSeatManager->updateActiveKeyboardData(); + g_pEventManager->postEvent(SHyprIPCEvent{"activelayout", PKEEB->hlName + "," + LAYOUT}); EMIT_HOOK_EVENT("activeLayout", (std::vector{PKEEB, LAYOUT})); }, From 9518cec833606166f6901fae0e68503a64938fce Mon Sep 17 00:00:00 2001 From: Vaxry Date: Fri, 17 May 2024 19:43:56 +0100 Subject: [PATCH 45/57] popup: clip input region to surface size fixes #6125 --- src/desktop/Popup.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/desktop/Popup.cpp b/src/desktop/Popup.cpp index c652d298..03acfff9 100644 --- a/src/desktop/Popup.cpp +++ b/src/desktop/Popup.cpp @@ -301,7 +301,9 @@ CPopup* CPopup::at(const Vector2D& globalCoords, bool allowsInput) { return p; } else { const Vector2D offset = p->m_pResource ? (p->size() - p->m_pResource->geometry.size()) / 2.F : Vector2D{}; - const auto REGION = CRegion{&p->m_sWLSurface.wlr()->current.input}.translate(p->coordsGlobal() + offset); + const auto REGION = CRegion{&p->m_sWLSurface.wlr()->current.input} + .intersect(CBox{{}, {p->m_sWLSurface.wlr()->current.width, p->m_sWLSurface.wlr()->current.height}}) + .translate(p->coordsGlobal() + offset); if (REGION.containsPoint(globalCoords)) return p; } From fe23d2b639f13db852adb5f10812f308b38d7e9b Mon Sep 17 00:00:00 2001 From: Vaxry Date: Fri, 17 May 2024 19:54:05 +0100 Subject: [PATCH 46/57] window: verify suppress flags in onUpdateState ref #6108 --- src/desktop/Window.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/desktop/Window.cpp b/src/desktop/Window.cpp index c11a714b..56003eca 100644 --- a/src/desktop/Window.cpp +++ b/src/desktop/Window.cpp @@ -1337,7 +1337,7 @@ void CWindow::onUpdateState() { if (!m_pXDGSurface) return; - if (m_pXDGSurface->toplevel->state.requestsFullscreen) { + if (m_pXDGSurface->toplevel->state.requestsFullscreen && !(m_eSuppressedEvents & SUPPRESS_FULLSCREEN)) { bool fs = m_pXDGSurface->toplevel->state.requestsFullscreen.value(); if (fs != m_bIsFullscreen && m_pXDGSurface->mapped) @@ -1347,7 +1347,7 @@ void CWindow::onUpdateState() { m_bWantsInitialFullscreen = fs; } - if (m_pXDGSurface->toplevel->state.requestsMaximize) { + if (m_pXDGSurface->toplevel->state.requestsMaximize && !(m_eSuppressedEvents & SUPPRESS_MAXIMIZE)) { bool fs = m_pXDGSurface->toplevel->state.requestsMaximize.value(); if (fs != m_bIsFullscreen && m_pXDGSurface->mapped) From 49485ba36a2d246c6fd3b32e3d4ad94f5c115ed5 Mon Sep 17 00:00:00 2001 From: Vaxry Date: Fri, 17 May 2024 20:04:17 +0100 Subject: [PATCH 47/57] pointer: damage in software mode on cursor image changes fixes #6126 --- src/managers/PointerManager.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/managers/PointerManager.cpp b/src/managers/PointerManager.cpp index d565ad6d..4be91b80 100644 --- a/src/managers/PointerManager.cpp +++ b/src/managers/PointerManager.cpp @@ -169,11 +169,13 @@ SP CPointerManager::stateFor(SP } void CPointerManager::setCursorBuffer(wlr_buffer* buf, const Vector2D& hotspot, const float& scale) { + damageIfSoftware(); if (buf == currentCursorImage.pBuffer) { if (hotspot != currentCursorImage.hotspot || scale != currentCursorImage.scale) { currentCursorImage.hotspot = hotspot; currentCursorImage.scale = scale; updateCursorBackend(); + damageIfSoftware(); } return; @@ -193,14 +195,18 @@ void CPointerManager::setCursorBuffer(wlr_buffer* buf, const Vector2D& hotspot, currentCursorImage.scale = scale; updateCursorBackend(); + damageIfSoftware(); } void CPointerManager::setCursorSurface(CWLSurface* surf, const Vector2D& hotspot) { + damageIfSoftware(); + if (surf == currentCursorImage.surface) { if (hotspot != currentCursorImage.hotspot || (surf && surf->wlr() ? surf->wlr()->current.scale : 1.F) != currentCursorImage.scale) { currentCursorImage.hotspot = hotspot; currentCursorImage.scale = surf && surf->wlr() ? surf->wlr()->current.scale : 1.F; updateCursorBackend(); + damageIfSoftware(); } return; @@ -217,10 +223,12 @@ void CPointerManager::setCursorSurface(CWLSurface* surf, const Vector2D& hotspot currentCursorImage.hyprListener_commitSurface.initCallback( &surf->wlr()->events.commit, [this](void* owner, void* data) { + damageIfSoftware(); currentCursorImage.size = {currentCursorImage.surface->wlr()->current.buffer_width, currentCursorImage.surface->wlr()->current.buffer_height}; currentCursorImage.scale = currentCursorImage.surface && currentCursorImage.surface->wlr() ? currentCursorImage.surface->wlr()->current.scale : 1.F; recheckEnteredOutputs(); updateCursorBackend(); + damageIfSoftware(); }, nullptr, "CPointerManager"); @@ -235,6 +243,7 @@ void CPointerManager::setCursorSurface(CWLSurface* surf, const Vector2D& hotspot recheckEnteredOutputs(); updateCursorBackend(); + damageIfSoftware(); } void CPointerManager::recheckEnteredOutputs() { @@ -276,6 +285,8 @@ void CPointerManager::recheckEnteredOutputs() { } void CPointerManager::resetCursorImage(bool apply) { + damageIfSoftware(); + if (currentCursorImage.surface) { for (auto& m : g_pCompositor->m_vMonitors) { wlr_surface_send_leave(currentCursorImage.surface->wlr(), m->output); From 2ead1fd22103ce065661555513bace5897083ded Mon Sep 17 00:00:00 2001 From: Vaxry Date: Fri, 17 May 2024 20:07:33 +0100 Subject: [PATCH 48/57] virtual-keyboard: emit event before finishing keyboard ref #6123 --- src/protocols/VirtualKeyboard.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protocols/VirtualKeyboard.cpp b/src/protocols/VirtualKeyboard.cpp index 7325f7cd..89b14c32 100644 --- a/src/protocols/VirtualKeyboard.cpp +++ b/src/protocols/VirtualKeyboard.cpp @@ -87,8 +87,8 @@ CVirtualKeyboardV1Resource::CVirtualKeyboardV1Resource(SP } CVirtualKeyboardV1Resource::~CVirtualKeyboardV1Resource() { - wlr_keyboard_finish(&keyboard); events.destroy.emit(); + wlr_keyboard_finish(&keyboard); } bool CVirtualKeyboardV1Resource::good() { From c21a5a934061c248ff1cdd459a74a05b621fb427 Mon Sep 17 00:00:00 2001 From: Gabriel Ford Date: Sat, 18 May 2024 18:28:48 +0000 Subject: [PATCH 49/57] layout: Fix shrinking pseudotile windows. (#6143) --- src/layout/IHyprLayout.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layout/IHyprLayout.cpp b/src/layout/IHyprLayout.cpp index a31180e1..ffd9ddea 100644 --- a/src/layout/IHyprLayout.cpp +++ b/src/layout/IHyprLayout.cpp @@ -523,7 +523,7 @@ void IHyprLayout::changeWindowFloatingMode(PHLWINDOW pWindow) { CBox wb = {pWindow->m_vRealPosition.goal() + (pWindow->m_vRealSize.goal() - pWindow->m_vLastFloatingSize) / 2.f, pWindow->m_vLastFloatingSize}; wb.round(); - if (DELTALESSTHAN(pWindow->m_vRealSize.value().x, pWindow->m_vLastFloatingSize.x, 10) && + if (!(pWindow->m_bIsFloating && pWindow->m_bIsPseudotiled) && DELTALESSTHAN(pWindow->m_vRealSize.value().x, pWindow->m_vLastFloatingSize.x, 10) && DELTALESSTHAN(pWindow->m_vRealSize.value().y, pWindow->m_vLastFloatingSize.y, 10)) { wb = {wb.pos() + Vector2D{10, 10}, wb.size() - Vector2D{20, 20}}; } From f8857e6072bd85b95393499688872aaf7f088b5b Mon Sep 17 00:00:00 2001 From: Vaxry Date: Sat, 18 May 2024 21:20:01 +0100 Subject: [PATCH 50/57] input: find surface pos correctly when mouse drag is active fixes #6144 --- src/managers/input/InputManager.cpp | 35 ++++++++++++++++------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index e05520f4..f4f97236 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -222,26 +222,29 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { // if we are holding a pointer button, // and we're not dnd-ing, don't refocus. Keep focus on last surface. if (!PROTO::data->dndActive() && !m_lCurrentlyHeldButtons.empty() && g_pCompositor->m_pLastFocus && g_pSeatManager->state.pointerFocus && !m_bHardInput) { - foundSurface = g_pSeatManager->state.pointerFocus; - pFoundLayerSurface = g_pCompositor->getLayerSurfaceFromSurface(foundSurface); - if (pFoundLayerSurface) { - surfacePos = pFoundLayerSurface->position; + foundSurface = g_pSeatManager->state.pointerFocus; + + // IME popups aren't desktop-like elements + // TODO: make them. + CInputPopup* foundPopup = m_sIMERelay.popupFromSurface(foundSurface); + if (foundPopup) { + surfacePos = foundPopup->globalBox().pos(); m_bFocusHeldByButtons = true; m_bRefocusHeldByButtons = refocus; } else { - CInputPopup* foundPopup = m_sIMERelay.popupFromSurface(foundSurface); - if (foundPopup) { - surfacePos = foundPopup->globalBox().pos(); - m_bFocusHeldByButtons = true; - m_bRefocusHeldByButtons = refocus; - } else if (!g_pCompositor->m_pLastWindow.expired()) { - foundSurface = g_pSeatManager->state.pointerFocus; - pFoundWindow = g_pCompositor->m_pLastWindow.lock(); + auto HLSurface = CWLSurface::surfaceFromWlr(foundSurface); - surfaceCoords = g_pCompositor->vectorToSurfaceLocal(mouseCoords, pFoundWindow, foundSurface); - m_bFocusHeldByButtons = true; - m_bRefocusHeldByButtons = refocus; - } + if (HLSurface) { + const auto BOX = HLSurface->getSurfaceBoxGlobal(); + + if (BOX) { + surfacePos = BOX->pos(); + pFoundLayerSurface = HLSurface->getLayer(); + pFoundWindow = HLSurface->getWindow(); + } else // reset foundSurface, find one normally + foundSurface = nullptr; + } else // reset foundSurface, find one normally + foundSurface = nullptr; } } From baef55da1ddccf8ca1b831214bb96e523b600a28 Mon Sep 17 00:00:00 2001 From: vaxerski Date: Tue, 21 May 2024 14:50:33 +0200 Subject: [PATCH 51/57] xdg-shell: fixup positioner behavior with slide and resize if sliding and resizing, include the slide in the resize to avoid off-screen surfaces. fixes #6150 --- src/protocols/XDGShell.cpp | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/protocols/XDGShell.cpp b/src/protocols/XDGShell.cpp index 9777281f..cbb93de9 100644 --- a/src/protocols/XDGShell.cpp +++ b/src/protocols/XDGShell.cpp @@ -550,6 +550,14 @@ CBox CXDGPositionerRules::getPosition(const CBox& constraint, const Vector2D& pa return test.translate(-parentCoord - constraint.pos()); } + // if flips fail, we will slide and remember. + // if the positioner is allowed to resize, then resize the slid thing. + CBox test = predictedBox; + + // for slide and resize, defines the padding around the edge for the positioned + // surface. + constexpr int EDGE_PADDING = 4; + if (state.constraintAdjustment & (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y)) { // attempt to slide const bool slideX = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X; @@ -563,17 +571,15 @@ CBox CXDGPositionerRules::getPosition(const CBox& constraint, const Vector2D& pa const bool rightEdgeOut = predictedBox.x + predictedBox.w > constraint.x + constraint.w; const bool bottomEdgeOut = predictedBox.y + predictedBox.h > constraint.y + constraint.h; - CBox test = predictedBox; - // TODO: this isn't truly conformant. if (leftEdgeOut && slideX) - test.x = constraint.x; + test.x = constraint.x + EDGE_PADDING; if (rightEdgeOut && slideX) - test.x = constraint.x + constraint.w - predictedBox.w; + test.x = constraint.x + constraint.w - predictedBox.w - EDGE_PADDING; if (topEdgeOut && slideY) - test.y = constraint.y; + test.y = constraint.y + EDGE_PADDING; if (bottomEdgeOut && slideY) - test.y = constraint.y + constraint.h - predictedBox.y; + test.y = constraint.y + constraint.h - predictedBox.y - EDGE_PADDING; success = test.copy().expand(-1).inside(constraint); @@ -590,21 +596,19 @@ CBox CXDGPositionerRules::getPosition(const CBox& constraint, const Vector2D& pa const bool rightEdgeOut = predictedBox.x + predictedBox.w > constraint.x + constraint.w; const bool bottomEdgeOut = predictedBox.y + predictedBox.h > constraint.y + constraint.h; - CBox test = predictedBox; - // TODO: this isn't truly conformant. if (leftEdgeOut && resizeX) { - test.w = test.x + test.w - constraint.x; - test.x = constraint.x; + test.w = test.x + test.w - constraint.x - EDGE_PADDING; + test.x = constraint.x + EDGE_PADDING; } if (rightEdgeOut && resizeX) - test.w = -(constraint.w + constraint.x - test.w - test.x); + test.w = -(constraint.w + constraint.x - test.w - test.x + EDGE_PADDING); if (topEdgeOut && resizeY) { - test.h = test.y + test.h - constraint.y; - test.y = constraint.y; + test.h = test.y + test.h - constraint.y - EDGE_PADDING; + test.y = constraint.y + EDGE_PADDING; } if (bottomEdgeOut && resizeY) - test.h = -(constraint.h + constraint.y - test.h - test.y); + test.h = -(constraint.h + constraint.y - test.h - test.y + EDGE_PADDING); success = test.copy().expand(-1).inside(constraint); @@ -614,7 +618,7 @@ CBox CXDGPositionerRules::getPosition(const CBox& constraint, const Vector2D& pa LOGM(WARN, "Compositor/client bug: xdg_positioner couldn't find a place"); - return predictedBox.translate(-parentCoord - constraint.pos()); + return test.translate(-parentCoord - constraint.pos()); } CXDGWMBase::CXDGWMBase(SP resource_) : resource(resource_) { From 4daa5c06587390c5c6835275ccbfc645bacc0052 Mon Sep 17 00:00:00 2001 From: Mihai Fufezan Date: Mon, 20 May 2024 17:29:35 +0300 Subject: [PATCH 52/57] flake.lock: update --- flake.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/flake.lock b/flake.lock index ca05387b..eba1996a 100644 --- a/flake.lock +++ b/flake.lock @@ -84,11 +84,11 @@ ] }, "locked": { - "lastModified": 1715879663, - "narHash": "sha256-/DwglRvj4XF4ECdNtrCIbthleszAZBwOiXG5A6r0K/c=", + "lastModified": 1716058375, + "narHash": "sha256-CwjWoVnBZE5SBpRx9dgSQGCr4Goxyfcyv3zZbOhVqzk=", "owner": "hyprwm", "repo": "hyprwayland-scanner", - "rev": "f5181a068c1b06f2db51f6222e50a0c665a2b0c3", + "rev": "3afed4364790aebe0426077631af1e164a9650cc", "type": "github" }, "original": { @@ -99,11 +99,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1715787315, - "narHash": "sha256-cYApT0NXJfqBkKcci7D9Kr4CBYZKOQKDYA23q8XNuWg=", + "lastModified": 1716137900, + "narHash": "sha256-sowPU+tLQv8GlqtVtsXioTKeaQvlMz/pefcdwg8MvfM=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "33d1e753c82ffc557b4a585c77de43d4c922ebb5", + "rev": "6c0b7a92c30122196a761b440ac0d46d3d9954f1", "type": "github" }, "original": { @@ -152,11 +152,11 @@ ] }, "locked": { - "lastModified": 1715788457, - "narHash": "sha256-32HOkjSIyANphV0p5gIwP4ONU/CcinhwOyVFB+tL/d0=", + "lastModified": 1716290197, + "narHash": "sha256-1u9Exrc7yx9qtES2brDh7/DDZ8w8ap1nboIOAtCgeuM=", "owner": "hyprwm", "repo": "xdg-desktop-portal-hyprland", - "rev": "af7c87a32f5d67eb2ada908a6a700f4e74831943", + "rev": "91e48d6acd8a5a611d26f925e51559ab743bc438", "type": "github" }, "original": { From 3c907f783027328cf90fae45ff51978c1f123418 Mon Sep 17 00:00:00 2001 From: giskard Date: Thu, 16 May 2024 00:13:56 +0800 Subject: [PATCH 53/57] build: update meson, cmake setup - meson . fix run_command() check warning . drop lines for compatability, as it's already using c++23 - cmake . generate `compile_commands.json` by default . position independent build: __FILE__ --- CMakeLists.txt | 7 +++++-- meson.build | 21 ++++++--------------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 43927fbe..858502cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ project(Hyprland set(HYPRLAND_VERSION ${VER}) set(PREFIX ${CMAKE_INSTALL_PREFIX}) set(INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR}) -configure_file(hyprland.pc.in hyprland.pc @ONLY) +configure_file(hyprland.pc.in hyprland.pc @ONLY) set(CMAKE_MESSAGE_LOG_LEVEL "STATUS") @@ -90,9 +90,12 @@ include_directories( "protocols/") set(CMAKE_CXX_STANDARD 23) add_compile_definitions(WLR_USE_UNSTABLE) -add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-unused-value -Wno-missing-field-initializers -Wno-narrowing -Wno-pointer-arith) +add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-unused-value + -Wno-missing-field-initializers -Wno-narrowing -Wno-pointer-arith + -fmacro-prefix-map=${CMAKE_SOURCE_DIR}/=) set(CMAKE_EXECUTABLE_ENABLE_EXPORTS TRUE) +set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) message(STATUS "Checking deps...") diff --git a/meson.build b/meson.build index 9b278198..b7b23470 100644 --- a/meson.build +++ b/meson.build @@ -5,20 +5,9 @@ project('Hyprland', 'cpp', 'c', 'default_library=static', 'optimization=3', 'buildtype=release', - 'debug=false' - # 'cpp_std=c++23' # not yet supported by meson, as of version 0.63.0 - ]) - -# clang v14.0.6 uses C++2b instead of C++23, so we've gotta account for that -# replace the following with a project default option once meson gets support for C++23 -cpp_compiler = meson.get_compiler('cpp') -if cpp_compiler.has_argument('-std=c++23') - add_global_arguments('-std=c++23', language: 'cpp') -elif cpp_compiler.has_argument('-std=c++2b') - add_global_arguments('-std=c++2b', language: 'cpp') -else - error('Could not configure current C++ compiler (' + cpp_compiler.get_id() + ' ' + cpp_compiler.version() + ') with required C++ standard (C++23)') -endif + 'debug=false', + 'cpp_std=c++23', + ]) add_project_arguments( [ @@ -26,9 +15,11 @@ add_project_arguments( '-Wno-unused-value', '-Wno-missing-field-initializers', '-Wno-narrowing', + '-Wno-pointer-arith', ], language: 'cpp') +cpp_compiler = meson.get_compiler('cpp') if cpp_compiler.check_header('execinfo.h') add_project_arguments('-DHAS_EXECINFO', language: 'cpp') endif @@ -65,7 +56,7 @@ if get_option('buildtype') == 'debug' add_project_arguments('-DHYPRLAND_DEBUG', language: 'cpp') endif -version_h = run_command('sh', '-c', 'scripts/generateVersion.sh') +version_h = run_command('sh', '-c', 'scripts/generateVersion.sh', check: true) globber = run_command('find', 'src', '-name', '*.h*', check: true) headers = globber.stdout().strip().split('\n') From e419ef1873de01b0762f7f1a411994170a4d8cab Mon Sep 17 00:00:00 2001 From: Mihai Fufezan Date: Tue, 21 May 2024 21:29:56 +0300 Subject: [PATCH 54/57] Revert "CMake: use add_custom_command for generating protocols (#6104)" Fixes https://github.com/hyprwm/Hyprland/issues/6115. --- CMakeLists.txt | 118 +++++++++++++++++++++++-------------------------- 1 file changed, 55 insertions(+), 63 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 858502cc..40e0f32a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -113,11 +113,9 @@ pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-server wayland-client wayland-cursor wayland-protocols cairo pango pangocairo pixman-1 libdrm libinput hwdata libseat libdisplay-info libliftoff libudev gbm - hyprlang>=0.3.2 hyprcursor>=0.1.7 + hyprwayland-scanner>=0.3.8 hyprlang>=0.3.2 hyprcursor>=0.1.7 ) -find_package(hyprwayland-scanner 0.3.8 REQUIRED) - file(GLOB_RECURSE SRCFILES "src/*.cpp") set(TRACY_CPP_FILES "") @@ -221,48 +219,42 @@ target_link_libraries(Hyprland rt PkgConfig::deps) function(protocol protoPath protoName external) if (external) - set(path ${CMAKE_SOURCE_DIR}/${protoPath}) + execute_process( + COMMAND ${WaylandScanner} server-header ${protoPath} protocols/${protoName}-protocol.h + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + execute_process( + COMMAND ${WaylandScanner} private-code ${protoPath} protocols/${protoName}-protocol.c + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + target_sources(Hyprland PRIVATE protocols/${protoName}-protocol.c) else() - set(path ${WAYLAND_PROTOCOLS_DIR}/${protoPath}) + execute_process( + COMMAND ${WaylandScanner} server-header ${WAYLAND_PROTOCOLS_DIR}/${protoPath} protocols/${protoName}-protocol.h + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + execute_process( + COMMAND ${WaylandScanner} private-code ${WAYLAND_PROTOCOLS_DIR}/${protoPath} protocols/${protoName}-protocol.c + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + target_sources(Hyprland PRIVATE protocols/${protoName}-protocol.c) endif() - - add_custom_command( - OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.h - COMMAND ${WaylandScanner} server-header ${path} protocols/${protoName}-protocol.h - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - ) - add_custom_command( - OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.c - COMMAND ${WaylandScanner} private-code ${path} protocols/${protoName}-protocol.c - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - ) - target_sources(Hyprland PRIVATE ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.h) - target_sources(Hyprland PRIVATE ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.c) endfunction() function(protocolNew protoPath protoName external) if (external) - set(path ${CMAKE_SOURCE_DIR}/${protoPath}) + execute_process( + COMMAND hyprwayland-scanner ${protoPath} ${CMAKE_SOURCE_DIR}/protocols/ + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + target_sources(Hyprland PRIVATE protocols/${protoName}.cpp) else() - set(path ${WAYLAND_PROTOCOLS_DIR}/${protoPath}) + execute_process( + COMMAND hyprwayland-scanner ${WAYLAND_PROTOCOLS_DIR}/${protoPath} ${CMAKE_SOURCE_DIR}/protocols/ + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + target_sources(Hyprland PRIVATE protocols/${protoName}.cpp) endif() - add_custom_command( - OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}.cpp - ${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp - COMMAND hyprwayland-scanner ${path}/${protoName}.xml ${CMAKE_SOURCE_DIR}/protocols/ - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - ) - target_sources(Hyprland PRIVATE protocols/${protoName}.cpp) - target_sources(Hyprland PRIVATE protocols/${protoName}.hpp) endfunction() function(protocolWayland) - add_custom_command( - OUTPUT ${CMAKE_SOURCE_DIR}/protocols/wayland.cpp - ${CMAKE_SOURCE_DIR}/protocols/wayland.hpp + execute_process( COMMAND hyprwayland-scanner --wayland-enums ${WAYLAND_SERVER_DIR}/wayland.xml ${CMAKE_SOURCE_DIR}/protocols/ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) target_sources(Hyprland PRIVATE protocols/wayland.cpp) - target_sources(Hyprland PRIVATE protocols/wayland.hpp) endfunction() target_link_libraries(Hyprland @@ -280,37 +272,37 @@ protocol("subprojects/hyprland-protocols/protocols/hyprland-toplevel-export-v1.x protocol("unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml" "linux-dmabuf-unstable-v1" false) protocol("unstable/text-input/text-input-unstable-v1.xml" "text-input-unstable-v1" false) -protocolNew("protocols" "wlr-gamma-control-unstable-v1" true) -protocolNew("protocols" "wlr-foreign-toplevel-management-unstable-v1" true) -protocolNew("protocols" "wlr-output-power-management-unstable-v1" true) -protocolNew("protocols" "virtual-keyboard-unstable-v1" true) -protocolNew("protocols" "wlr-virtual-pointer-unstable-v1" true) -protocolNew("protocols" "input-method-unstable-v2" true) -protocolNew("protocols" "wlr-output-management-unstable-v1" true) -protocolNew("protocols" "kde-server-decoration" true) -protocolNew("protocols" "wlr-data-control-unstable-v1" true) -protocolNew("subprojects/hyprland-protocols/protocols" "hyprland-focus-grab-v1" true) -protocolNew("protocols" "wlr-layer-shell-unstable-v1" true) -protocolNew("staging/tearing-control" "tearing-control-v1" false) -protocolNew("staging/fractional-scale" "fractional-scale-v1" false) -protocolNew("unstable/xdg-output" "xdg-output-unstable-v1" false) -protocolNew("staging/cursor-shape" "cursor-shape-v1" false) -protocolNew("unstable/idle-inhibit" "idle-inhibit-unstable-v1" false) -protocolNew("unstable/relative-pointer" "relative-pointer-unstable-v1" false) -protocolNew("unstable/xdg-decoration" "xdg-decoration-unstable-v1" false) -protocolNew("staging/alpha-modifier" "alpha-modifier-v1" false) -protocolNew("staging/ext-foreign-toplevel-list" "ext-foreign-toplevel-list-v1" false) -protocolNew("unstable/pointer-gestures" "pointer-gestures-unstable-v1" false) -protocolNew("unstable/keyboard-shortcuts-inhibit" "keyboard-shortcuts-inhibit-unstable-v1" false) -protocolNew("unstable/text-input" "text-input-unstable-v3" false) -protocolNew("unstable/pointer-constraints" "pointer-constraints-unstable-v1" false) -protocolNew("staging/xdg-activation" "xdg-activation-v1" false) -protocolNew("staging/ext-idle-notify" "ext-idle-notify-v1" false) -protocolNew("staging/ext-session-lock" "ext-session-lock-v1" false) -protocolNew("stable/tablet" "tablet-v2" false) -protocolNew("stable/presentation-time" "presentation-time" false) -protocolNew("stable/xdg-shell" "xdg-shell" false) -protocolNew("unstable/primary-selection" "primary-selection-unstable-v1" false) +protocolNew("protocols/wlr-gamma-control-unstable-v1.xml" "wlr-gamma-control-unstable-v1" true) +protocolNew("protocols/wlr-foreign-toplevel-management-unstable-v1.xml" "wlr-foreign-toplevel-management-unstable-v1" true) +protocolNew("protocols/wlr-output-power-management-unstable-v1.xml" "wlr-output-power-management-unstable-v1" true) +protocolNew("protocols/virtual-keyboard-unstable-v1.xml" "virtual-keyboard-unstable-v1" true) +protocolNew("protocols/wlr-virtual-pointer-unstable-v1.xml" "wlr-virtual-pointer-unstable-v1" true) +protocolNew("protocols/input-method-unstable-v2.xml" "input-method-unstable-v2" true) +protocolNew("protocols/wlr-output-management-unstable-v1.xml" "wlr-output-management-unstable-v1" true) +protocolNew("protocols/kde-server-decoration.xml" "kde-server-decoration" true) +protocolNew("protocols/wlr-data-control-unstable-v1.xml" "wlr-data-control-unstable-v1" true) +protocolNew("subprojects/hyprland-protocols/protocols/hyprland-focus-grab-v1.xml" "hyprland-focus-grab-v1" true) +protocolNew("protocols/wlr-layer-shell-unstable-v1.xml" "wlr-layer-shell-unstable-v1" true) +protocolNew("staging/tearing-control/tearing-control-v1.xml" "tearing-control-v1" false) +protocolNew("staging/fractional-scale/fractional-scale-v1.xml" "fractional-scale-v1" false) +protocolNew("unstable/xdg-output/xdg-output-unstable-v1.xml" "xdg-output-unstable-v1" false) +protocolNew("staging/cursor-shape/cursor-shape-v1.xml" "cursor-shape-v1" false) +protocolNew("unstable/idle-inhibit/idle-inhibit-unstable-v1.xml" "idle-inhibit-unstable-v1" false) +protocolNew("unstable/relative-pointer/relative-pointer-unstable-v1.xml" "relative-pointer-unstable-v1" false) +protocolNew("unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" "xdg-decoration-unstable-v1" false) +protocolNew("staging/alpha-modifier/alpha-modifier-v1.xml" "alpha-modifier-v1" false) +protocolNew("staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml" "ext-foreign-toplevel-list-v1" false) +protocolNew("unstable/pointer-gestures/pointer-gestures-unstable-v1.xml" "pointer-gestures-unstable-v1" false) +protocolNew("unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml" "keyboard-shortcuts-inhibit-unstable-v1" false) +protocolNew("unstable/text-input/text-input-unstable-v3.xml" "text-input-unstable-v3" false) +protocolNew("unstable/pointer-constraints/pointer-constraints-unstable-v1.xml" "pointer-constraints-unstable-v1" false) +protocolNew("staging/xdg-activation/xdg-activation-v1.xml" "xdg-activation-v1" false) +protocolNew("staging/ext-idle-notify/ext-idle-notify-v1.xml" "ext-idle-notify-v1" false) +protocolNew("staging/ext-session-lock/ext-session-lock-v1.xml" "ext-session-lock-v1" false) +protocolNew("stable/tablet/tablet-v2.xml" "tablet-v2" false) +protocolNew("stable/presentation-time/presentation-time.xml" "presentation-time" false) +protocolNew("stable/xdg-shell/xdg-shell.xml" "xdg-shell" false) +protocolNew("unstable/primary-selection/primary-selection-unstable-v1.xml" "primary-selection-unstable-v1" false) protocolWayland() From 93fea890433ec11d7a915f5e0466b2e8b513e895 Mon Sep 17 00:00:00 2001 From: giskard Date: Wed, 22 May 2024 16:09:36 +0800 Subject: [PATCH 55/57] renderer: render fonts with pango, add global `font_family` config option (#6138) --- src/config/ConfigManager.cpp | 5 +- src/debug/HyprDebugOverlay.cpp | 107 ++++++++--------- src/debug/HyprNotificationOverlay.cpp | 109 +++++++++--------- src/debug/HyprNotificationOverlay.hpp | 3 - src/hyprerror/HyprError.cpp | 25 +++- src/render/OpenGL.cpp | 37 +++--- .../decorations/CHyprGroupBarDecoration.cpp | 7 +- 7 files changed, 157 insertions(+), 136 deletions(-) diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 3dc36899..14a326bb 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -324,7 +324,8 @@ CConfigManager::CConfigManager() { m_pConfig->addConfigValue("misc:disable_hyprland_logo", Hyprlang::INT{0}); m_pConfig->addConfigValue("misc:disable_splash_rendering", Hyprlang::INT{0}); m_pConfig->addConfigValue("misc:col.splash", Hyprlang::INT{0x55ffffff}); - m_pConfig->addConfigValue("misc:splash_font_family", {"Sans"}); + m_pConfig->addConfigValue("misc:splash_font_family", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("misc:font_family", {"Sans"}); m_pConfig->addConfigValue("misc:force_default_wallpaper", Hyprlang::INT{-1}); m_pConfig->addConfigValue("misc:vfr", Hyprlang::INT{1}); m_pConfig->addConfigValue("misc:vrr", Hyprlang::INT{0}); @@ -353,7 +354,7 @@ CConfigManager::CConfigManager() { m_pConfig->addConfigValue("group:insert_after_current", Hyprlang::INT{1}); m_pConfig->addConfigValue("group:focus_removed_window", Hyprlang::INT{1}); m_pConfig->addConfigValue("group:groupbar:enabled", Hyprlang::INT{1}); - m_pConfig->addConfigValue("group:groupbar:font_family", {"Sans"}); + m_pConfig->addConfigValue("group:groupbar:font_family", {STRVAL_EMPTY}); m_pConfig->addConfigValue("group:groupbar:font_size", Hyprlang::INT{8}); m_pConfig->addConfigValue("group:groupbar:gradients", Hyprlang::INT{1}); m_pConfig->addConfigValue("group:groupbar:height", Hyprlang::INT{14}); diff --git a/src/debug/HyprDebugOverlay.cpp b/src/debug/HyprDebugOverlay.cpp index 37ccc67a..6d3ec907 100644 --- a/src/debug/HyprDebugOverlay.cpp +++ b/src/debug/HyprDebugOverlay.cpp @@ -1,4 +1,6 @@ +#include #include "HyprDebugOverlay.hpp" +#include "config/ConfigValue.hpp" #include "../Compositor.hpp" void CHyprMonitorDebugOverlay::renderData(CMonitor* pMonitor, float µs) { @@ -47,11 +49,6 @@ int CHyprMonitorDebugOverlay::draw(int offset) { if (!m_pMonitor) return 0; - int yOffset = offset; - cairo_text_extents_t cairoExtents; - float maxX = 0; - std::string text = ""; - // get avg fps float avgFrametime = 0; float maxFrametime = 0; @@ -105,23 +102,49 @@ int CHyprMonitorDebugOverlay::draw(int offset) { float varAnimMgrTick = maxAnimMgrTick - minAnimMgrTick; avgAnimMgrTick /= m_dLastAnimationTicks.size() == 0 ? 1 : m_dLastAnimationTicks.size(); - const float FPS = 1.f / (avgFrametime / 1000.f); // frametimes are in ms - const float idealFPS = m_dLastFrametimes.size(); + const float FPS = 1.f / (avgFrametime / 1000.f); // frametimes are in ms + const float idealFPS = m_dLastFrametimes.size(); - cairo_select_font_face(g_pDebugOverlay->m_pCairo, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + static auto fontFamily = CConfigValue("misc:font_family"); + PangoLayout* layoutText = pango_cairo_create_layout(g_pDebugOverlay->m_pCairo); + PangoFontDescription* pangoFD = pango_font_description_new(); - cairo_set_font_size(g_pDebugOverlay->m_pCairo, 10); + pango_font_description_set_family(pangoFD, (*fontFamily).c_str()); + pango_font_description_set_style(pangoFD, PANGO_STYLE_NORMAL); + pango_font_description_set_weight(pangoFD, PANGO_WEIGHT_NORMAL); + + float maxTextW = 0; + int fontSize = 0; + auto cr = g_pDebugOverlay->m_pCairo; + + auto showText = [cr, layoutText, pangoFD, &maxTextW, &fontSize](const char* text, int size) { + if (fontSize != size) { + pango_font_description_set_absolute_size(pangoFD, size * PANGO_SCALE); + pango_layout_set_font_description(layoutText, pangoFD); + fontSize = size; + } + + pango_layout_set_text(layoutText, text, -1); + pango_cairo_show_layout(cr, layoutText); + + int textW = 0, textH = 0; + pango_layout_get_size(layoutText, &textW, &textH); + textW /= PANGO_SCALE; + textH /= PANGO_SCALE; + if (textW > maxTextW) + maxTextW = textW; + + // move to next line + cairo_rel_move_to(cr, 0, fontSize + 1); + }; + + const int MARGIN_TOP = 8; + const int MARGIN_LEFT = 4; + cairo_move_to(cr, MARGIN_LEFT, MARGIN_TOP + offset); cairo_set_source_rgba(g_pDebugOverlay->m_pCairo, 1.f, 1.f, 1.f, 1.f); - yOffset += 10; - cairo_move_to(g_pDebugOverlay->m_pCairo, 0, yOffset); - text = m_pMonitor->szName; - cairo_show_text(g_pDebugOverlay->m_pCairo, text.c_str()); - cairo_text_extents(g_pDebugOverlay->m_pCairo, text.c_str(), &cairoExtents); - if (cairoExtents.width > maxX) - maxX = cairoExtents.width; - - cairo_set_font_size(g_pDebugOverlay->m_pCairo, 16); + std::string text; + showText(m_pMonitor->szName.c_str(), 10); if (FPS > idealFPS * 0.95f) cairo_set_source_rgba(g_pDebugOverlay->m_pCairo, 0.2f, 1.f, 0.2f, 1.f); @@ -130,57 +153,35 @@ int CHyprMonitorDebugOverlay::draw(int offset) { else cairo_set_source_rgba(g_pDebugOverlay->m_pCairo, 1.f, 0.2f, 0.2f, 1.f); - yOffset += 17; - cairo_move_to(g_pDebugOverlay->m_pCairo, 0, yOffset); text = std::format("{} FPS", (int)FPS); - cairo_show_text(g_pDebugOverlay->m_pCairo, text.c_str()); - cairo_text_extents(g_pDebugOverlay->m_pCairo, text.c_str(), &cairoExtents); - if (cairoExtents.width > maxX) - maxX = cairoExtents.width; + showText(text.c_str(), 16); - cairo_set_font_size(g_pDebugOverlay->m_pCairo, 10); cairo_set_source_rgba(g_pDebugOverlay->m_pCairo, 1.f, 1.f, 1.f, 1.f); - yOffset += 11; - cairo_move_to(g_pDebugOverlay->m_pCairo, 0, yOffset); text = std::format("Avg Frametime: {:.2f}ms (var {:.2f}ms)", avgFrametime, varFrametime); - cairo_show_text(g_pDebugOverlay->m_pCairo, text.c_str()); - cairo_text_extents(g_pDebugOverlay->m_pCairo, text.c_str(), &cairoExtents); - if (cairoExtents.width > maxX) - maxX = cairoExtents.width; + showText(text.c_str(), 10); - yOffset += 11; - cairo_move_to(g_pDebugOverlay->m_pCairo, 0, yOffset); text = std::format("Avg Rendertime: {:.2f}ms (var {:.2f}ms)", avgRenderTime, varRenderTime); - cairo_show_text(g_pDebugOverlay->m_pCairo, text.c_str()); - cairo_text_extents(g_pDebugOverlay->m_pCairo, text.c_str(), &cairoExtents); - if (cairoExtents.width > maxX) - maxX = cairoExtents.width; + showText(text.c_str(), 10); - yOffset += 11; - cairo_move_to(g_pDebugOverlay->m_pCairo, 0, yOffset); text = std::format("Avg Rendertime (No Overlay): {:.2f}ms (var {:.2f}ms)", avgRenderTimeNoOverlay, varRenderTimeNoOverlay); - cairo_show_text(g_pDebugOverlay->m_pCairo, text.c_str()); - cairo_text_extents(g_pDebugOverlay->m_pCairo, text.c_str(), &cairoExtents); - if (cairoExtents.width > maxX) - maxX = cairoExtents.width; + showText(text.c_str(), 10); - yOffset += 11; - cairo_move_to(g_pDebugOverlay->m_pCairo, 0, yOffset); text = std::format("Avg Anim Tick: {:.2f}ms (var {:.2f}ms) ({:.2f} TPS)", avgAnimMgrTick, varAnimMgrTick, 1.0 / (avgAnimMgrTick / 1000.0)); - cairo_show_text(g_pDebugOverlay->m_pCairo, text.c_str()); - cairo_text_extents(g_pDebugOverlay->m_pCairo, text.c_str(), &cairoExtents); - if (cairoExtents.width > maxX) - maxX = cairoExtents.width; + showText(text.c_str(), 10); - yOffset += 11; + pango_font_description_free(pangoFD); + g_object_unref(layoutText); + + double posX = 0, posY = 0; + cairo_get_current_point(cr, &posX, &posY); g_pHyprRenderer->damageBox(&m_wbLastDrawnBox); - m_wbLastDrawnBox = {(int)g_pCompositor->m_vMonitors.front()->vecPosition.x, (int)g_pCompositor->m_vMonitors.front()->vecPosition.y + offset - 1, (int)maxX + 2, - yOffset - offset + 2}; + m_wbLastDrawnBox = {(int)g_pCompositor->m_vMonitors.front()->vecPosition.x + MARGIN_LEFT - 1, (int)g_pCompositor->m_vMonitors.front()->vecPosition.y + offset + MARGIN_TOP - 1, + (int)maxTextW + 2, posY - offset - MARGIN_TOP + 2}; g_pHyprRenderer->damageBox(&m_wbLastDrawnBox); - return yOffset - offset; + return posY - offset; } void CHyprDebugOverlay::renderData(CMonitor* pMonitor, float µs) { diff --git a/src/debug/HyprNotificationOverlay.cpp b/src/debug/HyprNotificationOverlay.cpp index 80c80601..e1fc810b 100644 --- a/src/debug/HyprNotificationOverlay.cpp +++ b/src/debug/HyprNotificationOverlay.cpp @@ -1,6 +1,20 @@ +#include +#include #include "HyprNotificationOverlay.hpp" #include "../Compositor.hpp" -#include +#include "../config/ConfigValue.hpp" + +inline auto iconBackendFromLayout(PangoLayout* layout) { + // preference: Nerd > FontAwesome > text + auto eIconBackendChecks = std::array{ICONS_BACKEND_NF, ICONS_BACKEND_FA}; + for (auto iconID : eIconBackendChecks) { + auto iconsText = std::accumulate(ICONS_ARRAY[iconID].begin(), ICONS_ARRAY[iconID].end(), std::string()); + pango_layout_set_text(layout, iconsText.c_str(), -1); + if (pango_layout_get_unknown_glyphs_count(layout) == 0) + return iconID; + } + return ICONS_BACKEND_NONE; +} CHyprNotificationOverlay::CHyprNotificationOverlay() { static auto P = g_pHookSystem->hookDynamic("focusedMon", [&](void* self, SCallbackInfo& info, std::any param) { @@ -9,31 +23,6 @@ CHyprNotificationOverlay::CHyprNotificationOverlay() { g_pHyprRenderer->damageBox(&m_bLastDamage); }); - - // check for the icon backend - std::string fonts = execAndGet("fc-list"); - std::string fontsLower = fonts; - std::transform(fontsLower.begin(), fontsLower.end(), fontsLower.begin(), [&](char& i) { return std::tolower(i); }); - - size_t index = 0; - - if (index = fontsLower.find("nerd"); index != std::string::npos) { - m_eIconBackend = ICONS_BACKEND_NF; - } else if (index = fontsLower.find("font awesome"); index != std::string::npos) { - m_eIconBackend = ICONS_BACKEND_FA; - } else if (index = fontsLower.find("fontawesome"); index != std::string::npos) { - m_eIconBackend = ICONS_BACKEND_FA; - } else { - return; - } - - const auto LASTNEWLINE = fonts.find_last_of('\n', index); - const auto COLON = fonts.find(':', LASTNEWLINE); - const auto COMMA = fonts.find(',', COLON); - const auto NEWLINE = fonts.find('\n', COLON); - const auto LASTCHAR = COMMA < NEWLINE ? COMMA : NEWLINE; - - m_szIconFontName = fonts.substr(COLON + 2, LASTCHAR - (COLON + 2)); } CHyprNotificationOverlay::~CHyprNotificationOverlay() { @@ -81,25 +70,24 @@ CBox CHyprNotificationOverlay::drawNotifications(CMonitor* pMonitor) { float offsetY = 10; float maxWidth = 0; - const auto SCALE = pMonitor->scale; - + const auto SCALE = pMonitor->scale; const auto MONSIZE = pMonitor->vecTransformedSize; - cairo_text_extents_t cairoExtents; - int iconW = 0, iconH = 0; + static auto fontFamily = CConfigValue("misc:font_family"); - cairo_select_font_face(m_pCairo, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + PangoLayout* layout = pango_cairo_create_layout(m_pCairo); + PangoFontDescription* pangoFD = pango_font_description_new(); - const auto PBEZIER = g_pAnimationManager->getBezier("default"); + pango_font_description_set_family(pangoFD, (*fontFamily).c_str()); + pango_font_description_set_style(pangoFD, PANGO_STYLE_NORMAL); + pango_font_description_set_weight(pangoFD, PANGO_WEIGHT_NORMAL); + + const auto iconBackendID = iconBackendFromLayout(layout); + const auto PBEZIER = g_pAnimationManager->getBezier("default"); for (auto& notif : m_dNotifications) { - const auto ICONPADFORNOTIF = notif->icon == ICON_NONE ? 0 : ICON_PAD; - const auto FONTSIZE = std::clamp((int)(notif->fontSize * ((pMonitor->vecPixelSize.x * SCALE) / 1920.f)), 8, 40); - - PangoLayout* pangoLayout = pango_cairo_create_layout(m_pCairo); - PangoFontDescription* pangoFD = pango_font_description_from_string(("Sans " + std::to_string(FONTSIZE * ICON_SCALE)).c_str()); - pango_layout_set_font_description(pangoLayout, pangoFD); - cairo_set_font_size(m_pCairo, FONTSIZE); + const auto ICONPADFORNOTIF = notif->icon == ICON_NONE ? 0 : ICON_PAD; + const auto FONTSIZE = std::clamp((int)(notif->fontSize * ((pMonitor->vecPixelSize.x * SCALE) / 1920.f)), 8, 40); // first rect (bg, col) const float FIRSTRECTANIMP = @@ -122,31 +110,37 @@ CBox CHyprNotificationOverlay::drawNotifications(CMonitor* pMonitor) { const float THIRDRECTPERC = notif->started.getMillis() / notif->timeMs; // get text size - cairo_text_extents(m_pCairo, notif->text.c_str(), &cairoExtents); - const auto ICON = ICONS_ARRAY[m_eIconBackend][notif->icon]; + const auto ICON = ICONS_ARRAY[iconBackendID][notif->icon]; const auto ICONCOLOR = ICONS_COLORS[notif->icon]; - pango_layout_set_text(pangoLayout, ICON.c_str(), -1); - pango_layout_set_font_description(pangoLayout, pangoFD); - pango_cairo_update_layout(m_pCairo, pangoLayout); - pango_layout_get_size(pangoLayout, &iconW, &iconH); + + int iconW = 0, iconH = 0; + pango_font_description_set_absolute_size(pangoFD, PANGO_SCALE * FONTSIZE * ICON_SCALE); + pango_layout_set_font_description(layout, pangoFD); + pango_layout_set_text(layout, ICON.c_str(), -1); + pango_layout_get_size(layout, &iconW, &iconH); iconW /= PANGO_SCALE; iconH /= PANGO_SCALE; - cairo_set_source_rgba(m_pCairo, notif->color.r, notif->color.g, notif->color.b, notif->color.a); + int textW = 0, textH = 0; + pango_font_description_set_absolute_size(pangoFD, PANGO_SCALE * FONTSIZE); + pango_layout_set_font_description(layout, pangoFD); + pango_layout_set_text(layout, notif->text.c_str(), -1); + pango_layout_get_size(layout, &textW, &textH); + textW /= PANGO_SCALE; + textH /= PANGO_SCALE; - const auto NOTIFSIZE = Vector2D{cairoExtents.width + 20 + iconW + 2 * ICONPADFORNOTIF, cairoExtents.height + 10}; + const auto NOTIFSIZE = Vector2D{textW + 20 + iconW + 2 * ICONPADFORNOTIF, textH + 10}; // draw rects + cairo_set_source_rgba(m_pCairo, notif->color.r, notif->color.g, notif->color.b, notif->color.a); cairo_rectangle(m_pCairo, MONSIZE.x - (NOTIFSIZE.x + NOTIF_LEFTBAR_SIZE) * FIRSTRECTPERC, offsetY, (NOTIFSIZE.x + NOTIF_LEFTBAR_SIZE) * FIRSTRECTPERC, NOTIFSIZE.y); cairo_fill(m_pCairo); cairo_set_source_rgb(m_pCairo, 0.f, 0.f, 0.f); - cairo_rectangle(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC, offsetY, NOTIFSIZE.x * SECONDRECTPERC, NOTIFSIZE.y); cairo_fill(m_pCairo); cairo_set_source_rgba(m_pCairo, notif->color.r, notif->color.g, notif->color.b, notif->color.a); - cairo_rectangle(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + 3, offsetY + NOTIFSIZE.y - 4, THIRDRECTPERC * (NOTIFSIZE.x - 6), 2); cairo_fill(m_pCairo); @@ -164,26 +158,27 @@ CBox CHyprNotificationOverlay::drawNotifications(CMonitor* pMonitor) { // draw icon cairo_set_source_rgb(m_pCairo, 1.f, 1.f, 1.f); - cairo_move_to(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + NOTIF_LEFTBAR_SIZE + ICONPADFORNOTIF - 1, offsetY + std::round((NOTIFSIZE.y - iconH - 4) / 2.0)); - pango_cairo_show_layout(m_pCairo, pangoLayout); + cairo_move_to(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + NOTIF_LEFTBAR_SIZE + ICONPADFORNOTIF - 1, offsetY - 2 + std::round((NOTIFSIZE.y - iconH) / 2.0)); + pango_layout_set_text(layout, ICON.c_str(), -1); + pango_cairo_show_layout(m_pCairo, layout); } // draw text - cairo_set_font_size(m_pCairo, FONTSIZE); cairo_set_source_rgb(m_pCairo, 1.f, 1.f, 1.f); - cairo_move_to(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + NOTIF_LEFTBAR_SIZE + iconW + 2 * ICONPADFORNOTIF, offsetY + FONTSIZE + (FONTSIZE / 10.0)); - cairo_show_text(m_pCairo, notif->text.c_str()); + cairo_move_to(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + NOTIF_LEFTBAR_SIZE + iconW + 2 * ICONPADFORNOTIF, offsetY - 2 + std::round((NOTIFSIZE.y - textH) / 2.0)); + pango_layout_set_text(layout, notif->text.c_str(), -1); + pango_cairo_show_layout(m_pCairo, layout); // adjust offset and move on offsetY += NOTIFSIZE.y + 10; if (maxWidth < NOTIFSIZE.x) maxWidth = NOTIFSIZE.x; - - pango_font_description_free(pangoFD); - g_object_unref(pangoLayout); } + pango_font_description_free(pangoFD); + g_object_unref(layout); + // cleanup notifs std::erase_if(m_dNotifications, [](const auto& notif) { return notif->started.getMillis() > notif->timeMs; }); diff --git a/src/debug/HyprNotificationOverlay.hpp b/src/debug/HyprNotificationOverlay.hpp index d086ada9..5c978089 100644 --- a/src/debug/HyprNotificationOverlay.hpp +++ b/src/debug/HyprNotificationOverlay.hpp @@ -59,9 +59,6 @@ class CHyprNotificationOverlay { Vector2D m_vecLastSize = Vector2D(-1, -1); CTexture m_tTexture; - - eIconBackend m_eIconBackend = ICONS_BACKEND_NONE; - std::string m_szIconFontName = "Sans"; }; inline std::unique_ptr g_pHyprNotificationOverlay; diff --git a/src/hyprerror/HyprError.cpp b/src/hyprerror/HyprError.cpp index e7e2ff0d..d147a6bb 100644 --- a/src/hyprerror/HyprError.cpp +++ b/src/hyprerror/HyprError.cpp @@ -1,3 +1,4 @@ +#include #include "HyprError.hpp" #include "../Compositor.hpp" #include "../config/ConfigValue.hpp" @@ -94,12 +95,19 @@ void CHyprError::createQueued() { // draw the text with a common font const CColor textColor = m_cQueued.r + m_cQueued.g + m_cQueued.b < 0.2f ? CColor(1.0, 1.0, 1.0, 1.0) : CColor(0, 0, 0, 1.0); - - cairo_select_font_face(CAIRO, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); - cairo_set_font_size(CAIRO, FONTSIZE); cairo_set_source_rgba(CAIRO, textColor.r, textColor.g, textColor.b, textColor.a); - float yoffset = TOPBAR ? FONTSIZE : Y - PAD + FONTSIZE; + static auto fontFamily = CConfigValue("misc:font_family"); + PangoLayout* layoutText = pango_cairo_create_layout(CAIRO); + PangoFontDescription* pangoFD = pango_font_description_new(); + + pango_font_description_set_family(pangoFD, (*fontFamily).c_str()); + pango_font_description_set_absolute_size(pangoFD, FONTSIZE * PANGO_SCALE); + pango_font_description_set_style(pangoFD, PANGO_STYLE_NORMAL); + pango_font_description_set_weight(pangoFD, PANGO_WEIGHT_NORMAL); + pango_layout_set_font_description(layoutText, pangoFD); + + float yoffset = TOPBAR ? 0 : Y - PAD; int renderedcnt = 0; while (!m_szQueued.empty() && renderedcnt < VISLINECOUNT) { std::string current = m_szQueued.substr(0, m_szQueued.find('\n')); @@ -108,17 +116,22 @@ void CHyprError::createQueued() { else m_szQueued = ""; cairo_move_to(CAIRO, PAD + 1 + RADIUS, yoffset + PAD + 1); - cairo_show_text(CAIRO, current.c_str()); + pango_layout_set_text(layoutText, current.c_str(), -1); + pango_cairo_show_layout(CAIRO, layoutText); yoffset += FONTSIZE + (FONTSIZE / 10.f); renderedcnt++; } if (VISLINECOUNT < LINECOUNT) { std::string moreString = std::format("({} more...)", LINECOUNT - VISLINECOUNT); cairo_move_to(CAIRO, PAD + 1 + RADIUS, yoffset + PAD + 1); - cairo_show_text(CAIRO, moreString.c_str()); + pango_layout_set_text(layoutText, moreString.c_str(), -1); + pango_cairo_show_layout(CAIRO, layoutText); } m_szQueued = ""; + pango_font_description_free(pangoFD); + g_object_unref(layoutText); + cairo_surface_flush(CAIROSURFACE); // copy the data to an OpenGL texture we have diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index a8a20f30..a1e6f73e 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -1,9 +1,9 @@ +#include +#include #include "Shaders.hpp" #include "OpenGL.hpp" #include "../Compositor.hpp" #include "../helpers/MiscFunctions.hpp" -#include "Shaders.hpp" -#include #include "../config/ConfigValue.hpp" #include "../desktop/LayerSurface.hpp" #include "../protocols/LayerShell.hpp" @@ -2097,25 +2097,36 @@ void CHyprOpenGLImpl::renderMirrored() { } void CHyprOpenGLImpl::renderSplash(cairo_t* const CAIRO, cairo_surface_t* const CAIROSURFACE, double offsetY, const Vector2D& size) { - static auto PSPLASHCOLOR = CConfigValue("misc:col.splash"); + static auto PSPLASHCOLOR = CConfigValue("misc:col.splash"); + static auto PSPLASHFONT = CConfigValue("misc:splash_font_family"); + static auto FALLBACKFONT = CConfigValue("misc:font_family"); - static auto PSPLASHFONT = CConfigValue("misc:splash_font_family"); + const auto FONTFAMILY = *PSPLASHFONT != STRVAL_EMPTY ? *PSPLASHFONT : *FALLBACKFONT; + const auto FONTSIZE = (int)(size.y / 76); + const auto COLOR = CColor(*PSPLASHCOLOR); - cairo_select_font_face(CAIRO, (*PSPLASHFONT).c_str(), CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + PangoLayout* layoutText = pango_cairo_create_layout(CAIRO); + PangoFontDescription* pangoFD = pango_font_description_new(); - const auto FONTSIZE = (int)(size.y / 76); - cairo_set_font_size(CAIRO, FONTSIZE); - - const auto COLOR = CColor(*PSPLASHCOLOR); + pango_font_description_set_family_static(pangoFD, FONTFAMILY.c_str()); + pango_font_description_set_absolute_size(pangoFD, FONTSIZE * PANGO_SCALE); + pango_font_description_set_style(pangoFD, PANGO_STYLE_NORMAL); + pango_font_description_set_weight(pangoFD, PANGO_WEIGHT_NORMAL); + pango_layout_set_font_description(layoutText, pangoFD); cairo_set_source_rgba(CAIRO, COLOR.r, COLOR.g, COLOR.b, COLOR.a); - cairo_text_extents_t textExtents; - cairo_text_extents(CAIRO, g_pCompositor->m_szCurrentSplash.c_str(), &textExtents); + int textW = 0, textH = 0; + pango_layout_set_text(layoutText, g_pCompositor->m_szCurrentSplash.c_str(), -1); + pango_layout_get_size(layoutText, &textW, &textH); + textW /= PANGO_SCALE; + textH /= PANGO_SCALE; - cairo_move_to(CAIRO, (size.x - textExtents.width) / 2.0, size.y - textExtents.height + offsetY); + cairo_move_to(CAIRO, (size.x - textW) / 2.0, size.y - textH * 2 + offsetY); + pango_cairo_show_layout(CAIRO, layoutText); - cairo_show_text(CAIRO, g_pCompositor->m_szCurrentSplash.c_str()); + pango_font_description_free(pangoFD); + g_object_unref(layoutText); cairo_surface_flush(CAIROSURFACE); } diff --git a/src/render/decorations/CHyprGroupBarDecoration.cpp b/src/render/decorations/CHyprGroupBarDecoration.cpp index 318999ed..04f69aaa 100644 --- a/src/render/decorations/CHyprGroupBarDecoration.cpp +++ b/src/render/decorations/CHyprGroupBarDecoration.cpp @@ -209,11 +209,13 @@ CTitleTex::CTitleTex(PHLWINDOW pWindow, const Vector2D& bufferSize, const float const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, bufferSize.x, bufferSize.y); const auto CAIRO = cairo_create(CAIROSURFACE); + static auto FALLBACKFONT = CConfigValue("misc:font_family"); static auto PTITLEFONTFAMILY = CConfigValue("group:groupbar:font_family"); static auto PTITLEFONTSIZE = CConfigValue("group:groupbar:font_size"); static auto PTEXTCOLOR = CConfigValue("group:groupbar:text_color"); - const CColor COLOR = CColor(*PTEXTCOLOR); + const CColor COLOR = CColor(*PTEXTCOLOR); + const auto FONTFAMILY = *PTITLEFONTFAMILY != STRVAL_EMPTY ? *PTITLEFONTFAMILY : *FALLBACKFONT; // clear the pixmap cairo_save(CAIRO); @@ -225,7 +227,8 @@ CTitleTex::CTitleTex(PHLWINDOW pWindow, const Vector2D& bufferSize, const float PangoLayout* layout = pango_cairo_create_layout(CAIRO); pango_layout_set_text(layout, szContent.c_str(), -1); - PangoFontDescription* fontDesc = pango_font_description_from_string((*PTITLEFONTFAMILY).c_str()); + PangoFontDescription* fontDesc = pango_font_description_new(); + pango_font_description_set_family_static(fontDesc, FONTFAMILY.c_str()); pango_font_description_set_size(fontDesc, *PTITLEFONTSIZE * PANGO_SCALE * monitorScale); pango_layout_set_font_description(layout, fontDesc); pango_font_description_free(fontDesc); From 155ae3721c5ef57321a7ef010369f0e03a358612 Mon Sep 17 00:00:00 2001 From: shezdy <77217897+shezdy@users.noreply.github.com> Date: Wed, 22 May 2024 13:51:46 -0600 Subject: [PATCH 56/57] keybinds: Add option to disable window direction monitor fallback (#6182) * add monitor fallback option * format --- src/Compositor.cpp | 9 ++++++++- src/config/ConfigManager.cpp | 1 + src/managers/KeybindManager.cpp | 17 +++++++++++++---- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/Compositor.cpp b/src/Compositor.cpp index efbd997f..5c9cfbad 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -1386,7 +1386,8 @@ PHLWINDOW CCompositor::getWindowInDirection(PHLWINDOW pWindow, char dir) { return nullptr; // 0 -> history, 1 -> shared length - static auto PMETHOD = CConfigValue("binds:focus_preferred_method"); + static auto PMETHOD = CConfigValue("binds:focus_preferred_method"); + static auto PMONITORFALLBACK = CConfigValue("binds:window_direction_monitor_fallback"); const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID); @@ -1416,6 +1417,9 @@ PHLWINDOW CCompositor::getWindowInDirection(PHLWINDOW pWindow, char dir) { if (PWORKSPACE->m_bHasFullscreenWindow && !w->m_bIsFullscreen && !w->m_bCreatedOverFullscreen) continue; + if (!*PMONITORFALLBACK && pWindow->m_iMonitorID != w->m_iMonitorID) + continue; + const auto BWINDOWIDEALBB = w->getWindowIdealBoundingBoxIgnoreReserved(); const auto POSB = Vector2D(BWINDOWIDEALBB.x, BWINDOWIDEALBB.y); @@ -1505,6 +1509,9 @@ PHLWINDOW CCompositor::getWindowInDirection(PHLWINDOW pWindow, char dir) { if (PWORKSPACE->m_bHasFullscreenWindow && !w->m_bIsFullscreen && !w->m_bCreatedOverFullscreen) continue; + if (!*PMONITORFALLBACK && pWindow->m_iMonitorID != w->m_iMonitorID) + continue; + const auto DIST = w->middle().distance(pWindow->middle()); const auto ANGLE = vectorAngles(Vector2D{w->middle() - pWindow->middle()}, VECTORS.at(dir)); diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 14a326bb..77aa2fb3 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -495,6 +495,7 @@ CConfigManager::CConfigManager() { m_pConfig->addConfigValue("binds:ignore_group_lock", Hyprlang::INT{0}); m_pConfig->addConfigValue("binds:movefocus_cycles_fullscreen", Hyprlang::INT{1}); m_pConfig->addConfigValue("binds:disable_keybind_grabbing", Hyprlang::INT{0}); + m_pConfig->addConfigValue("binds:window_direction_monitor_fallback", Hyprlang::INT{1}); m_pConfig->addConfigValue("gestures:workspace_swipe", Hyprlang::INT{0}); m_pConfig->addConfigValue("gestures:workspace_swipe_fingers", Hyprlang::INT{3}); diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp index ea064031..925a7fb7 100644 --- a/src/managers/KeybindManager.cpp +++ b/src/managers/KeybindManager.cpp @@ -1220,8 +1220,9 @@ void CKeybindManager::moveActiveToWorkspaceSilent(std::string args) { } void CKeybindManager::moveFocusTo(std::string args) { - static auto PFULLCYCLE = CConfigValue("binds:movefocus_cycles_fullscreen"); - char arg = args[0]; + static auto PFULLCYCLE = CConfigValue("binds:movefocus_cycles_fullscreen"); + static auto PMONITORFALLBACK = CConfigValue("binds:window_direction_monitor_fallback"); + char arg = args[0]; if (!isDirection(args)) { Debug::log(ERR, "Cannot move focus in direction {}, unsupported direction. Supported: l,r,u/t,d/b", arg); @@ -1230,7 +1231,9 @@ void CKeybindManager::moveFocusTo(std::string args) { const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock(); if (!PLASTWINDOW) { - tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(arg)); + if (*PMONITORFALLBACK) + tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(arg)); + return; } @@ -1246,7 +1249,7 @@ void CKeybindManager::moveFocusTo(std::string args) { Debug::log(LOG, "No window found in direction {}, looking for a monitor", arg); - if (tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(arg))) + if (*PMONITORFALLBACK && tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(arg))) return; static auto PNOFALLBACK = CConfigValue("general:no_focus_fallback"); @@ -1317,6 +1320,8 @@ void CKeybindManager::moveActiveTo(std::string args) { moveActiveToWorkspaceSilent(PNEWMONITOR->activeWorkspace->getConfigName()); else moveActiveToWorkspace(PNEWMONITOR->activeWorkspace->getConfigName()); + + return; } if (!isDirection(args)) { @@ -1356,6 +1361,10 @@ void CKeybindManager::moveActiveTo(std::string args) { return; } + static auto PMONITORFALLBACK = CConfigValue("binds:window_direction_monitor_fallback"); + if (!*PMONITORFALLBACK) + return; + // Otherwise, we always want to move to the next monitor in that direction const auto PMONITORTOCHANGETO = g_pCompositor->getMonitorInDirection(arg); if (!PMONITORTOCHANGETO) From 3775776a07dc54347dd0859c7b6fc63aa3a4c7fd Mon Sep 17 00:00:00 2001 From: vaxerski Date: Wed, 22 May 2024 22:37:02 +0200 Subject: [PATCH 57/57] window: guard monitor in bounding box calculations fixes #6190 --- src/desktop/Window.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/desktop/Window.cpp b/src/desktop/Window.cpp index 56003eca..7987c9d6 100644 --- a/src/desktop/Window.cpp +++ b/src/desktop/Window.cpp @@ -87,9 +87,9 @@ SWindowDecorationExtents CWindow::getFullWindowExtents() { const int BORDERSIZE = getRealBorderSize(); if (m_sAdditionalConfigData.dimAround) { - const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); - return {{m_vRealPosition.value().x - PMONITOR->vecPosition.x, m_vRealPosition.value().y - PMONITOR->vecPosition.y}, - {PMONITOR->vecSize.x - (m_vRealPosition.value().x - PMONITOR->vecPosition.x), PMONITOR->vecSize.y - (m_vRealPosition.value().y - PMONITOR->vecPosition.y)}}; + if (const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); PMONITOR) + return {{m_vRealPosition.value().x - PMONITOR->vecPosition.x, m_vRealPosition.value().y - PMONITOR->vecPosition.y}, + {PMONITOR->vecSize.x - (m_vRealPosition.value().x - PMONITOR->vecPosition.x), PMONITOR->vecSize.y - (m_vRealPosition.value().y - PMONITOR->vecPosition.y)}}; } SWindowDecorationExtents maxExtents = {{BORDERSIZE + 2, BORDERSIZE + 2}, {BORDERSIZE + 2, BORDERSIZE + 2}}; @@ -147,8 +147,8 @@ SWindowDecorationExtents CWindow::getFullWindowExtents() { CBox CWindow::getFullWindowBoundingBox() { if (m_sAdditionalConfigData.dimAround) { - const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); - return {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y}; + if (const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); PMONITOR) + return {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y}; } auto maxExtents = getFullWindowExtents();