input: Rewritten pointer constraints (#4889)

* rewritten constraints

* send pointer enter on activate if not pointer focus

* minor cleanup

* simulate movement on commit

* don't ignore oneshot prop

* various fixes

* dont send motion on confined

* update pos hint on region change
This commit is contained in:
Vaxry 2024-03-02 21:04:55 +00:00 committed by GitHub
parent 328ab43165
commit d72ea5f2a7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 282 additions and 386 deletions

View File

@ -1040,13 +1040,6 @@ void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) {
if (pWindow->m_phForeignToplevel)
wlr_foreign_toplevel_handle_v1_set_activated(pWindow->m_phForeignToplevel, true);
if (!pWindow->m_bIsX11) {
const auto PCONSTRAINT = wlr_pointer_constraints_v1_constraint_for_surface(m_sWLRPointerConstraints, pWindow->m_uSurface.xdg->surface, m_sSeat.seat);
if (PCONSTRAINT)
g_pInputManager->constrainMouse(m_sSeat.mouse, PCONSTRAINT);
}
g_pInputManager->recheckIdleInhibitorStatus();
// move to front of the window history
@ -1069,6 +1062,8 @@ void CCompositor::focusSurface(wlr_surface* pSurface, CWindow* pWindowOwner) {
if (g_pSessionLockManager->isSessionLocked() && !g_pSessionLockManager->isSurfaceSessionLock(pSurface))
return;
const auto PLASTSURF = m_pLastFocus;
// Unfocus last surface if should
if (m_pLastFocus && !pWindowOwner)
g_pXWaylandManager->activateSurface(m_pLastFocus, false);
@ -1103,6 +1098,15 @@ void CCompositor::focusSurface(wlr_surface* pSurface, CWindow* pWindowOwner) {
m_pLastFocus = pSurface;
EMIT_HOOK_EVENT("keyboardFocus", pSurface);
const auto SURF = CWLSurface::surfaceFromWlr(pSurface);
const auto OLDSURF = CWLSurface::surfaceFromWlr(PLASTSURF);
if (OLDSURF && OLDSURF->constraint())
OLDSURF->constraint()->deactivate();
if (SURF && SURF->constraint())
SURF->constraint()->activate();
}
bool CCompositor::windowValidMapped(CWindow* pWindow) {
@ -1770,31 +1774,6 @@ void checkFocusSurfaceIter(wlr_surface* pSurface, int x, int y, void* data) {
pair->second = pair->second || pSurface == pair->first;
}
CWindow* CCompositor::getConstraintWindow(SMouse* pMouse) {
if (!pMouse->currentConstraint)
return nullptr;
const auto PSURFACE = pMouse->currentConstraint->surface;
for (auto& w : m_vWindows) {
if (w->isHidden() || !w->m_bIsMapped || !w->m_pWLSurface.exists())
continue;
if (w->m_bIsX11) {
if (PSURFACE == w->m_pWLSurface.wlr())
return w.get();
} else {
std::pair<wlr_surface*, bool> check = {PSURFACE, false};
wlr_surface_for_each_surface(w->m_uSurface.xdg->surface, checkFocusSurfaceIter, &check);
if (check.second)
return w.get();
}
}
return nullptr;
}
CMonitor* CCompositor::getMonitorInDirection(const char& dir) {
return this->getMonitorInDirection(m_pLastMonitor, dir);
}

View File

@ -166,7 +166,6 @@ class CCompositor {
int getNextAvailableNamedWorkspace();
bool isPointOnAnyMonitor(const Vector2D&);
bool isPointOnReservedArea(const Vector2D& point, const CMonitor* monitor = nullptr);
CWindow* getConstraintWindow(SMouse*);
CMonitor* getMonitorInDirection(const char&);
CMonitor* getMonitorInDirection(CMonitor*, const char&);
void updateAllWindowsAnimatedDecorationValues();

121
src/desktop/Constraint.cpp Normal file
View File

@ -0,0 +1,121 @@
#include "Constraint.hpp"
#include "WLSurface.hpp"
#include "../Compositor.hpp"
CConstraint::CConstraint(wlr_pointer_constraint_v1* constraint, CWLSurface* owner) : m_pOwner(owner), m_pConstraint(constraint) {
initSignals();
m_vCursorPosOnActivate = g_pInputManager->getMouseCoordsInternal();
g_pInputManager->m_vConstraints.push_back(this);
if (g_pCompositor->m_pLastFocus == m_pOwner->wlr())
activate();
}
CConstraint::~CConstraint() {
std::erase(g_pInputManager->m_vConstraints, this);
}
static void onConstraintDestroy(void* owner, void* data) {
const auto CONSTRAINT = (CConstraint*)owner;
CONSTRAINT->onDestroy();
}
static void onConstraintSetRegion(void* owner, void* data) {
const auto CONSTRAINT = (CConstraint*)owner;
CONSTRAINT->onSetRegion();
}
void CConstraint::initSignals() {
hyprListener_setConstraintRegion.initCallback(&m_pConstraint->events.set_region, ::onConstraintSetRegion, this, "CConstraint");
hyprListener_destroyConstraint.initCallback(&m_pConstraint->events.destroy, ::onConstraintDestroy, this, "CConstraint");
}
void CConstraint::onDestroy() {
if (active())
deactivate();
// this is us
m_pOwner->m_pConstraint.reset();
}
void CConstraint::onSetRegion() {
if (!m_bActive)
return;
m_rRegion.set(&m_pConstraint->region);
m_vPositionHint = m_rRegion.closestPoint(m_vPositionHint);
g_pInputManager->simulateMouseMovement(); // to warp the cursor if anything's amiss
}
void CConstraint::onCommit() {
if (!m_bActive)
return;
const auto COMMITTED = m_pConstraint->current.committed;
if (COMMITTED & WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) {
m_bHintSet = true;
m_vPositionHint = {m_pConstraint->current.cursor_hint.x, m_pConstraint->current.cursor_hint.y};
g_pInputManager->simulateMouseMovement();
}
if (COMMITTED & WLR_POINTER_CONSTRAINT_V1_STATE_REGION)
onSetRegion();
}
CRegion CConstraint::logicConstraintRegion() {
CRegion rg = m_rRegion;
const auto SURFBOX = m_pOwner->getSurfaceBoxGlobal();
const auto CONSTRAINTPOS = SURFBOX.has_value() ? SURFBOX->pos() : Vector2D{};
rg.translate(CONSTRAINTPOS);
return rg;
}
CWLSurface* CConstraint::owner() {
return m_pOwner;
}
bool CConstraint::isLocked() {
return m_pConstraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED;
}
Vector2D CConstraint::logicPositionHint() {
const auto SURFBOX = m_pOwner->getSurfaceBoxGlobal();
const auto CONSTRAINTPOS = SURFBOX.has_value() ? SURFBOX->pos() : Vector2D{};
return m_bHintSet ? CONSTRAINTPOS + m_vPositionHint : m_vCursorPosOnActivate;
}
void CConstraint::deactivate() {
if (!m_bActive)
return;
wlr_pointer_constraint_v1_send_deactivated(m_pConstraint);
m_bActive = false;
g_pCompositor->warpCursorTo(logicPositionHint(), true);
if (m_pConstraint->lifetime == ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT)
m_bDead = true;
}
void CConstraint::activate() {
if (m_bActive || m_bDead)
return;
// TODO: hack, probably not a super duper great idea
if (g_pCompositor->m_sSeat.seat->pointer_state.focused_surface != m_pOwner->wlr()) {
const auto SURFBOX = m_pOwner->getSurfaceBoxGlobal();
const auto LOCAL = SURFBOX.has_value() ? logicPositionHint() - SURFBOX->pos() : Vector2D{};
wlr_seat_pointer_enter(g_pCompositor->m_sSeat.seat, m_pOwner->wlr(), LOCAL.x, LOCAL.y);
}
g_pCompositor->warpCursorTo(logicPositionHint(), true);
wlr_pointer_constraint_v1_send_activated(m_pConstraint);
m_bActive = true;
}
bool CConstraint::active() {
return m_bActive;
}

View File

@ -0,0 +1,44 @@
#pragma once
#include "../includes.hpp"
#include "../helpers/Region.hpp"
#include "../helpers/WLListener.hpp"
class CWLSurface;
class CConstraint {
public:
CConstraint(wlr_pointer_constraint_v1* constraint, CWLSurface* owner);
~CConstraint();
void onCommit();
void onDestroy();
void onSetRegion();
CRegion logicConstraintRegion();
bool isLocked();
Vector2D logicPositionHint();
void deactivate();
void activate();
bool active();
CWLSurface* owner();
private:
bool m_bActive = false;
CWLSurface* m_pOwner = nullptr;
wlr_pointer_constraint_v1* m_pConstraint;
CRegion m_rRegion;
bool m_bHintSet = false;
Vector2D m_vPositionHint = {-1, -1};
Vector2D m_vCursorPosOnActivate = {-1, -1};
// for oneshot constraints that have been activated once
bool m_bDead = false;
DYNLISTENER(destroyConstraint);
DYNLISTENER(setConstraintRegion);
void initSignals();
};

View File

@ -103,7 +103,10 @@ void CWLSurface::destroy() {
if (!m_pWLRSurface)
return;
m_pConstraint.reset();
hyprListener_destroy.removeCallback();
hyprListener_commit.removeCallback();
m_pWLRSurface->data = nullptr;
m_pWindowOwner = nullptr;
m_pLayerOwner = nullptr;
@ -123,6 +126,11 @@ void CWLSurface::destroy() {
Debug::log(LOG, "CWLSurface {:x} called destroy()", (uintptr_t)this);
}
static void onCommit(void* owner, void* data) {
const auto SURF = (CWLSurface*)owner;
SURF->onCommit();
}
void CWLSurface::init() {
if (!m_pWLRSurface)
return;
@ -133,6 +141,7 @@ void CWLSurface::init() {
hyprListener_destroy.initCallback(
&m_pWLRSurface->events.destroy, [&](void* owner, void* data) { destroy(); }, this, "CWLSurface");
hyprListener_commit.initCallback(&m_pWLRSurface->events.commit, ::onCommit, this, "CWLSurface");
Debug::log(LOG, "CWLSurface {:x} called init()", (uintptr_t)this);
}
@ -172,3 +181,16 @@ std::optional<CBox> CWLSurface::getSurfaceBoxGlobal() {
return {};
}
void CWLSurface::appendConstraint(wlr_pointer_constraint_v1* constraint) {
m_pConstraint = std::make_unique<CConstraint>(constraint, this);
}
void CWLSurface::onCommit() {
if (m_pConstraint)
m_pConstraint->onCommit();
}
CConstraint* CWLSurface::constraint() {
return m_pConstraint.get();
}

View File

@ -2,6 +2,7 @@
#include "../defines.hpp"
#include "../helpers/Region.hpp"
#include "Constraint.hpp"
class CWindow;
struct SLayerSurface;
@ -32,6 +33,7 @@ class CWLSurface {
Vector2D correctSmallVec() const; // returns a corrective vector for small() surfaces
Vector2D getViewporterCorrectedSize() const;
CRegion logicalDamage() const;
void onCommit();
// getters for owners.
CWindow* getWindow();
@ -41,6 +43,8 @@ class CWLSurface {
// desktop components misc utils
std::optional<CBox> getSurfaceBoxGlobal();
void appendConstraint(wlr_pointer_constraint_v1* constraint);
CConstraint* constraint();
// allow stretching. Useful for plugins.
bool m_bFillIgnoreSmall = false;
@ -72,6 +76,8 @@ class CWLSurface {
}
static CWLSurface* surfaceFromWlr(wlr_surface* pSurface) {
if (!pSurface)
return nullptr;
return (CWLSurface*)pSurface->data;
}
@ -85,9 +91,15 @@ class CWLSurface {
CPopup* m_pPopupOwner = nullptr;
CSubsurface* m_pSubsurfaceOwner = nullptr;
void destroy();
void init();
bool desktopComponent();
//
std::unique_ptr<CConstraint> m_pConstraint;
void destroy();
void init();
bool desktopComponent();
DYNLISTENER(destroy);
DYNLISTENER(commit);
friend class CConstraint;
};

View File

@ -97,44 +97,14 @@ void Events::listener_newConstraint(wl_listener* listener, void* data) {
Debug::log(LOG, "New mouse constraint at {:x}", (uintptr_t)PCONSTRAINT);
g_pInputManager->m_lConstraints.emplace_back();
const auto CONSTRAINT = &g_pInputManager->m_lConstraints.back();
const auto SURFACE = CWLSurface::surfaceFromWlr(PCONSTRAINT->surface);
CONSTRAINT->pMouse = g_pCompositor->m_sSeat.mouse;
CONSTRAINT->constraint = PCONSTRAINT;
CONSTRAINT->hyprListener_destroyConstraint.initCallback(&PCONSTRAINT->events.destroy, &Events::listener_destroyConstraint, CONSTRAINT, "Constraint");
CONSTRAINT->hyprListener_setConstraintRegion.initCallback(&PCONSTRAINT->events.set_region, &Events::listener_setConstraintRegion, CONSTRAINT, "Constraint");
if (g_pCompositor->m_pLastFocus == PCONSTRAINT->surface) {
g_pInputManager->constrainMouse(CONSTRAINT->pMouse, PCONSTRAINT);
if (!CONSTRAINT->hintSet)
CONSTRAINT->positionHint = Vector2D{-1, -1};
}
}
void Events::listener_destroyConstraint(void* owner, void* data) {
const auto PCONSTRAINT = (SConstraint*)owner;
if (PCONSTRAINT->pMouse->currentConstraint == PCONSTRAINT->constraint) {
PCONSTRAINT->pMouse->hyprListener_commitConstraint.removeCallback();
const auto PWINDOW = g_pCompositor->getConstraintWindow(g_pCompositor->m_sSeat.mouse);
if (PWINDOW && PCONSTRAINT->active && PCONSTRAINT->constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED)
g_pInputManager->warpMouseToConstraintMiddle(PCONSTRAINT);
PCONSTRAINT->pMouse->currentConstraint = nullptr;
if (!SURFACE) {
Debug::log(ERR, "Refusing a constraint from an unassigned wl_surface {:x}", (uintptr_t)PCONSTRAINT->surface);
return;
}
Debug::log(LOG, "Unconstrained mouse from {:x}", (uintptr_t)PCONSTRAINT->constraint);
g_pInputManager->m_lConstraints.remove(*PCONSTRAINT);
}
void Events::listener_setConstraintRegion(void* owner, void* data) {
// no
SURFACE->appendConstraint(PCONSTRAINT);
}
void Events::listener_newVirtPtr(wl_listener* listener, void* data) {

View File

@ -65,10 +65,7 @@ namespace Events {
DYNLISTENFUNC(keyboardMod);
DYNLISTENFUNC(keyboardDestroy);
DYNLISTENFUNC(commitConstraint);
LISTENER(newConstraint);
DYNLISTENFUNC(setConstraintRegion);
DYNLISTENFUNC(destroyConstraint);
// Various
LISTENER(requestMouse);

View File

@ -144,7 +144,7 @@ void Events::listener_mapLayerSurface(void* owner, void* data) {
const bool GRABSFOCUS = layersurface->layerSurface->current.keyboard_interactive != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE &&
// don't focus if constrained
(!g_pCompositor->m_sSeat.mouse || !g_pCompositor->m_sSeat.mouse->currentConstraint);
(!g_pCompositor->m_sSeat.mouse || !g_pInputManager->isConstrained());
if (GRABSFOCUS) {
g_pCompositor->focusSurface(layersurface->layerSurface->surface);
@ -340,8 +340,7 @@ void Events::listener_commitLayerSurface(void* owner, void* data) {
layersurface->realSize.setValueAndWarp(layersurface->geometry.size());
}
if (layersurface->layerSurface->current.keyboard_interactive &&
(!g_pCompositor->m_sSeat.mouse || !g_pCompositor->m_sSeat.mouse->currentConstraint) // don't focus if constrained
if (layersurface->layerSurface->current.keyboard_interactive && (!g_pCompositor->m_sSeat.mouse || !g_pInputManager->isConstrained()) // don't focus if constrained
&& !layersurface->keyboardExclusive && layersurface->mapped) {
g_pCompositor->focusSurface(layersurface->layerSurface->surface);
@ -349,7 +348,7 @@ void Events::listener_commitLayerSurface(void* owner, void* data) {
g_pInputManager->getMouseCoordsInternal() - Vector2D(layersurface->geometry.x + PMONITOR->vecPosition.x, layersurface->geometry.y + PMONITOR->vecPosition.y);
wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, layersurface->layerSurface->surface, LOCAL.x, LOCAL.y);
wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, 0, LOCAL.x, LOCAL.y);
} else if (!layersurface->layerSurface->current.keyboard_interactive && (!g_pCompositor->m_sSeat.mouse || !g_pCompositor->m_sSeat.mouse->currentConstraint) &&
} else if (!layersurface->layerSurface->current.keyboard_interactive && (!g_pCompositor->m_sSeat.mouse || !g_pInputManager->isConstrained()) &&
layersurface->keyboardExclusive) {
g_pInputManager->refocus();
}

View File

@ -476,8 +476,9 @@ void Events::listener_mapWindow(void* owner, void* data) {
}
if (!PWINDOW->m_sAdditionalConfigData.noFocus && !PWINDOW->m_bNoInitialFocus &&
(PWINDOW->m_iX11Type != 2 || (PWINDOW->m_bIsX11 && wlr_xwayland_or_surface_wants_focus(PWINDOW->m_uSurface.xwayland))) && !workspaceSilent &&
(!PFORCEFOCUS || PFORCEFOCUS == PWINDOW)) {
(PWINDOW->m_iX11Type != 2 ||
(PWINDOW->m_bIsX11 && PWINDOW->m_uSurface.xwayland->window_type_len > 0 && wlr_xwayland_or_surface_wants_focus(PWINDOW->m_uSurface.xwayland))) &&
!workspaceSilent && (!PFORCEFOCUS || PFORCEFOCUS == PWINDOW) && !g_pInputManager->isConstrained()) {
g_pCompositor->focusWindow(PWINDOW);
PWINDOW->m_fActiveInactiveAlpha.setValueAndWarp(**PACTIVEALPHA);
PWINDOW->m_fDimPercent.setValueAndWarp(PWINDOW->m_sAdditionalConfigData.forceNoDim ? 0.f : **PDIMSTRENGTH);
@ -645,7 +646,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 || !g_pCompositor->m_sSeat.mouse->constraintActive)
if (!g_pCompositor->m_sSeat.mouse || !g_pInputManager->isConstrained())
g_pInputManager->sendMotionEventsToFocused();
// fix some xwayland apps that don't behave nicely

View File

@ -167,82 +167,6 @@ bool SLayerSurface::isFadedOut() {
return !realPosition.isBeingAnimated() && !realSize.isBeingAnimated() && !alpha.isBeingAnimated();
}
CRegion SConstraint::getLogicCoordsRegion() {
CRegion result;
if (!constraint)
return result;
const auto PWINDOWOWNER = g_pCompositor->getWindowFromSurface(constraint->surface);
if (!PWINDOWOWNER)
return result;
result.add(&constraint->region); // surface-local coords
if (!PWINDOWOWNER->m_bIsX11) {
result.translate(PWINDOWOWNER->m_vRealPosition.goal());
return result;
}
const auto COORDS = PWINDOWOWNER->m_bIsMapped ? PWINDOWOWNER->m_vRealPosition.goal() :
g_pXWaylandManager->xwaylandToWaylandCoords({PWINDOWOWNER->m_uSurface.xwayland->x, PWINDOWOWNER->m_uSurface.xwayland->y});
const auto PMONITOR = PWINDOWOWNER->m_bIsMapped ? g_pCompositor->getMonitorFromID(PWINDOWOWNER->m_iMonitorID) : g_pCompositor->getMonitorFromVector(COORDS);
if (!PMONITOR)
return CRegion{};
result.scale(PMONITOR->xwaylandScale);
result.translate(COORDS);
return result;
}
Vector2D SConstraint::getLogicConstraintPos() {
if (!constraint)
return {};
const auto PWINDOWOWNER = g_pCompositor->getWindowFromSurface(constraint->surface);
if (!PWINDOWOWNER)
return {};
if (!PWINDOWOWNER->m_bIsX11)
return PWINDOWOWNER->m_vRealPosition.goal();
const auto COORDS = PWINDOWOWNER->m_bIsMapped ? PWINDOWOWNER->m_vRealPosition.goal() :
g_pXWaylandManager->xwaylandToWaylandCoords({PWINDOWOWNER->m_uSurface.xwayland->x, PWINDOWOWNER->m_uSurface.xwayland->y});
return COORDS;
}
Vector2D SConstraint::getLogicConstraintSize() {
if (!constraint)
return {};
const auto PWINDOWOWNER = g_pCompositor->getWindowFromSurface(constraint->surface);
if (!PWINDOWOWNER)
return {};
if (!PWINDOWOWNER->m_bIsX11)
return PWINDOWOWNER->m_vRealSize.goal();
const auto PMONITOR = PWINDOWOWNER->m_bIsMapped ?
g_pCompositor->getMonitorFromID(PWINDOWOWNER->m_iMonitorID) :
g_pCompositor->getMonitorFromVector(g_pXWaylandManager->xwaylandToWaylandCoords({PWINDOWOWNER->m_uSurface.xwayland->x, PWINDOWOWNER->m_uSurface.xwayland->y}));
if (!PMONITOR)
return {};
const auto SIZE = PWINDOWOWNER->m_bIsMapped ? PWINDOWOWNER->m_vRealSize.goal() :
Vector2D{PWINDOWOWNER->m_uSurface.xwayland->width, PWINDOWOWNER->m_uSurface.xwayland->height} * PMONITOR->xwaylandScale;
return SIZE;
}
void SKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {
xkb_state_unref(xkbTranslationState);

View File

@ -157,20 +157,14 @@ struct SKeyboard {
};
struct SMouse {
wlr_input_device* mouse = nullptr;
wlr_input_device* mouse = nullptr;
wlr_pointer_constraint_v1* currentConstraint = nullptr;
bool constraintActive = false;
std::string name = "";
CRegion confinedTo;
bool virt = false;
std::string name = "";
bool connected = false; // means connected to the cursor
bool virt = false;
bool connected = false; // means connected to the cursor
DYNLISTENER(commitConstraint);
DYNLISTENER(destroyMouse);
bool operator==(const SMouse& b) const {
@ -178,29 +172,6 @@ struct SMouse {
}
};
struct SConstraint {
SMouse* pMouse = nullptr;
wlr_pointer_constraint_v1* constraint = nullptr;
bool active = false;
bool hintSet = false;
Vector2D positionHint = {-1, -1}; // the position hint, but will use cursorPosOnActivate if unset
Vector2D cursorPosOnActivate = {-1, -1};
DYNLISTENER(setConstraintRegion);
DYNLISTENER(destroyConstraint);
CRegion getLogicCoordsRegion();
Vector2D getLogicConstraintPos();
Vector2D getLogicConstraintSize();
//
bool operator==(const SConstraint& b) const {
return constraint == b.constraint;
}
};
class CMonitor;
struct SSeat {

View File

@ -4,7 +4,7 @@
#include <ranges>
CInputManager::~CInputManager() {
m_lConstraints.clear();
m_vConstraints.clear();
m_lKeyboards.clear();
m_lMice.clear();
m_lTablets.clear();
@ -56,7 +56,7 @@ void CInputManager::simulateMouseMovement() {
}
void CInputManager::sendMotionEventsToFocused() {
if (!g_pCompositor->m_pLastFocus)
if (!g_pCompositor->m_pLastFocus || isConstrained())
return;
// todo: this sucks ass
@ -139,56 +139,27 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
}
// constraints
// All constraints TODO: multiple mice?
if (g_pCompositor->m_sSeat.mouse && g_pCompositor->m_sSeat.mouse->currentConstraint && !g_pCompositor->m_sSeat.exclusiveClient && !g_pSessionLockManager->isSessionLocked()) {
// XWayland windows sometimes issue constraints weirdly.
// TODO: We probably should search their parent. wlr_xwayland_surface->parent
const auto CONSTRAINTWINDOW = g_pCompositor->getConstraintWindow(g_pCompositor->m_sSeat.mouse);
const auto PCONSTRAINT = constraintFromWlr(g_pCompositor->m_sSeat.mouse->currentConstraint);
if (g_pCompositor->m_sSeat.mouse && isConstrained()) {
const auto SURF = CWLSurface::surfaceFromWlr(g_pCompositor->m_pLastFocus);
const auto CONSTRAINT = SURF->constraint();
if (!CONSTRAINTWINDOW || !PCONSTRAINT) {
unconstrainMouse();
} else {
// Native Wayland apps know how 2 constrain themselves.
// XWayland, we just have to accept them. Might cause issues, but thats XWayland for ya.
const auto CONSTRAINTPOS = PCONSTRAINT->getLogicConstraintPos();
const auto CONSTRAINTSIZE = PCONSTRAINT->getLogicConstraintSize();
if (g_pCompositor->m_sSeat.mouse->currentConstraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) {
// we just snap the cursor to where it should be.
if (PCONSTRAINT->hintSet)
wlr_cursor_warp_closest(g_pCompositor->m_sWLRCursor, g_pCompositor->m_sSeat.mouse->mouse, CONSTRAINTPOS.x + PCONSTRAINT->positionHint.x,
CONSTRAINTPOS.y + PCONSTRAINT->positionHint.y);
return; // don't process anything else, the cursor is locked. The surface should not receive any further events.
// these are usually FPS games. They will use the relative motion.
if (SURF && CONSTRAINT) {
if (CONSTRAINT->isLocked()) {
const auto HINT = CONSTRAINT->logicPositionHint();
wlr_cursor_warp(g_pCompositor->m_sWLRCursor, nullptr, HINT.x, HINT.y);
} else {
// we restrict the cursor to the confined region
const auto REGION = PCONSTRAINT->getLogicCoordsRegion();
const auto RG = CONSTRAINT->logicConstraintRegion();
const auto CLOSEST = RG.closestPoint(mouseCoords);
const auto BOX = SURF->getSurfaceBoxGlobal();
const auto CLOSESTLOCAL = CLOSEST - (BOX.has_value() ? BOX->pos() : Vector2D{});
if (!REGION.containsPoint(mouseCoords)) {
if (g_pCompositor->m_sSeat.mouse->constraintActive) {
const auto CLOSEST = REGION.closestPoint(mouseCoords);
wlr_cursor_warp_closest(g_pCompositor->m_sWLRCursor, NULL, CLOSEST.x, CLOSEST.y);
mouseCoords = getMouseCoordsInternal();
}
} else {
if ((!CONSTRAINTWINDOW->m_bIsX11 && PMONITOR && CONSTRAINTWINDOW->m_iWorkspaceID == PMONITOR->activeWorkspace) || (CONSTRAINTWINDOW->m_bIsX11)) {
g_pCompositor->m_sSeat.mouse->constraintActive = true;
}
}
wlr_cursor_warp(g_pCompositor->m_sWLRCursor, nullptr, CLOSEST.x, CLOSEST.y);
}
if (CONSTRAINTWINDOW->m_bIsX11) {
foundSurface = CONSTRAINTWINDOW->m_pWLSurface.wlr();
surfacePos = CONSTRAINTWINDOW->m_vRealPosition.value();
} else {
g_pCompositor->vectorWindowToSurface(mouseCoords, CONSTRAINTWINDOW, surfaceCoords);
}
return;
pFoundWindow = CONSTRAINTWINDOW;
}
} else
Debug::log(ERR, "BUG THIS: Null SURF/CONSTRAINT in mouse refocus. Ignoring constraints. {:x} {:x}", (uintptr_t)SURF, (uintptr_t)CONSTRAINT);
}
// update stuff
@ -641,7 +612,7 @@ void CInputManager::processMouseDownNormal(wlr_pointer_button_event* e) {
if (**PFOLLOWMOUSE == 3) // don't refocus on full loose
break;
if ((!g_pCompositor->m_sSeat.mouse || !g_pCompositor->m_sSeat.mouse->currentConstraint) /* No constraints */
if ((!g_pCompositor->m_sSeat.mouse || !isConstrained()) /* No constraints */
&& (w && g_pCompositor->m_pLastWindow != 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
@ -1278,133 +1249,27 @@ void CInputManager::updateDragIcon() {
}
}
void CInputManager::recheckConstraint(SMouse* pMouse) {
if (!pMouse->currentConstraint)
return;
const auto PREGION = &pMouse->currentConstraint->region;
if (pMouse->currentConstraint->type == WLR_POINTER_CONSTRAINT_V1_CONFINED)
pMouse->confinedTo.set(PREGION);
else
pMouse->confinedTo.clear();
}
void CInputManager::constrainMouse(SMouse* pMouse, wlr_pointer_constraint_v1* constraint) {
if (pMouse->currentConstraint == constraint)
return;
const auto MOUSECOORDS = getMouseCoordsInternal();
const auto PCONSTRAINT = constraintFromWlr(constraint);
pMouse->hyprListener_commitConstraint.removeCallback();
if (pMouse->currentConstraint)
wlr_pointer_constraint_v1_send_deactivated(pMouse->currentConstraint);
if (const auto PWINDOW = g_pCompositor->getWindowFromSurface(constraint->surface); PWINDOW) {
const auto RELATIVETO = PWINDOW->m_bIsX11 ?
(PWINDOW->m_bIsMapped ? PWINDOW->m_vRealPosition.goal() :
g_pXWaylandManager->xwaylandToWaylandCoords({PWINDOW->m_uSurface.xwayland->x, PWINDOW->m_uSurface.xwayland->y})) :
PWINDOW->m_vRealPosition.goal();
PCONSTRAINT->cursorPosOnActivate = (MOUSECOORDS - RELATIVETO) * PWINDOW->m_fX11SurfaceScaledBy;
}
if (constraint->current.committed & WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) {
PCONSTRAINT->hintSet = true;
PCONSTRAINT->positionHint = {constraint->current.cursor_hint.x, constraint->current.cursor_hint.y};
}
if (constraint->current.committed & WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT && constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED)
warpMouseToConstraintMiddle(PCONSTRAINT);
pMouse->currentConstraint = constraint;
pMouse->constraintActive = true;
if (pixman_region32_not_empty(&constraint->current.region))
pixman_region32_intersect(&constraint->region, &constraint->surface->input_region, &constraint->current.region);
else
pixman_region32_copy(&constraint->region, &constraint->surface->input_region);
// warp to the constraint
recheckConstraint(pMouse);
PCONSTRAINT->active = true;
wlr_pointer_constraint_v1_send_activated(pMouse->currentConstraint);
pMouse->hyprListener_commitConstraint.initCallback(&pMouse->currentConstraint->surface->events.commit, &Events::listener_commitConstraint, pMouse, "Mouse constraint commit");
Debug::log(LOG, "Constrained mouse to {:x}", (uintptr_t)pMouse->currentConstraint);
}
void CInputManager::warpMouseToConstraintMiddle(SConstraint* pConstraint) {
if (!pConstraint)
return;
const auto PWINDOW = g_pCompositor->getWindowFromSurface(pConstraint->constraint->surface);
if (PWINDOW) {
const auto RELATIVETO = pConstraint->getLogicConstraintPos();
const auto HINTSCALE = PWINDOW->m_fX11SurfaceScaledBy;
auto HINT = pConstraint->hintSet ? pConstraint->positionHint : pConstraint->cursorPosOnActivate;
if (HINT == Vector2D{-1, -1})
HINT = pConstraint->getLogicConstraintSize() / 2.f;
if (HINT != Vector2D{-1, -1}) {
wlr_cursor_warp(g_pCompositor->m_sWLRCursor, nullptr, RELATIVETO.x + HINT.x / HINTSCALE, RELATIVETO.y + HINT.y / HINTSCALE);
wlr_seat_pointer_warp(pConstraint->constraint->seat, pConstraint->constraint->current.cursor_hint.x, pConstraint->constraint->current.cursor_hint.y);
}
}
}
void CInputManager::unconstrainMouse() {
if (!g_pCompositor->m_sSeat.mouse || !g_pCompositor->m_sSeat.mouse->currentConstraint)
if (!g_pCompositor->m_sSeat.mouse)
return;
const auto CONSTRAINTWINDOW = g_pCompositor->getConstraintWindow(g_pCompositor->m_sSeat.mouse);
for (auto& c : m_vConstraints) {
if (!c->active())
continue;
if (CONSTRAINTWINDOW)
g_pXWaylandManager->activateSurface(CONSTRAINTWINDOW->m_pWLSurface.wlr(), false);
wlr_pointer_constraint_v1_send_deactivated(g_pCompositor->m_sSeat.mouse->currentConstraint);
const auto PCONSTRAINT = constraintFromWlr(g_pCompositor->m_sSeat.mouse->currentConstraint);
if (PCONSTRAINT)
PCONSTRAINT->active = false;
g_pCompositor->m_sSeat.mouse->constraintActive = false;
// TODO: its better to somehow detect the workspace...
g_pCompositor->m_sSeat.mouse->currentConstraint = nullptr;
g_pCompositor->m_sSeat.mouse->hyprListener_commitConstraint.removeCallback();
c->deactivate();
}
}
void Events::listener_commitConstraint(void* owner, void* data) {
const auto PMOUSE = (SMouse*)owner;
bool CInputManager::isConstrained() {
for (auto& c : m_vConstraints) {
if (!c->active() || c->owner()->wlr() != g_pCompositor->m_pLastFocus)
continue;
if (PMOUSE->currentConstraint->current.committed & WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) {
const auto PCONSTRAINT = g_pInputManager->constraintFromWlr(PMOUSE->currentConstraint);
if (PCONSTRAINT) { // should never be null but who knows
PCONSTRAINT->positionHint = Vector2D(PMOUSE->currentConstraint->current.cursor_hint.x, PMOUSE->currentConstraint->current.cursor_hint.y);
PCONSTRAINT->hintSet = true;
}
return true;
}
if (PMOUSE->currentConstraint->current.committed & WLR_POINTER_CONSTRAINT_V1_STATE_REGION) {
if (pixman_region32_not_empty(&PMOUSE->currentConstraint->current.region))
pixman_region32_intersect(&PMOUSE->currentConstraint->region, &PMOUSE->currentConstraint->surface->input_region, &PMOUSE->currentConstraint->current.region);
else
pixman_region32_copy(&PMOUSE->currentConstraint->region, &PMOUSE->currentConstraint->surface->input_region);
g_pInputManager->recheckConstraint(PMOUSE);
}
return false;
}
void CInputManager::updateCapabilities() {
@ -1670,15 +1535,6 @@ std::string CInputManager::getNameForNewDevice(std::string internalName) {
return proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno)));
}
SConstraint* CInputManager::constraintFromWlr(wlr_pointer_constraint_v1* constraint) {
for (auto& c : m_lConstraints) {
if (c.constraint == constraint)
return &c;
}
return nullptr;
}
void CInputManager::releaseAllMouseButtons() {
const auto buttonsCopy = m_lCurrentlyHeldButtons;

View File

@ -7,6 +7,8 @@
#include "../../helpers/Timer.hpp"
#include "InputMethodRelay.hpp"
class CConstraint;
enum eClickBehaviorMode {
CLICKMODE_DEFAULT = 0,
CLICKMODE_KILL
@ -80,11 +82,8 @@ class CInputManager {
void destroyMouse(wlr_input_device*);
void destroySwitch(SSwitchDevice*);
void constrainMouse(SMouse*, wlr_pointer_constraint_v1*);
void warpMouseToConstraintMiddle(SConstraint*);
void recheckConstraint(SMouse*);
void unconstrainMouse();
SConstraint* constraintFromWlr(wlr_pointer_constraint_v1*);
bool isConstrained();
std::string getActiveLayoutForKeyboard(SKeyboard*);
Vector2D getMouseCoordsInternal();
@ -120,13 +119,12 @@ class CInputManager {
bool m_bWasDraggingWindow = false;
// for refocus to be forced
CWindow* m_pForcedFocus = nullptr;
CWindow* m_pForcedFocus = nullptr;
SDrag m_sDrag;
SDrag m_sDrag;
std::list<SConstraint> m_lConstraints;
std::list<SKeyboard> m_lKeyboards;
std::list<SMouse> m_lMice;
std::list<SKeyboard> m_lKeyboards;
std::list<SMouse> m_lMice;
// tablets
std::list<STablet> m_lTablets;
@ -145,25 +143,28 @@ class CInputManager {
// Exclusive layer surfaces
std::deque<SLayerSurface*> m_dExclusiveLSes;
void newTabletTool(wlr_input_device*);
void newTabletPad(wlr_input_device*);
void focusTablet(STablet*, wlr_tablet_tool*, bool motion = false);
void newIdleInhibitor(wlr_idle_inhibitor_v1*);
void recheckIdleInhibitorStatus();
// constraints
std::vector<CConstraint*> m_vConstraints;
void onSwipeBegin(wlr_pointer_swipe_begin_event*);
void onSwipeEnd(wlr_pointer_swipe_end_event*);
void onSwipeUpdate(wlr_pointer_swipe_update_event*);
void newTabletTool(wlr_input_device*);
void newTabletPad(wlr_input_device*);
void focusTablet(STablet*, wlr_tablet_tool*, bool motion = false);
void newIdleInhibitor(wlr_idle_inhibitor_v1*);
void recheckIdleInhibitorStatus();
SSwipeGesture m_sActiveSwipe;
void onSwipeBegin(wlr_pointer_swipe_begin_event*);
void onSwipeEnd(wlr_pointer_swipe_end_event*);
void onSwipeUpdate(wlr_pointer_swipe_update_event*);
SKeyboard* m_pActiveKeyboard = nullptr;
SSwipeGesture m_sActiveSwipe;
CTimer m_tmrLastCursorMovement;
SKeyboard* m_pActiveKeyboard = nullptr;
CInputMethodRelay m_sIMERelay;
CTimer m_tmrLastCursorMovement;
void updateKeyboardsLeds(wlr_input_device* pKeyboard);
CInputMethodRelay m_sIMERelay;
void updateKeyboardsLeds(wlr_input_device* pKeyboard);
// for shared mods
uint32_t accumulateModsFromAllKBs();