From 6b16eae7783ad6ef578ee32c970c19b71ad82060 Mon Sep 17 00:00:00 2001 From: littleblack111 Date: Sat, 14 Jun 2025 14:55:28 +0800 Subject: clip --- example/hyprland.conf | 4 ++ src/config/ConfigManager.cpp | 6 ++ .../permissions/DynamicPermissionManager.cpp | 19 +++++ .../permissions/DynamicPermissionManager.hpp | 2 + src/protocols/DataDeviceWlr.cpp | 32 +++++++++ src/protocols/core/DataDevice.cpp | 83 +++++++++++++++++++++- src/xwayland/XWM.cpp | 31 +++++++- 7 files changed, 175 insertions(+), 2 deletions(-) diff --git a/example/hyprland.conf b/example/hyprland.conf index 98ac0996..9c88dcdb 100644 --- a/example/hyprland.conf +++ b/example/hyprland.conf @@ -67,6 +67,10 @@ env = HYPRCURSOR_SIZE,24 # permission = /usr/(bin|local/bin)/grim, screencopy, allow # permission = /usr/(lib|libexec|lib64)/xdg-desktop-portal-hyprland, screencopy, allow # permission = /usr/(bin|local/bin)/hyprpm, plugin, allow +# permission = /usr/bin/clipman, clipboard-read, allow +# permission = /usr/bin/clipman, clipboard-write, allow +# permission = /usr/bin/copyq, clipboard-read, allow +# permission = /usr/bin/copyq, clipboard-write, allow ##################### diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 046a2667..979a4228 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -2833,6 +2833,12 @@ std::optional CConfigManager::handlePermission(const std::string& c type = PERMISSION_TYPE_PLUGIN; else if (data[1] == "keyboard" || data[1] == "keeb") type = PERMISSION_TYPE_KEYBOARD; + else if (data[1] == "clipboard" || data[1] == "clip") + type = PERMISSION_TYPE_CLIPBOARD_WRITE; // default to write for backward compatibility + else if (data[1] == "clipboard-read" || data[1] == "clip-read") + type = PERMISSION_TYPE_CLIPBOARD_READ; + else if (data[1] == "clipboard-write" || data[1] == "clip-write") + type = PERMISSION_TYPE_CLIPBOARD_WRITE; if (data[2] == "ask") mode = PERMISSION_RULE_ALLOW_MODE_ASK; diff --git a/src/managers/permissions/DynamicPermissionManager.cpp b/src/managers/permissions/DynamicPermissionManager.cpp index e2865459..62c6fb67 100644 --- a/src/managers/permissions/DynamicPermissionManager.cpp +++ b/src/managers/permissions/DynamicPermissionManager.cpp @@ -53,6 +53,21 @@ static const char* permissionToString(eDynamicPermissionType type) { case PERMISSION_TYPE_SCREENCOPY: return "PERMISSION_TYPE_SCREENCOPY"; case PERMISSION_TYPE_PLUGIN: return "PERMISSION_TYPE_PLUGIN"; case PERMISSION_TYPE_KEYBOARD: return "PERMISSION_TYPE_KEYBOARD"; + case PERMISSION_TYPE_CLIPBOARD_READ: return "PERMISSION_TYPE_CLIPBOARD_READ"; + case PERMISSION_TYPE_CLIPBOARD_WRITE: return "PERMISSION_TYPE_CLIPBOARD_WRITE"; + } + + return "error"; +} + +static const char* permissionToHumanString(eDynamicPermissionType type) { + switch (type) { + case PERMISSION_TYPE_UNKNOWN: return "An application {} is requesting an unknown permission."; + case PERMISSION_TYPE_SCREENCOPY: return "An application {} is trying to capture your screen.

Do you want to allow it to do so?"; + case PERMISSION_TYPE_PLUGIN: return "An application {} is trying to load a plugin: {}.

