Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement new Input Capture protocol #7919

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[submodule "subprojects/hyprland-protocols"]
path = subprojects/hyprland-protocols
url = https://github.com/hyprwm/hyprland-protocols
url = https://github.com/3l0w/hyprland-protocols
branch = feat/input-capture-impl
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

marking this one as a blocker until this is removed so we dont forget (don't resolve)

[submodule "subprojects/udis86"]
path = subprojects/udis86
url = https://github.com/canihavesomecoffee/udis86
Expand Down
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,8 @@ protocolnew("staging/xdg-dialog" "xdg-dialog-v1" false)
protocolnew("staging/single-pixel-buffer" "single-pixel-buffer-v1" false)
protocolnew("staging/security-context" "security-context-v1" false)

protocolnew("subprojects/hyprland-protocols/protocols" "hyprland-input-capture-v1"
3l0w marked this conversation as resolved.
Show resolved Hide resolved
true)
protocolwayland()

# tools
Expand Down
15 changes: 15 additions & 0 deletions src/managers/KeybindManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "eventLoop/EventLoopManager.hpp"
#include "debug/Log.hpp"
#include "helpers/varlist/VarList.hpp"
#include "protocols/InputCapture.hpp"

#include <optional>
#include <iterator>
Expand Down Expand Up @@ -126,6 +127,7 @@ CKeybindManager::CKeybindManager() {
m_mDispatchers["denywindowfromgroup"] = denyWindowFromGroup;
m_mDispatchers["event"] = event;
m_mDispatchers["global"] = global;
m_mDispatchers["releaseinputcapture"] = releaseInputCapture;

m_tScrollTimer.reset();

Expand Down Expand Up @@ -716,6 +718,14 @@ SDispatchResult CKeybindManager::handleKeybinds(const uint32_t modmask, const SP

m_iPassPressed = (int)pressed;

// We only process the releaseinputcapture dispatcher when input capture is active
if (PROTO::inputCapture->isCaptured()) {
if (k.handler == "releaseinputcapture")
res = DISPATCHER->second(k.arg);
else
break;
}

// if the dispatchers says to pass event then we will
if (k.handler == "mouse")
res = DISPATCHER->second((pressed ? "1" : "0") + k.arg);
Expand Down Expand Up @@ -2891,3 +2901,8 @@ SDispatchResult CKeybindManager::event(std::string args) {
g_pEventManager->postEvent(SHyprIPCEvent{"custom", args});
return {};
}

SDispatchResult CKeybindManager::releaseInputCapture(std::string args) {
PROTO::inputCapture->forceRelease();
return {};
}
1 change: 1 addition & 0 deletions src/managers/KeybindManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ class CKeybindManager {
static SDispatchResult denyWindowFromGroup(std::string);
static SDispatchResult global(std::string);
static SDispatchResult event(std::string);
static SDispatchResult releaseInputCapture(std::string);

friend class CCompositor;
friend class CInputManager;
Expand Down
15 changes: 7 additions & 8 deletions src/managers/PointerManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "../protocols/core/Seat.hpp"
#include "eventLoop/EventLoopManager.hpp"
#include "SeatManager.hpp"
#include "protocols/InputCapture.hpp"
#include <cstring>
#include <gbm.h>

Expand Down Expand Up @@ -673,6 +674,11 @@ void CPointerManager::move(const Vector2D& deltaLogical) {
const auto oldPos = pointerPos;
auto newPos = oldPos + Vector2D{std::isnan(deltaLogical.x) ? 0.0 : deltaLogical.x, std::isnan(deltaLogical.y) ? 0.0 : deltaLogical.y};

PROTO::inputCapture->sendMotion(newPos, deltaLogical);

if (PROTO::inputCapture->isCaptured())
return;

warpTo(newPos);
}

Expand Down Expand Up @@ -846,14 +852,7 @@ void CPointerManager::attachPointer(SP<IPointer> pointer) {
});

listener->frame = pointer->pointerEvents.frame.registerListener([this] (std::any e) {
bool shouldSkip = false;
if (!g_pSeatManager->mouse.expired() && g_pInputManager->isLocked()) {
auto PMONITOR = g_pCompositor->m_pLastMonitor.get();
shouldSkip = PMONITOR && PMONITOR->shouldSkipScheduleFrameOnMouseEvent();
}
g_pSeatManager->isPointerFrameSkipped = shouldSkip;
if (!g_pSeatManager->isPointerFrameSkipped)
g_pSeatManager->sendPointerFrame();
g_pInputManager->onMouseFrame();
});

listener->swipeBegin = pointer->pointerEvents.swipeBegin.registerListener([this] (std::any e) {
Expand Down
3 changes: 3 additions & 0 deletions src/managers/ProtocolManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include "../protocols/SinglePixel.hpp"
#include "../protocols/SecurityContext.hpp"
#include "../protocols/CTMControl.hpp"
#include "../protocols/InputCapture.hpp"

#include "../protocols/core/Seat.hpp"
#include "../protocols/core/DataDevice.hpp"
Expand Down Expand Up @@ -159,6 +160,7 @@ CProtocolManager::CProtocolManager() {
PROTO::singlePixel = std::make_unique<CSinglePixelProtocol>(&wp_single_pixel_buffer_manager_v1_interface, 1, "SinglePixel");
PROTO::securityContext = std::make_unique<CSecurityContextProtocol>(&wp_security_context_manager_v1_interface, 1, "SecurityContext");
PROTO::ctm = std::make_unique<CHyprlandCTMControlProtocol>(&hyprland_ctm_control_manager_v1_interface, 1, "CTMControl");
PROTO::inputCapture = std::make_unique<CInputCaptureProtocol>(&hyprland_input_capture_manager_v1_interface, 1, "InputCapture");

for (auto const& b : g_pCompositor->m_pAqBackend->getImplementations()) {
if (b->type() != Aquamarine::AQ_BACKEND_DRM)
Expand Down Expand Up @@ -232,6 +234,7 @@ CProtocolManager::~CProtocolManager() {
PROTO::singlePixel.reset();
PROTO::securityContext.reset();
PROTO::ctm.reset();
PROTO::inputCapture.reset();

PROTO::lease.reset();
PROTO::sync.reset();
Expand Down
2 changes: 2 additions & 0 deletions src/managers/SeatManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "../protocols/DataDeviceWlr.hpp"
#include "../protocols/PrimarySelection.hpp"
#include "../protocols/core/Compositor.hpp"
#include "../protocols/InputCapture.hpp"
#include "../Compositor.hpp"
#include "../devices/IKeyboard.hpp"
#include "wlr-layer-shell-unstable-v1.hpp"
Expand Down Expand Up @@ -98,6 +99,7 @@ void CSeatManager::updateActiveKeyboardData() {
if (keyboard)
PROTO::seat->updateRepeatInfo(keyboard->repeatRate, keyboard->repeatDelay);
PROTO::seat->updateKeymap();
PROTO::inputCapture->updateKeymap();
}

void CSeatManager::setKeyboardFocus(SP<CWLSurfaceResource> surf) {
Expand Down
37 changes: 35 additions & 2 deletions src/managers/input/InputManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "../../protocols/core/DataDevice.hpp"
#include "../../protocols/core/Compositor.hpp"
#include "../../protocols/XDGShell.hpp"
#include "../../protocols/InputCapture.hpp"

#include "../../devices/Mouse.hpp"
#include "../../devices/VirtualPointer.hpp"
Expand Down Expand Up @@ -96,6 +97,9 @@ void CInputManager::onMouseMoved(IPointer::SMotionEvent e) {

g_pPointerManager->move(DELTA);

if (PROTO::inputCapture->isCaptured())
return;

mouseMoveUnified(e.timeMs);

m_tmrLastCursorMovement.reset();
Expand Down Expand Up @@ -531,6 +535,11 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
void CInputManager::onMouseButton(IPointer::SButtonEvent e) {
EMIT_HOOK_EVENT_CANCELLABLE("mouseButton", e);

PROTO::inputCapture->sendButton(e.button, (hyprlandInputCaptureManagerV1ButtonState)e.state);

if (PROTO::inputCapture->isCaptured())
return;

m_tmrLastCursorMovement.reset();

if (e.state == WL_POINTER_BUTTON_STATE_PRESSED) {
Expand Down Expand Up @@ -763,7 +772,13 @@ void CInputManager::onMouseWheel(IPointer::SAxisEvent e) {
const auto EMAP = std::unordered_map<std::string, std::any>{{"event", e}};
EMIT_HOOK_EVENT_CANCELLABLE("mouseAxis", EMAP);

bool passEvent = g_pKeybindManager->onAxisEvent(e);
PROTO::inputCapture->sendAxis((hyprlandInputCaptureManagerV1Axis)e.axis, e.delta);
if (e.source == 0)
PROTO::inputCapture->sendAxisValue120((hyprlandInputCaptureManagerV1Axis)e.axis, e.delta);
else if (e.delta == 0)
PROTO::inputCapture->sendAxisStop((hyprlandInputCaptureManagerV1Axis)e.axis);

bool passEvent = !PROTO::inputCapture->isCaptured() && g_pKeybindManager->onAxisEvent(e);

if (!passEvent)
return;
Expand Down Expand Up @@ -833,6 +848,22 @@ void CInputManager::onMouseWheel(IPointer::SAxisEvent e) {
g_pSeatManager->sendPointerAxis(e.timeMs, e.axis, delta, deltaDiscrete, value120, e.source, WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL);
}

void CInputManager::onMouseFrame() {
PROTO::inputCapture->sendFrame();

if (PROTO::inputCapture->isCaptured())
return;

bool shouldSkip = false;
if (!g_pSeatManager->mouse.expired() && g_pInputManager->isLocked()) {
auto PMONITOR = g_pCompositor->m_pLastMonitor.get();
shouldSkip = PMONITOR && PMONITOR->shouldSkipScheduleFrameOnMouseEvent();
}
g_pSeatManager->isPointerFrameSkipped = shouldSkip;
if (!g_pSeatManager->isPointerFrameSkipped)
g_pSeatManager->sendPointerFrame();
}

Vector2D CInputManager::getMouseCoordsInternal() {
return g_pPointerManager->position();
}
Expand Down Expand Up @@ -1292,10 +1323,12 @@ void CInputManager::onKeyboardKey(std::any event, SP<IKeyboard> pKeyboard) {
const auto EMAP = std::unordered_map<std::string, std::any>{{"keyboard", pKeyboard}, {"event", event}};
EMIT_HOOK_EVENT_CANCELLABLE("keyPress", EMAP);

bool passEvent = DISALLOWACTION || g_pKeybindManager->onKeyEvent(event, pKeyboard);
bool passEvent = (DISALLOWACTION || g_pKeybindManager->onKeyEvent(event, pKeyboard)) && !PROTO::inputCapture->isCaptured();

auto e = std::any_cast<IKeyboard::SKeyEvent>(event);

PROTO::inputCapture->sendKey(e.keycode, (hyprlandInputCaptureManagerV1KeyState)e.state);

if (passEvent) {
const auto IME = m_sIMERelay.m_pIME.lock();

Expand Down
1 change: 1 addition & 0 deletions src/managers/input/InputManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class CInputManager {
void onMouseWarp(IPointer::SMotionAbsoluteEvent);
void onMouseButton(IPointer::SButtonEvent);
void onMouseWheel(IPointer::SAxisEvent);
void onMouseFrame();
void onKeyboardKey(std::any, SP<IKeyboard>);
void onKeyboardMod(SP<IKeyboard>);

Expand Down
106 changes: 106 additions & 0 deletions src/protocols/InputCapture.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#include "InputCapture.hpp"

#include "../devices/IKeyboard.hpp"
#include "managers/SeatManager.hpp"
#include <fcntl.h>

CInputCaptureProtocol::CInputCaptureProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
;
}

void CInputCaptureProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
const auto& RESOURCE = m_vManagers.emplace_back(std::make_unique<CHyprlandInputCaptureManagerV1>(client, ver, id));

RESOURCE->setOnDestroy([this](CHyprlandInputCaptureManagerV1* p) { std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == p->resource(); }); });

RESOURCE->setCapture([this](CHyprlandInputCaptureManagerV1* p) {
Debug::log(LOG, "[input-capture] Input captured");
active = true;
});
RESOURCE->setRelease([this](CHyprlandInputCaptureManagerV1* p) {
Debug::log(LOG, "[input-capture] Input released");
active = false;
});

sendKeymap(g_pSeatManager->keyboard.lock(), RESOURCE);
}

bool CInputCaptureProtocol::isCaptured() {
return active;
}

void CInputCaptureProtocol::updateKeymap() {
for (const auto& manager : m_vManagers)
sendKeymap(g_pSeatManager->keyboard.lock(), manager);
}

void CInputCaptureProtocol::sendMotion(const Vector2D& absolutePosition, const Vector2D& delta) {
for (const auto& manager : m_vManagers) {
manager->sendMotion(wl_fixed_from_double(absolutePosition.x), wl_fixed_from_double(absolutePosition.y), wl_fixed_from_double(delta.x), wl_fixed_from_double(delta.y));
}
}

void CInputCaptureProtocol::sendKeymap(SP<IKeyboard> keyboard, const std::unique_ptr<CHyprlandInputCaptureManagerV1>& manager) {
if (!keyboard)
return;

hyprlandInputCaptureManagerV1KeymapFormat format;
int fd;
uint32_t size;
if (keyboard) {
format = HYPRLAND_INPUT_CAPTURE_MANAGER_V1_KEYMAP_FORMAT_XKB_V1;
fd = keyboard->xkbKeymapFD;
size = keyboard->xkbKeymapString.length() + 1;
} else {
format = HYPRLAND_INPUT_CAPTURE_MANAGER_V1_KEYMAP_FORMAT_NO_KEYMAP;
fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
if (fd < 0) {
LOGM(ERR, "Failed to open /dev/null");
return;
}
size = 0;
}

manager->sendKeymap(format, fd, size);

if (!keyboard)
close(fd);
}

void CInputCaptureProtocol::forceRelease() {
Debug::log(LOG, "[input-capture] Force Input released");
active = false;

for (const auto& manager : m_vManagers)
manager->sendForceRelease();
}

void CInputCaptureProtocol::sendKey(uint32_t keyCode, hyprlandInputCaptureManagerV1KeyState state) {
for (const auto& manager : m_vManagers)
manager->sendKey(keyCode, state);
}

void CInputCaptureProtocol::sendButton(uint32_t button, hyprlandInputCaptureManagerV1ButtonState state) {
for (const auto& manager : m_vManagers)
manager->sendButton(button, state);
}

void CInputCaptureProtocol::sendAxis(hyprlandInputCaptureManagerV1Axis axis, double value) {
for (const auto& manager : m_vManagers)
manager->sendAxis(axis, value);
}

void CInputCaptureProtocol::sendAxisValue120(hyprlandInputCaptureManagerV1Axis axis, int32_t value120) {
for (const auto& manager : m_vManagers)
manager->sendAxisValue120(axis, value120);
}

void CInputCaptureProtocol::sendAxisStop(hyprlandInputCaptureManagerV1Axis axis) {
for (const auto& manager : m_vManagers)
manager->sendAxisStop(axis);
}

void CInputCaptureProtocol::sendFrame() {
for (const auto& manager : m_vManagers)
manager->sendFrame();
}
37 changes: 37 additions & 0 deletions src/protocols/InputCapture.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#pragma once

#include "hyprland-input-capture-v1.hpp"
#include "../protocols/WaylandProtocol.hpp"
#include <hyprutils/math/Vector2D.hpp>

class CInputCaptureProtocol : public IWaylandProtocol {
public:
CInputCaptureProtocol(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);

//
bool isCaptured();

void updateKeymap();
void forceRelease();

void sendMotion(const Vector2D& absolutePosition, const Vector2D& delta);
void sendKey(uint32_t keyCode, hyprlandInputCaptureManagerV1KeyState state);
void sendButton(uint32_t button, hyprlandInputCaptureManagerV1ButtonState state);
void sendAxis(hyprlandInputCaptureManagerV1Axis axis, double value);
void sendAxisValue120(hyprlandInputCaptureManagerV1Axis axis, int32_t value120);
void sendAxisStop(hyprlandInputCaptureManagerV1Axis axis);

void sendFrame();

private:
void sendKeymap(SP<IKeyboard> keyboard, const std::unique_ptr<CHyprlandInputCaptureManagerV1>& manager);

bool active = false;
//
std::vector<UP<CHyprlandInputCaptureManagerV1>> m_vManagers;
};

namespace PROTO {
inline UP<CInputCaptureProtocol> inputCapture;
}
5 changes: 2 additions & 3 deletions src/protocols/core/Output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,9 @@ void CWLOutputResource::updateState() {
if (resource->version() >= 2)
resource->sendScale(std::ceil(monitor->scale));

resource->sendMode((wl_output_mode)(WL_OUTPUT_MODE_CURRENT), monitor->vecPixelSize.x, monitor->vecPixelSize.y, monitor->refreshRate * 1000.0);

resource->sendGeometry(0, 0, monitor->output->physicalSize.x, monitor->output->physicalSize.y, (wl_output_subpixel)monitor->output->subpixel, monitor->output->make.c_str(),
resource->sendGeometry(monitor->vecPosition.x, monitor->vecPosition.y, monitor->output->physicalSize.x, monitor->output->physicalSize.y, (wl_output_subpixel)monitor->output->subpixel, monitor->output->make.c_str(),
vaxerski marked this conversation as resolved.
Show resolved Hide resolved
monitor->output->model.c_str(), monitor->transform);
resource->sendMode((wl_output_mode)(WL_OUTPUT_MODE_CURRENT), monitor->vecPixelSize.x, monitor->vecPixelSize.y, monitor->refreshRate * 1000.0);

if (resource->version() >= 2)
resource->sendDone();
Expand Down
2 changes: 1 addition & 1 deletion subprojects/hyprland-protocols