Do you want to load it?"; + case PERMISSION_TYPE_KEYBOARD: return "A new keyboard has been plugged in: {}.

Do you want to allow it to operate?"; + case PERMISSION_TYPE_CLIPBOARD_READ: return "An application {} is trying to read from your clipboard.

Do you want to allow it to do so?"; + case PERMISSION_TYPE_CLIPBOARD_WRITE: return "An application {} is trying to write to your clipboard.

Do you want to allow it to do so?"; } return "error"; @@ -140,6 +155,10 @@ eDynamicPermissionAllowMode CDynamicPermissionManager::clientPermissionMode(wl_c return PERMISSION_RULE_ALLOW_MODE_PENDING; } + // clipboard permissions are allow default (like keyboard) + if (permission == PERMISSION_TYPE_CLIPBOARD_READ || permission == PERMISSION_TYPE_CLIPBOARD_WRITE) + return PERMISSION_RULE_ALLOW_MODE_ALLOW; + // if we are here, we need to ask, that's the fallback for all these (keyboards won't come here) askForPermission(client, LOOKUP.value_or(""), permission); diff --git a/src/managers/permissions/DynamicPermissionManager.hpp b/src/managers/permissions/DynamicPermissionManager.hpp index 4de1eb32..7f536661 100644 --- a/src/managers/permissions/DynamicPermissionManager.hpp +++ b/src/managers/permissions/DynamicPermissionManager.hpp @@ -18,6 +18,8 @@ enum eDynamicPermissionType : uint8_t { PERMISSION_TYPE_SCREENCOPY, PERMISSION_TYPE_PLUGIN, PERMISSION_TYPE_KEYBOARD, + PERMISSION_TYPE_CLIPBOARD_READ, + PERMISSION_TYPE_CLIPBOARD_WRITE, }; enum eDynamicPermissionRuleSource : uint8_t { diff --git a/src/protocols/DataDeviceWlr.cpp b/src/protocols/DataDeviceWlr.cpp index d29106e0..262bc109 100644 --- a/src/protocols/DataDeviceWlr.cpp +++ b/src/protocols/DataDeviceWlr.cpp @@ -1,6 +1,8 @@ #include "DataDeviceWlr.hpp" #include +#include #include "../managers/SeatManager.hpp" +#include "../managers/permissions/DynamicPermissionManager.hpp" #include "core/Seat.hpp" using namespace Hyprutils::OS; @@ -23,6 +25,16 @@ CWLRDataOffer::CWLRDataOffer(SP resource_, SPclient(); + const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(CLIENT, PERMISSION_TYPE_CLIPBOARD_READ); + if (PERM == PERMISSION_RULE_ALLOW_MODE_DENY) { + LOGM(Log::LOG, "Clipboard read denied by permission manager for client {:x}", (uintptr_t)CLIENT); + return; + } else if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING) { + LOGM(Log::LOG, "Clipboard read permission pending for client {:x}", (uintptr_t)CLIENT); + return; + } + LOGM(Log::DEBUG, "Offer {:x} asks to send data from source {:x}", (uintptr_t)this, (uintptr_t)m_source.get()); m_source->send(mime, std::move(sendFd)); @@ -118,6 +130,16 @@ CWLRDataDevice::CWLRDataDevice(SP resource_) : m_resou return; } + const auto CLIENT = r->client(); + const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(CLIENT, PERMISSION_TYPE_CLIPBOARD_WRITE); + if (PERM == PERMISSION_RULE_ALLOW_MODE_DENY) { + LOGM(Log::LOG, "Clipboard write denied by permission manager for client {:x}", (uintptr_t)CLIENT); + return; + } else if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING) { + LOGM(Log::LOG, "Clipboard write permission pending for client {:x}", (uintptr_t)CLIENT); + return; + } + if (source && source->used()) LOGM(Log::WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this."); @@ -135,6 +157,16 @@ CWLRDataDevice::CWLRDataDevice(SP resource_) : m_resou return; } + const auto CLIENT = r->client(); + const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(CLIENT, PERMISSION_TYPE_CLIPBOARD_WRITE); + if (PERM == PERMISSION_RULE_ALLOW_MODE_DENY) { + LOGM(Log::LOG, "Clipboard write denied by permission manager for client {:x}", (uintptr_t)CLIENT); + return; + } else if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING) { + LOGM(Log::LOG, "Clipboard write permission pending for client {:x}", (uintptr_t)CLIENT); + return; + } + if (source && source->used()) LOGM(Log::WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this."); diff --git a/src/protocols/core/DataDevice.cpp b/src/protocols/core/DataDevice.cpp index 41f07273..1318910a 100644 --- a/src/protocols/core/DataDevice.cpp +++ b/src/protocols/core/DataDevice.cpp @@ -1,5 +1,6 @@ #include "DataDevice.hpp" #include +#include #include "../../managers/SeatManager.hpp" #include "../../managers/PointerManager.hpp" #include "../../managers/eventLoop/EventLoopManager.hpp" @@ -15,6 +16,7 @@ #include "../../helpers/Monitor.hpp" #include "../../render/Renderer.hpp" #include "../../xwayland/Dnd.hpp" +#include "../../managers/permissions/DynamicPermissionManager.hpp" using namespace Hyprutils::OS; CWLDataOfferResource::CWLDataOfferResource(SP resource_, SP source_) : m_source(source_), m_resource(resource_) { @@ -53,7 +55,49 @@ CWLDataOfferResource::CWLDataOfferResource(SP resource_, SPclient(); + const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(CLIENT, PERMISSION_TYPE_CLIPBOARD_READ); + if (PERM == PERMISSION_RULE_ALLOW_MODE_DENY) { + LOGM(Log::LOG, "Clipboard read denied by permission manager for client {:x}", (uintptr_t)CLIENT); + return; + } else if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING) { + LOGM(Log::LOG, "Clipboard read permission pending for client {:x}", (uintptr_t)CLIENT); + const auto PROMISE = g_pDynamicPermissionManager->promiseFor(CLIENT, PERMISSION_TYPE_CLIPBOARD_READ); + if (!PROMISE) { + LOGM(Log::ERR, "BUG THIS: No promise for client permission for clipboard read"); + return; + } + auto sharedFd = std::make_shared(std::move(sendFd)); + PROMISE->then([this, mime = std::string(mime ? mime : ""), sharedFd](SP> r) { + if (r->hasError()) { + LOGM(Log::ERR, "BUG THIS: No permission returned for clipboard read"); + return; + } + + if (r->result() != PERMISSION_RULE_ALLOW_MODE_ALLOW) { + LOGM(Log::LOG, "Clipboard read denied by user"); + return; + } + + if (!m_source) { + LOGM(Log::WARN, "Source disappeared while waiting for permission"); + return; + } + + LOGM(Log::LOG, "Offer {:x} asks to send data from source {:x} (permission granted)", (uintptr_t)this, (uintptr_t)m_source.get()); + + if (!m_accepted) { + LOGM(Log::WARN, "Offer was never accepted, sending accept first"); + m_source->accepted(mime.c_str()); + } + + m_source->send(mime.c_str(), std::move(*sharedFd)); + m_recvd = true; + }); + return; + } + + LOGM(Log::LOG, "Offer {:x} asks to send data from source {:x}", (uintptr_t)this, (uintptr_t)m_source.get()); if (!m_accepted) { LOGM(Log::WARN, "Offer was never accepted, sending accept first"); @@ -253,6 +297,43 @@ CWLDataDeviceResource::CWLDataDeviceResource(SP resource_) : m_re return; } + const auto CLIENT = r->client(); + const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(CLIENT, PERMISSION_TYPE_CLIPBOARD_WRITE); + if (PERM == PERMISSION_RULE_ALLOW_MODE_DENY) { + LOGM(Log::LOG, "Clipboard write denied by permission manager for client {:x}", (uintptr_t)CLIENT); + return; + } else if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING) { + LOGM(Log::LOG, "Clipboard write permission pending for client {:x}", (uintptr_t)CLIENT); + const auto PROMISE = g_pDynamicPermissionManager->promiseFor(CLIENT, PERMISSION_TYPE_CLIPBOARD_WRITE); + if (!PROMISE) { + LOGM(Log::ERR, "BUG THIS: No promise for client permission for clipboard write"); + return; + } + PROMISE->then([source](SP> r) mutable { + if (r->hasError()) { + LOGM(Log::ERR, "BUG THIS: No permission returned for clipboard write"); + return; + } + + if (r->result() != PERMISSION_RULE_ALLOW_MODE_ALLOW) { + LOGM(Log::LOG, "Clipboard write denied by user"); + return; + } + + if (!source) { + LOGM(Log::WARN, "Source disappeared while waiting for permission"); + return; + } + + if (source && source->m_used) + LOGM(Log::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); + }); + return; + } + if (source && source->m_used) LOGM(Log::WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this."); diff --git a/src/xwayland/XWM.cpp b/src/xwayland/XWM.cpp index 7e20d9b1..a896e222 100644 --- a/src/xwayland/XWM.cpp +++ b/src/xwayland/XWM.cpp @@ -22,8 +22,9 @@ #include "../protocols/XWaylandShell.hpp" #include "../protocols/core/Compositor.hpp" #include "../desktop/state/FocusState.hpp" -using Hyprutils::Memory::CUniquePointer; +#include "../managers/permissions/DynamicPermissionManager.hpp" +using Hyprutils::Memory::CUniquePointer; using namespace Hyprutils::OS; #define XCB_EVENT_RESPONSE_TYPE_MASK 0x7f @@ -1279,6 +1280,20 @@ void CXWM::initSelection() { } void CXWM::setClipboardToWayland(SXSelection& sel) { + const auto CLIENT = g_pXWayland->m_server ? g_pXWayland->m_server->m_xwaylandClient : nullptr; + if (CLIENT && &sel == &m_clipboard) { + const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(CLIENT, PERMISSION_TYPE_CLIPBOARD_WRITE); + if (PERM == PERMISSION_RULE_ALLOW_MODE_DENY) { + Debug::log(LOG, "[xwm] Clipboard write denied by permission manager for XWayland"); + return; + } else if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING) { + Debug::log(LOG, "[xwm] Clipboard write permission pending for XWayland"); + // For now, deny clipboard access when permission is pending as XWayland doesn't support async operations + // TODO: In the future, this could be enhanced to store the request and retry after permission is granted + return; + } + } + auto source = makeShared(sel); if (source->mimes().empty()) { Log::logger->log(Log::ERR, "[xwm] can't set selection: no MIMEs"); @@ -1520,6 +1535,20 @@ bool SXSelection::sendData(xcb_selection_request_event_t* e, std::string mime) { return false; } + const auto CLIENT = g_pXWayland->m_server ? g_pXWayland->m_server->m_xwaylandClient : nullptr; + if (CLIENT) { + const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(CLIENT, PERMISSION_TYPE_CLIPBOARD_READ); + if (PERM == PERMISSION_RULE_ALLOW_MODE_DENY) { + Debug::log(LOG, "[xwm] Clipboard read denied by permission manager for XWayland"); + return false; + } else if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING) { + Debug::log(LOG, "[xwm] Clipboard read permission pending for XWayland"); + // For now, deny clipboard access when permission is pending as XWayland doesn't support async operations + // TODO: In the future, this could be enhanced to store the request and retry after permission is granted + return false; + } + } + const auto MIMES = selection->mimes(); if (MIMES.empty()) { -- cgit v1.2.3