Hyprland/src/managers/input/InputManager.cpp
Tom Englund 1c73beaf9b
inputmgr: dont double free on hotplug (#5855)
* inputmgr: dont double free on hotplug

since we are also unrefing the state on hotplugging the keyboard set the
state to nullptr so the destructor if case actually catches its been
already freed.

* keybindgmgr: dont double free on layout switching

d5bf153 added keymap unref at the end of updateXKBTranslationState to
not leak it when exiting, only it causes updateXKBTranslationState to
double free when changing layouts. since its already freed. remove the
unneeded extra xkb_keymap_unref.
2024-05-03 18:10:41 +01:00

1763 lines
75 KiB
C++

#include "InputManager.hpp"
#include "../../Compositor.hpp"
#include "wlr/types/wlr_switch.h"
#include <ranges>
#include "../../config/ConfigValue.hpp"
#include "../../desktop/Window.hpp"
#include "../../protocols/CursorShape.hpp"
#include "../../protocols/IdleInhibit.hpp"
#include "../../protocols/RelativePointer.hpp"
#include "../../protocols/PointerConstraints.hpp"
#include "../../protocols/IdleNotify.hpp"
#include "../../protocols/SessionLock.hpp"
#include "../../protocols/InputMethodV2.hpp"
#include "../../protocols/VirtualKeyboard.hpp"
#include "../../protocols/VirtualPointer.hpp"
CInputManager::CInputManager() {
m_sListeners.setCursorShape = PROTO::cursorShape->events.setShape.registerListener([this](std::any data) {
if (!cursorImageUnlocked())
return;
auto event = std::any_cast<CCursorShapeProtocol::SSetShapeEvent>(data);
if (!g_pCompositor->m_sSeat.seat->pointer_state.focused_client)
return;
if (wl_resource_get_client(event.pMgr->resource()) != g_pCompositor->m_sSeat.seat->pointer_state.focused_client->client)
return;
Debug::log(LOG, "cursorImage request: shape {} -> {}", (uint32_t)event.shape, event.shapeName);
m_sCursorSurfaceInfo.wlSurface.unassign();
m_sCursorSurfaceInfo.vHotspot = {};
m_sCursorSurfaceInfo.name = event.shapeName;
m_sCursorSurfaceInfo.hidden = false;
m_sCursorSurfaceInfo.inUse = true;
g_pHyprRenderer->setCursorFromName(m_sCursorSurfaceInfo.name);
});
m_sListeners.newIdleInhibitor = PROTO::idleInhibit->events.newIdleInhibitor.registerListener([this](std::any data) { this->newIdleInhibitor(data); });
m_sListeners.newVirtualKeyboard =
PROTO::virtualKeyboard->events.newKeyboard.registerListener([this](std::any data) { this->newVirtualKeyboard(std::any_cast<SP<CVirtualKeyboard>>(data)); });
m_sListeners.newVirtualMouse =
PROTO::virtualPointer->events.newPointer.registerListener([this](std::any data) { this->newVirtualMouse(std::any_cast<SP<CVirtualPointer>>(data)); });
}
CInputManager::~CInputManager() {
m_vConstraints.clear();
m_lKeyboards.clear();
m_lMice.clear();
m_lTablets.clear();
m_lTabletTools.clear();
m_lTabletPads.clear();
m_vIdleInhibitors.clear();
m_lTouchDevices.clear();
m_lSwitches.clear();
}
void CInputManager::onMouseMoved(wlr_pointer_motion_event* e) {
static auto PSENS = CConfigValue<Hyprlang::FLOAT>("general:sensitivity");
static auto PNOACCEL = CConfigValue<Hyprlang::INT>("input:force_no_accel");
static auto PSENSTORAW = CConfigValue<Hyprlang::INT>("general:apply_sens_to_raw");
const auto DELTA = *PNOACCEL == 1 ? Vector2D(e->unaccel_dx, e->unaccel_dy) : Vector2D(e->delta_x, e->delta_y);
if (*PSENSTORAW == 1)
PROTO::relativePointer->sendRelativeMotion((uint64_t)e->time_msec * 1000, DELTA * *PSENS, Vector2D{e->unaccel_dx, e->unaccel_dy} * *PSENS);
else
PROTO::relativePointer->sendRelativeMotion((uint64_t)e->time_msec * 1000, DELTA, Vector2D{e->unaccel_dx, e->unaccel_dy});
wlr_cursor_move(g_pCompositor->m_sWLRCursor, &e->pointer->base, DELTA.x * *PSENS, DELTA.y * *PSENS);
mouseMoveUnified(e->time_msec);
m_tmrLastCursorMovement.reset();
m_bLastInputTouch = false;
}
void CInputManager::onMouseWarp(wlr_pointer_motion_absolute_event* e) {
wlr_cursor_warp_absolute(g_pCompositor->m_sWLRCursor, &e->pointer->base, e->x, e->y);
mouseMoveUnified(e->time_msec);
m_tmrLastCursorMovement.reset();
m_bLastInputTouch = false;
}
void CInputManager::simulateMouseMovement() {
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
m_vLastCursorPosFloored = m_vLastCursorPosFloored - Vector2D(1, 1); // hack: force the mouseMoveUnified to report without making this a refocus.
mouseMoveUnified(now.tv_sec * 1000 + now.tv_nsec / 10000000);
}
void CInputManager::sendMotionEventsToFocused() {
if (!g_pCompositor->m_pLastFocus || isConstrained())
return;
// todo: this sucks ass
const auto PWINDOW = g_pCompositor->getWindowFromSurface(g_pCompositor->m_pLastFocus);
const auto PLS = g_pCompositor->getLayerSurfaceFromSurface(g_pCompositor->m_pLastFocus);
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
const auto LOCAL = getMouseCoordsInternal() - (PWINDOW ? PWINDOW->m_vRealPosition.goal() : (PLS ? Vector2D{PLS->geometry.x, PLS->geometry.y} : Vector2D{}));
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);
}
void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
static auto PFOLLOWMOUSE = CConfigValue<Hyprlang::INT>("input:follow_mouse");
static auto PMOUSEREFOCUS = CConfigValue<Hyprlang::INT>("input:mouse_refocus");
static auto PMOUSEDPMS = CConfigValue<Hyprlang::INT>("misc:mouse_move_enables_dpms");
static auto PFOLLOWONDND = CConfigValue<Hyprlang::INT>("misc:always_follow_on_dnd");
static auto PFLOATBEHAVIOR = CConfigValue<Hyprlang::INT>("input:float_switch_override_focus");
static auto PMOUSEFOCUSMON = CConfigValue<Hyprlang::INT>("misc:mouse_move_focuses_monitor");
static auto PRESIZEONBORDER = CConfigValue<Hyprlang::INT>("general:resize_on_border");
static auto PRESIZECURSORICON = CConfigValue<Hyprlang::INT>("general:hover_icon_on_border");
static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("misc:cursor_zoom_factor");
const auto FOLLOWMOUSE = *PFOLLOWONDND && m_sDrag.drag ? 1 : *PFOLLOWMOUSE;
m_pFoundSurfaceToFocus = nullptr;
m_pFoundLSToFocus.reset();
m_pFoundWindowToFocus.reset();
wlr_surface* foundSurface = nullptr;
Vector2D surfaceCoords;
Vector2D surfacePos = Vector2D(-1337, -1337);
PHLWINDOW pFoundWindow;
PHLLS pFoundLayerSurface;
if (!g_pCompositor->m_bReadyToProcess || g_pCompositor->m_bIsShuttingDown || g_pCompositor->m_bUnsafeState)
return;
if (!g_pCompositor->m_bDPMSStateON && *PMOUSEDPMS) {
// enable dpms
g_pKeybindManager->dpms("on");
}
Vector2D mouseCoords = getMouseCoordsInternal();
const auto MOUSECOORDSFLOORED = mouseCoords.floor();
if (MOUSECOORDSFLOORED == m_vLastCursorPosFloored && !refocus)
return;
EMIT_HOOK_EVENT_CANCELLABLE("mouseMove", MOUSECOORDSFLOORED);
if (time)
PROTO::idle->onActivity();
m_vLastCursorPosFloored = MOUSECOORDSFLOORED;
const auto PMONITOR = g_pCompositor->getMonitorFromCursor();
// this can happen if there are no displays hooked up to Hyprland
if (PMONITOR == nullptr)
return;
if (*PZOOMFACTOR != 1.f)
g_pHyprRenderer->damageMonitor(PMONITOR);
if (!PMONITOR->solitaryClient.lock() && g_pHyprRenderer->shouldRenderCursor() && PMONITOR->output->software_cursor_locks > 0)
g_pCompositor->scheduleFrameForMonitor(PMONITOR);
PHLWINDOW forcedFocus = m_pForcedFocus.lock();
if (!forcedFocus)
forcedFocus = g_pCompositor->getForceFocus();
if (forcedFocus) {
pFoundWindow = forcedFocus;
surfacePos = pFoundWindow->m_vRealPosition.value();
foundSurface = pFoundWindow->m_pWLSurface.wlr();
}
// constraints
if (g_pCompositor->m_sSeat.mouse && isConstrained()) {
const auto SURF = CWLSurface::surfaceFromWlr(g_pCompositor->m_pLastFocus);
const auto CONSTRAINT = SURF->constraint();
if (SURF && CONSTRAINT) {
if (CONSTRAINT->isLocked()) {
const auto HINT = CONSTRAINT->logicPositionHint();
wlr_cursor_warp(g_pCompositor->m_sWLRCursor, nullptr, HINT.x, HINT.y);
} else {
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{})) * (SURF->getWindow() ? SURF->getWindow()->m_fX11SurfaceScaledBy : 1.0);
wlr_cursor_warp(g_pCompositor->m_sWLRCursor, nullptr, CLOSEST.x, CLOSEST.y);
wlr_seat_pointer_send_motion(g_pCompositor->m_sSeat.seat, time, CLOSESTLOCAL.x, CLOSESTLOCAL.y);
PROTO::relativePointer->sendRelativeMotion((uint64_t)time * 1000, {}, {});
}
return;
} else
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 && m_pLastMouseSurface) {
foundSurface = m_pLastMouseSurface;
pFoundLayerSurface = g_pCompositor->getLayerSurfaceFromSurface(foundSurface);
if (pFoundLayerSurface) {
surfacePos = pFoundLayerSurface->position;
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 = m_pLastMouseSurface;
pFoundWindow = g_pCompositor->m_pLastWindow.lock();
surfaceCoords = g_pCompositor->vectorToSurfaceLocal(mouseCoords, pFoundWindow, foundSurface);
m_bFocusHeldByButtons = true;
m_bRefocusHeldByButtons = refocus;
}
}
}
g_pLayoutManager->getCurrentLayout()->onMouseMove(getMouseCoordsInternal());
if (PMONITOR && PMONITOR != g_pCompositor->m_pLastMonitor && (*PMOUSEFOCUSMON || refocus) && m_pForcedFocus.expired())
g_pCompositor->setActiveMonitor(PMONITOR);
if (g_pSessionLockManager->isSessionLocked()) {
const auto PSLS = PMONITOR ? g_pSessionLockManager->getSessionLockSurfaceForMonitor(PMONITOR->ID) : nullptr;
if (!PSLS)
return;
foundSurface = PSLS->surface.lock()->surface();
surfacePos = PMONITOR->vecPosition;
}
if (!foundSurface)
foundSurface = g_pCompositor->vectorToLayerPopupSurface(mouseCoords, PMONITOR, &surfaceCoords, &pFoundLayerSurface);
// overlays are above fullscreen
if (!foundSurface)
foundSurface = g_pCompositor->vectorToLayerSurface(mouseCoords, &PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &surfaceCoords, &pFoundLayerSurface);
// also IME popups
if (!foundSurface) {
auto popup = g_pInputManager->m_sIMERelay.popupFromCoords(mouseCoords);
if (popup) {
foundSurface = popup->getWlrSurface();
surfacePos = popup->globalBox().pos();
}
}
// also top layers
if (!foundSurface)
foundSurface = g_pCompositor->vectorToLayerSurface(mouseCoords, &PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], &surfaceCoords, &pFoundLayerSurface);
// then, we check if the workspace doesnt have a fullscreen window
const auto PWORKSPACE = PMONITOR->activeWorkspace;
if (PWORKSPACE->m_bHasFullscreenWindow && !foundSurface && PWORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL) {
pFoundWindow = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID);
if (!pFoundWindow) {
// what the fuck, somehow happens occasionally??
PWORKSPACE->m_bHasFullscreenWindow = false;
return;
}
const auto PWINDOWIDEAL = g_pCompositor->vectorToWindowUnified(mouseCoords, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
if (PWINDOWIDEAL &&
((PWINDOWIDEAL->m_bIsFloating && PWINDOWIDEAL->m_bCreatedOverFullscreen) /* floating over fullscreen */
|| (PMONITOR->activeSpecialWorkspace == PWINDOWIDEAL->m_pWorkspace) /* on an open special workspace */))
pFoundWindow = PWINDOWIDEAL;
if (!pFoundWindow->m_bIsX11) {
foundSurface = g_pCompositor->vectorWindowToSurface(mouseCoords, pFoundWindow, surfaceCoords);
surfacePos = Vector2D(-1337, -1337);
} else {
foundSurface = pFoundWindow->m_pWLSurface.wlr();
surfacePos = pFoundWindow->m_vRealPosition.value();
}
}
// then windows
if (!foundSurface) {
if (PWORKSPACE->m_bHasFullscreenWindow && PWORKSPACE->m_efFullscreenMode == FULLSCREEN_MAXIMIZED) {
if (!foundSurface) {
if (PMONITOR->activeSpecialWorkspace) {
pFoundWindow = g_pCompositor->vectorToWindowUnified(mouseCoords, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
if (pFoundWindow && !pFoundWindow->onSpecialWorkspace()) {
pFoundWindow = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID);
}
} else {
// if we have a maximized window, allow focusing on a bar or something if in reserved area.
if (g_pCompositor->isPointOnReservedArea(mouseCoords, PMONITOR)) {
foundSurface = g_pCompositor->vectorToLayerSurface(mouseCoords, &PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &surfaceCoords,
&pFoundLayerSurface);
}
if (!foundSurface) {
pFoundWindow = g_pCompositor->vectorToWindowUnified(mouseCoords, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
if (!(pFoundWindow && pFoundWindow->m_bIsFloating && pFoundWindow->m_bCreatedOverFullscreen))
pFoundWindow = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID);
}
}
}
} else {
pFoundWindow = g_pCompositor->vectorToWindowUnified(mouseCoords, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
}
if (pFoundWindow) {
if (!pFoundWindow->m_bIsX11) {
foundSurface = g_pCompositor->vectorWindowToSurface(mouseCoords, pFoundWindow, surfaceCoords);
} else {
foundSurface = pFoundWindow->m_pWLSurface.wlr();
surfacePos = pFoundWindow->m_vRealPosition.value();
}
}
}
// then surfaces below
if (!foundSurface)
foundSurface = g_pCompositor->vectorToLayerSurface(mouseCoords, &PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &surfaceCoords, &pFoundLayerSurface);
if (!foundSurface)
foundSurface =
g_pCompositor->vectorToLayerSurface(mouseCoords, &PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &surfaceCoords, &pFoundLayerSurface);
if (g_pCompositor->m_pLastMonitor->output->software_cursor_locks > 0)
g_pCompositor->scheduleFrameForMonitor(g_pCompositor->m_pLastMonitor);
if (!foundSurface) {
if (!m_bEmptyFocusCursorSet) {
if (*PRESIZEONBORDER && *PRESIZECURSORICON && m_eBorderIconDirection != BORDERICON_NONE) {
m_eBorderIconDirection = BORDERICON_NONE;
unsetCursorImage();
}
// TODO: maybe wrap?
if (m_ecbClickBehavior == CLICKMODE_KILL)
setCursorImageOverride("crosshair");
else
setCursorImageOverride("left_ptr");
m_bEmptyFocusCursorSet = true;
}
wlr_seat_pointer_clear_focus(g_pCompositor->m_sSeat.seat);
m_pLastMouseSurface = 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);
return;
}
m_bEmptyFocusCursorSet = false;
Vector2D surfaceLocal = surfacePos == Vector2D(-1337, -1337) ? surfaceCoords : mouseCoords - surfacePos;
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);
surfaceLocal = mouseCoords - surfacePos + Vector2D(geom.x, geom.y);
}
if (pFoundWindow && pFoundWindow->m_bIsX11) // for x11 force scale zero
surfaceLocal = surfaceLocal * pFoundWindow->m_fX11SurfaceScaledBy;
bool allowKeyboardRefocus = true;
if (!refocus && g_pCompositor->m_pLastFocus) {
const auto PLS = g_pCompositor->getLayerSurfaceFromSurface(g_pCompositor->m_pLastFocus);
if (PLS && PLS->layerSurface->current.keyboard_interactive == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE)
allowKeyboardRefocus = false;
}
// set the values for use
if (refocus) {
m_pFoundLSToFocus = pFoundLayerSurface;
m_pFoundWindowToFocus = pFoundWindow;
m_pFoundSurfaceToFocus = foundSurface;
}
if (currentlyDraggedWindow.lock() && pFoundWindow != currentlyDraggedWindow.lock()) {
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);
return;
}
if (pFoundWindow && foundSurface == pFoundWindow->m_pWLSurface.wlr() && !m_bCursorImageOverridden) {
const auto BOX = pFoundWindow->getWindowMainSurfaceBox();
if (!VECINRECT(mouseCoords, BOX.x, BOX.y, BOX.x + BOX.width, BOX.y + BOX.height))
setCursorImageOverride("left_ptr");
else
restoreCursorIconToApp();
}
if (pFoundWindow) {
// change cursor icon if hovering over border
if (*PRESIZEONBORDER && *PRESIZECURSORICON) {
if (!pFoundWindow->m_bIsFullscreen && !pFoundWindow->hasPopupAt(mouseCoords)) {
setCursorIconOnBorder(pFoundWindow);
} else if (m_eBorderIconDirection != BORDERICON_NONE) {
unsetCursorImage();
}
}
if (FOLLOWMOUSE != 1 && !refocus) {
if (pFoundWindow != g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow.lock() &&
((pFoundWindow->m_bIsFloating && *PFLOATBEHAVIOR == 2) ||
(g_pCompositor->m_pLastWindow.lock()->m_bIsFloating != pFoundWindow->m_bIsFloating && *PFLOATBEHAVIOR != 0))) {
// 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);
}
if (pFoundWindow == g_pCompositor->m_pLastWindow.lock()) {
m_pLastMouseSurface = foundSurface;
wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, foundSurface, surfaceLocal.x, surfaceLocal.y);
}
if (FOLLOWMOUSE != 0 || pFoundWindow == g_pCompositor->m_pLastWindow.lock())
wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, time, surfaceLocal.x, surfaceLocal.y);
m_bLastFocusOnLS = false;
return; // don't enter any new surfaces
} else {
if (allowKeyboardRefocus && ((FOLLOWMOUSE != 3 && (*PMOUSEREFOCUS || m_pLastMouseFocus.lock() != pFoundWindow)) || refocus)) {
if (m_pLastMouseFocus.lock() != pFoundWindow || g_pCompositor->m_pLastWindow.lock() != pFoundWindow || g_pCompositor->m_pLastFocus != foundSurface || refocus) {
m_pLastMouseFocus = pFoundWindow;
// TODO: this looks wrong. When over a popup, it constantly is switching.
// Temp fix until that's figured out. Otherwise spams windowrule lookups and other shit.
if (m_pLastMouseFocus.lock() != pFoundWindow || g_pCompositor->m_pLastWindow.lock() != pFoundWindow)
g_pCompositor->focusWindow(pFoundWindow, foundSurface);
else
g_pCompositor->focusSurface(foundSurface, pFoundWindow);
}
}
}
m_bLastFocusOnLS = false;
} else {
if (*PRESIZEONBORDER && *PRESIZECURSORICON && m_eBorderIconDirection != BORDERICON_NONE) {
m_eBorderIconDirection = BORDERICON_NONE;
unsetCursorImage();
}
if (pFoundLayerSurface && (pFoundLayerSurface->layerSurface->current.keyboard_interactive != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE) && FOLLOWMOUSE != 3 &&
allowKeyboardRefocus) {
g_pCompositor->focusSurface(foundSurface);
}
if (pFoundLayerSurface)
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);
}
void CInputManager::onMouseButton(wlr_pointer_button_event* e) {
EMIT_HOOK_EVENT_CANCELLABLE("mouseButton", e);
PROTO::idle->onActivity();
m_tmrLastCursorMovement.reset();
if (e->state == WL_POINTER_BUTTON_STATE_PRESSED) {
m_lCurrentlyHeldButtons.push_back(e->button);
} else {
if (std::find_if(m_lCurrentlyHeldButtons.begin(), m_lCurrentlyHeldButtons.end(), [&](const auto& other) { return other == e->button; }) == m_lCurrentlyHeldButtons.end())
return;
std::erase_if(m_lCurrentlyHeldButtons, [&](const auto& other) { return other == e->button; });
}
switch (m_ecbClickBehavior) {
case CLICKMODE_DEFAULT: processMouseDownNormal(e); break;
case CLICKMODE_KILL: processMouseDownKill(e); break;
default: break;
}
if (m_bFocusHeldByButtons && m_lCurrentlyHeldButtons.empty() && e->state == WL_POINTER_BUTTON_STATE_RELEASED) {
if (m_bRefocusHeldByButtons)
refocus();
else
simulateMouseMovement();
m_bFocusHeldByButtons = false;
m_bRefocusHeldByButtons = false;
}
}
void CInputManager::processMouseRequest(wlr_seat_pointer_request_set_cursor_event* e) {
if (!cursorImageUnlocked())
return;
Debug::log(LOG, "cursorImage request: surface {:x}", (uintptr_t)e->surface);
if (e->seat_client == g_pCompositor->m_sSeat.seat->pointer_state.focused_client) {
if (e->surface != 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(e->surface, e->hotspot_x, e->hotspot_y);
}
}
void CInputManager::restoreCursorIconToApp() {
if (m_sCursorSurfaceInfo.inUse)
return;
if (m_sCursorSurfaceInfo.hidden) {
g_pHyprRenderer->setCursorSurface(nullptr, 0, 0);
return;
}
if (m_sCursorSurfaceInfo.name.empty()) {
if (m_sCursorSurfaceInfo.wlSurface.exists())
g_pHyprRenderer->setCursorSurface(m_sCursorSurfaceInfo.wlSurface.wlr(), m_sCursorSurfaceInfo.vHotspot.x, m_sCursorSurfaceInfo.vHotspot.y);
} else {
g_pHyprRenderer->setCursorFromName(m_sCursorSurfaceInfo.name);
}
m_sCursorSurfaceInfo.inUse = true;
}
void CInputManager::setCursorImageOverride(const std::string& name) {
if (m_bCursorImageOverridden)
return;
m_sCursorSurfaceInfo.inUse = false;
g_pHyprRenderer->setCursorFromName(name);
}
bool CInputManager::cursorImageUnlocked() {
if (m_ecbClickBehavior == CLICKMODE_KILL)
return false;
if (m_bCursorImageOverridden)
return false;
return true;
}
eClickBehaviorMode CInputManager::getClickMode() {
return m_ecbClickBehavior;
}
void CInputManager::setClickMode(eClickBehaviorMode mode) {
switch (mode) {
case CLICKMODE_DEFAULT:
Debug::log(LOG, "SetClickMode: DEFAULT");
m_ecbClickBehavior = CLICKMODE_DEFAULT;
g_pHyprRenderer->setCursorFromName("left_ptr");
break;
case CLICKMODE_KILL:
Debug::log(LOG, "SetClickMode: KILL");
m_ecbClickBehavior = CLICKMODE_KILL;
// remove constraints
g_pInputManager->unconstrainMouse();
refocus();
// set cursor
g_pHyprRenderer->setCursorFromName("crosshair");
break;
default: break;
}
}
void CInputManager::processMouseDownNormal(wlr_pointer_button_event* e) {
// notify the keybind manager
static auto PPASSMOUSE = CConfigValue<Hyprlang::INT>("binds:pass_mouse_when_bound");
const auto PASS = g_pKeybindManager->onMouseEvent(e);
static auto PFOLLOWMOUSE = CConfigValue<Hyprlang::INT>("input:follow_mouse");
static auto PRESIZEONBORDER = CConfigValue<Hyprlang::INT>("general:resize_on_border");
static auto PBORDERSIZE = CConfigValue<Hyprlang::INT>("general:border_size");
static auto PBORDERGRABEXTEND = CConfigValue<Hyprlang::INT>("general:extend_border_grab_area");
const auto BORDER_GRAB_AREA = *PRESIZEONBORDER ? *PBORDERSIZE + *PBORDERGRABEXTEND : 0;
if (!PASS && !*PPASSMOUSE)
return;
const auto mouseCoords = g_pInputManager->getMouseCoordsInternal();
const auto w = g_pCompositor->vectorToWindowUnified(mouseCoords, ALLOW_FLOATING | RESERVED_EXTENTS | INPUT_EXTENTS);
if (w && !m_bLastFocusOnLS && w->checkInputOnDecos(INPUT_TYPE_BUTTON, mouseCoords, e))
return;
// clicking on border triggers resize
// TODO detect click on LS properly
if (*PRESIZEONBORDER && !m_bLastFocusOnLS && e->state == WL_POINTER_BUTTON_STATE_PRESSED) {
if (w && !w->m_bIsFullscreen) {
const CBox real = {w->m_vRealPosition.value().x, w->m_vRealPosition.value().y, w->m_vRealSize.value().x, w->m_vRealSize.value().y};
const CBox grab = {real.x - BORDER_GRAB_AREA, real.y - BORDER_GRAB_AREA, real.width + 2 * BORDER_GRAB_AREA, real.height + 2 * BORDER_GRAB_AREA};
if ((grab.containsPoint(mouseCoords) && (!real.containsPoint(mouseCoords) || w->isInCurvedCorner(mouseCoords.x, mouseCoords.y))) && !w->hasPopupAt(mouseCoords)) {
g_pKeybindManager->resizeWithBorder(e);
return;
}
}
}
switch (e->state) {
case WL_POINTER_BUTTON_STATE_PRESSED:
if (*PFOLLOWMOUSE == 3) // don't refocus on full loose
break;
if ((!g_pCompositor->m_sSeat.mouse || !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
if (m_lCurrentlyHeldButtons.size() == 1) {
const auto COPY = m_lCurrentlyHeldButtons;
m_lCurrentlyHeldButtons.clear();
refocus();
m_lCurrentlyHeldButtons = COPY;
} else
refocus();
}
// if clicked on a floating window make it top
if (g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow.lock()->m_bIsFloating)
g_pCompositor->changeWindowZOrder(g_pCompositor->m_pLastWindow.lock(), true);
break;
case WL_POINTER_BUTTON_STATE_RELEASED: break;
}
// 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->time_msec, e->button, e->state);
if (const auto PMON = g_pCompositor->getMonitorFromVector(mouseCoords); PMON != g_pCompositor->m_pLastMonitor && PMON)
g_pCompositor->setActiveMonitor(PMON);
}
void CInputManager::processMouseDownKill(wlr_pointer_button_event* e) {
switch (e->state) {
case WL_POINTER_BUTTON_STATE_PRESSED: {
const auto PWINDOW = g_pCompositor->vectorToWindowUnified(getMouseCoordsInternal(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
if (!PWINDOW) {
Debug::log(ERR, "Cannot kill invalid window!");
break;
}
// kill the mf
kill(PWINDOW->getPID(), SIGKILL);
break;
}
case WL_POINTER_BUTTON_STATE_RELEASED: break;
default: break;
}
// reset click behavior mode
m_ecbClickBehavior = CLICKMODE_DEFAULT;
}
void CInputManager::onMouseWheel(wlr_pointer_axis_event* e) {
static auto POFFWINDOWAXIS = CConfigValue<Hyprlang::INT>("input:off_window_axis_events");
static auto PINPUTSCROLLFACTOR = CConfigValue<Hyprlang::FLOAT>("input:scroll_factor");
static auto PTOUCHPADSCROLLFACTOR = CConfigValue<Hyprlang::FLOAT>("input:touchpad:scroll_factor");
auto factor = (*PTOUCHPADSCROLLFACTOR <= 0.f || e->source == WL_POINTER_AXIS_SOURCE_FINGER ? *PTOUCHPADSCROLLFACTOR : *PINPUTSCROLLFACTOR);
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::idle->onActivity();
if (!passEvent)
return;
if (!m_bLastFocusOnLS) {
const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal();
const auto PWINDOW = g_pCompositor->vectorToWindowUnified(MOUSECOORDS, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
if (PWINDOW && PWINDOW->checkInputOnDecos(INPUT_TYPE_AXIS, MOUSECOORDS, e))
return;
if (PWINDOW && *POFFWINDOWAXIS != 1) {
const auto BOX = PWINDOW->getWindowMainSurfaceBox();
if (!BOX.containsPoint(MOUSECOORDS) && !PWINDOW->hasPopupAt(MOUSECOORDS)) {
if (*POFFWINDOWAXIS == 0)
return;
const auto TEMPCURX = std::clamp(MOUSECOORDS.x, BOX.x, BOX.x + BOX.w - 1);
const auto TEMPCURY = std::clamp(MOUSECOORDS.y, BOX.y, BOX.y + BOX.h - 1);
if (*POFFWINDOWAXIS == 3)
wlr_cursor_warp(g_pCompositor->m_sWLRCursor, nullptr, TEMPCURX, TEMPCURY);
wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, e->time_msec, TEMPCURX - BOX.x, TEMPCURY - BOX.y);
wlr_seat_pointer_notify_frame(g_pCompositor->m_sSeat.seat);
}
}
}
wlr_seat_pointer_notify_axis(g_pCompositor->m_sSeat.seat, e->time_msec, e->orientation, factor * e->delta, std::round(factor * e->delta_discrete), e->source,
WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL);
}
Vector2D CInputManager::getMouseCoordsInternal() {
return Vector2D(g_pCompositor->m_sWLRCursor->x, g_pCompositor->m_sWLRCursor->y);
}
void CInputManager::newKeyboard(wlr_input_device* keyboard) {
const auto PNEWKEYBOARD = &m_lKeyboards.emplace_back();
PNEWKEYBOARD->keyboard = keyboard;
try {
PNEWKEYBOARD->name = getNameForNewDevice(keyboard->name);
} catch (std::exception& e) {
Debug::log(ERR, "Keyboard had no name???"); // logic error
}
PNEWKEYBOARD->hyprListener_keyboardMod.initCallback(&wlr_keyboard_from_input_device(keyboard)->events.modifiers, &Events::listener_keyboardMod, PNEWKEYBOARD, "Keyboard");
PNEWKEYBOARD->hyprListener_keyboardKey.initCallback(&wlr_keyboard_from_input_device(keyboard)->events.key, &Events::listener_keyboardKey, PNEWKEYBOARD, "Keyboard");
PNEWKEYBOARD->hyprListener_keyboardDestroy.initCallback(&keyboard->events.destroy, &Events::listener_keyboardDestroy, PNEWKEYBOARD, "Keyboard");
PNEWKEYBOARD->hyprListener_keyboardKeymap.initCallback(
&wlr_keyboard_from_input_device(keyboard)->events.keymap,
[&](void* owner, void* data) {
const auto PKEYBOARD = (SKeyboard*)owner;
const auto LAYOUT = getActiveLayoutForKeyboard(PKEYBOARD);
g_pEventManager->postEvent(SHyprIPCEvent{"activelayout", PKEYBOARD->name + "," + LAYOUT});
EMIT_HOOK_EVENT("activeLayout", (std::vector<void*>{PKEYBOARD, (void*)&LAYOUT}));
},
PNEWKEYBOARD, "Keyboard");
disableAllKeyboards(false);
m_pActiveKeyboard = PNEWKEYBOARD;
PNEWKEYBOARD->active = true;
applyConfigToKeyboard(PNEWKEYBOARD);
wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, wlr_keyboard_from_input_device(keyboard));
Debug::log(LOG, "New keyboard created, pointers Hypr: {:x} and WLR: {:x}", (uintptr_t)PNEWKEYBOARD, (uintptr_t)keyboard);
}
void CInputManager::newVirtualKeyboard(SP<CVirtualKeyboard> keyboard) {
const auto PNEWKEYBOARD = &m_lKeyboards.emplace_back();
PNEWKEYBOARD->keyboard = &keyboard->wlr()->base;
PNEWKEYBOARD->isVirtual = true;
PNEWKEYBOARD->virtKeyboard = keyboard;
try {
PNEWKEYBOARD->name = getNameForNewDevice(keyboard->wlr()->base.name);
} catch (std::exception& e) {
Debug::log(ERR, "Keyboard had no name???"); // logic error
}
PNEWKEYBOARD->hyprListener_keyboardMod.initCallback(&keyboard->wlr()->events.modifiers, &Events::listener_keyboardMod, PNEWKEYBOARD, "VKeyboard");
PNEWKEYBOARD->hyprListener_keyboardKey.initCallback(&keyboard->wlr()->events.key, &Events::listener_keyboardKey, PNEWKEYBOARD, "VKeyboard");
PNEWKEYBOARD->hyprListener_keyboardDestroy.initCallback(&keyboard->wlr()->base.events.destroy, &Events::listener_keyboardDestroy, PNEWKEYBOARD, "VKeyboard");
PNEWKEYBOARD->hyprListener_keyboardKeymap.initCallback(
&keyboard->wlr()->events.keymap,
[&](void* owner, void* data) {
const auto PKEYBOARD = (SKeyboard*)owner;
const auto LAYOUT = getActiveLayoutForKeyboard(PKEYBOARD);
g_pEventManager->postEvent(SHyprIPCEvent{"activelayout", PKEYBOARD->name + "," + LAYOUT});
EMIT_HOOK_EVENT("activeLayout", (std::vector<void*>{PKEYBOARD, (void*)&LAYOUT}));
},
PNEWKEYBOARD, "VKeyboard");
// TODO: this pointer pass sucks.
PNEWKEYBOARD->listeners.destroyVKeyboard = keyboard->events.destroy.registerListener([this, PNEWKEYBOARD](std::any data) { destroyKeyboard(PNEWKEYBOARD); });
disableAllKeyboards(true);
m_pActiveKeyboard = PNEWKEYBOARD;
PNEWKEYBOARD->active = true;
applyConfigToKeyboard(PNEWKEYBOARD);
wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, keyboard->wlr());
Debug::log(LOG, "New virtual keyboard created, pointers Hypr: {:x} and WLR: {:x}", (uintptr_t)PNEWKEYBOARD, (uintptr_t)keyboard->wlr());
}
void CInputManager::setKeyboardLayout() {
for (auto& k : m_lKeyboards)
applyConfigToKeyboard(&k);
g_pKeybindManager->updateXKBTranslationState();
}
void CInputManager::applyConfigToKeyboard(SKeyboard* pKeyboard) {
auto devname = pKeyboard->name;
const auto HASCONFIG = g_pConfigManager->deviceConfigExists(devname);
Debug::log(LOG, "ApplyConfigToKeyboard for \"{}\", hasconfig: {}", pKeyboard->name, (int)HASCONFIG);
ASSERT(pKeyboard);
if (!wlr_keyboard_from_input_device(pKeyboard->keyboard))
return;
const auto REPEATRATE = g_pConfigManager->getDeviceInt(devname, "repeat_rate", "input:repeat_rate");
const auto REPEATDELAY = g_pConfigManager->getDeviceInt(devname, "repeat_delay", "input:repeat_delay");
const auto NUMLOCKON = g_pConfigManager->getDeviceInt(devname, "numlock_by_default", "input:numlock_by_default");
const auto RESOLVEBINDSBYSYM = g_pConfigManager->getDeviceInt(devname, "resolve_binds_by_sym", "input:resolve_binds_by_sym");
const auto FILEPATH = g_pConfigManager->getDeviceString(devname, "kb_file", "input:kb_file");
const auto RULES = g_pConfigManager->getDeviceString(devname, "kb_rules", "input:kb_rules");
const auto MODEL = g_pConfigManager->getDeviceString(devname, "kb_model", "input:kb_model");
const auto LAYOUT = g_pConfigManager->getDeviceString(devname, "kb_layout", "input:kb_layout");
const auto VARIANT = g_pConfigManager->getDeviceString(devname, "kb_variant", "input:kb_variant");
const auto OPTIONS = g_pConfigManager->getDeviceString(devname, "kb_options", "input:kb_options");
const auto ENABLED = HASCONFIG ? g_pConfigManager->getDeviceInt(devname, "enabled") : true;
pKeyboard->enabled = ENABLED;
pKeyboard->resolveBindsBySym = RESOLVEBINDSBYSYM;
try {
if (NUMLOCKON == pKeyboard->numlockOn && REPEATDELAY == pKeyboard->repeatDelay && REPEATRATE == pKeyboard->repeatRate && RULES != "" &&
RULES == pKeyboard->currentRules.rules && MODEL == pKeyboard->currentRules.model && LAYOUT == pKeyboard->currentRules.layout &&
VARIANT == pKeyboard->currentRules.variant && OPTIONS == pKeyboard->currentRules.options && FILEPATH == pKeyboard->xkbFilePath) {
Debug::log(LOG, "Not applying config to keyboard, it did not change.");
return;
}
} catch (std::exception& e) {
// can be libc errors for null std::string
// we can ignore those and just apply
}
wlr_keyboard_set_repeat_info(wlr_keyboard_from_input_device(pKeyboard->keyboard), std::max(0, REPEATRATE), std::max(0, REPEATDELAY));
pKeyboard->repeatDelay = REPEATDELAY;
pKeyboard->repeatRate = REPEATRATE;
pKeyboard->numlockOn = NUMLOCKON;
pKeyboard->xkbFilePath = FILEPATH;
xkb_rule_names rules = {.rules = RULES.c_str(), .model = MODEL.c_str(), .layout = LAYOUT.c_str(), .variant = VARIANT.c_str(), .options = OPTIONS.c_str()};
pKeyboard->currentRules.rules = RULES;
pKeyboard->currentRules.model = MODEL;
pKeyboard->currentRules.variant = VARIANT;
pKeyboard->currentRules.options = OPTIONS;
pKeyboard->currentRules.layout = LAYOUT;
const auto CONTEXT = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!CONTEXT) {
Debug::log(ERR, "applyConfigToKeyboard: CONTEXT null??");
return;
}
Debug::log(LOG, "Attempting to create a keymap for layout {} with variant {} (rules: {}, model: {}, options: {})", rules.layout, rules.variant, rules.rules, rules.model,
rules.options);
xkb_keymap* KEYMAP = NULL;
if (!FILEPATH.empty()) {
auto path = absolutePath(FILEPATH, g_pConfigManager->configCurrentPath);
if (FILE* const KEYMAPFILE = fopen(path.c_str(), "r"); !KEYMAPFILE)
Debug::log(ERR, "Cannot open input:kb_file= file for reading");
else {
KEYMAP = xkb_keymap_new_from_file(CONTEXT, KEYMAPFILE, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
fclose(KEYMAPFILE);
}
}
if (!KEYMAP)
KEYMAP = xkb_keymap_new_from_names(CONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!KEYMAP) {
g_pConfigManager->addParseError("Invalid keyboard layout passed. ( rules: " + RULES + ", model: " + MODEL + ", variant: " + VARIANT + ", options: " + OPTIONS +
", layout: " + LAYOUT + " )");
Debug::log(ERR, "Keyboard layout {} with variant {} (rules: {}, model: {}, options: {}) couldn't have been loaded.", rules.layout, rules.variant, rules.rules, rules.model,
rules.options);
memset(&rules, 0, sizeof(rules));
pKeyboard->currentRules.rules = "";
pKeyboard->currentRules.model = "";
pKeyboard->currentRules.variant = "";
pKeyboard->currentRules.options = "";
pKeyboard->currentRules.layout = "us";
KEYMAP = xkb_keymap_new_from_names(CONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
}
wlr_keyboard_set_keymap(wlr_keyboard_from_input_device(pKeyboard->keyboard), KEYMAP);
pKeyboard->updateXKBTranslationState();
wlr_keyboard_modifiers wlrMods = {0};
if (NUMLOCKON == 1) {
// lock numlock
const auto IDX = xkb_map_mod_get_index(KEYMAP, XKB_MOD_NAME_NUM);
if (IDX != XKB_MOD_INVALID)
wlrMods.locked |= (uint32_t)1 << IDX;
}
if (wlrMods.locked != 0)
wlr_keyboard_notify_modifiers(wlr_keyboard_from_input_device(pKeyboard->keyboard), 0, 0, wlrMods.locked, 0);
xkb_keymap_unref(KEYMAP);
xkb_context_unref(CONTEXT);
const auto LAYOUTSTR = getActiveLayoutForKeyboard(pKeyboard);
g_pEventManager->postEvent(SHyprIPCEvent{"activelayout", pKeyboard->name + "," + LAYOUTSTR});
EMIT_HOOK_EVENT("activeLayout", (std::vector<void*>{pKeyboard, (void*)&LAYOUTSTR}));
Debug::log(LOG, "Set the keyboard layout to {} and variant to {} for keyboard \"{}\"", pKeyboard->currentRules.layout, pKeyboard->currentRules.variant,
pKeyboard->keyboard->name);
}
void CInputManager::newVirtualMouse(SP<CVirtualPointer> mouse) {
const auto PMOUSE = &m_lMice.emplace_back();
PMOUSE->mouse = &mouse->wlr()->base;
PMOUSE->virtualPointer = mouse;
PMOUSE->virt = true;
try {
PMOUSE->name = getNameForNewDevice(mouse->wlr()->base.name);
} catch (std::exception& e) {
Debug::log(ERR, "Mouse had no name???"); // logic error
}
wlr_cursor_attach_input_device(g_pCompositor->m_sWLRCursor, &mouse->wlr()->base);
PMOUSE->connected = true;
setPointerConfigs();
PMOUSE->hyprListener_destroyMouse.initCallback(&mouse->wlr()->base.events.destroy, &Events::listener_destroyMouse, PMOUSE, "Mouse");
// TODO: this pointer pass sucks.
PMOUSE->listeners.destroyMouse = mouse->events.destroy.registerListener([this, PMOUSE](std::any data) { destroyMouse(PMOUSE->mouse); });
g_pCompositor->m_sSeat.mouse = PMOUSE;
m_tmrLastCursorMovement.reset();
Debug::log(LOG, "New virtual mouse created, pointer WLR: {:x}", (uintptr_t)mouse->wlr());
}
void CInputManager::newMouse(wlr_input_device* mouse) {
m_lMice.emplace_back();
const auto PMOUSE = &m_lMice.back();
PMOUSE->mouse = mouse;
try {
PMOUSE->name = getNameForNewDevice(mouse->name);
} catch (std::exception& e) {
Debug::log(ERR, "Mouse had no name???"); // logic error
}
if (wlr_input_device_is_libinput(mouse)) {
const auto LIBINPUTDEV = (libinput_device*)wlr_libinput_get_device_handle(mouse);
Debug::log(LOG, "New mouse has libinput sens {:.2f} ({:.2f}) with accel profile {} ({})", libinput_device_config_accel_get_speed(LIBINPUTDEV),
libinput_device_config_accel_get_default_speed(LIBINPUTDEV), (int)libinput_device_config_accel_get_profile(LIBINPUTDEV),
(int)libinput_device_config_accel_get_default_profile(LIBINPUTDEV));
}
wlr_cursor_attach_input_device(g_pCompositor->m_sWLRCursor, mouse);
PMOUSE->connected = true;
setPointerConfigs();
PMOUSE->hyprListener_destroyMouse.initCallback(&mouse->events.destroy, &Events::listener_destroyMouse, PMOUSE, "Mouse");
g_pCompositor->m_sSeat.mouse = PMOUSE;
m_tmrLastCursorMovement.reset();
Debug::log(LOG, "New mouse created, pointer WLR: {:x}", (uintptr_t)mouse);
}
void CInputManager::setPointerConfigs() {
for (auto& m : m_lMice) {
const auto PPOINTER = &m;
auto devname = PPOINTER->name;
const auto HASCONFIG = g_pConfigManager->deviceConfigExists(devname);
if (HASCONFIG) {
const auto ENABLED = g_pConfigManager->getDeviceInt(devname, "enabled");
if (ENABLED && !m.connected) {
wlr_cursor_attach_input_device(g_pCompositor->m_sWLRCursor, m.mouse);
m.connected = true;
} else if (!ENABLED && m.connected) {
wlr_cursor_detach_input_device(g_pCompositor->m_sWLRCursor, m.mouse);
m.connected = false;
}
}
if (wlr_input_device_is_libinput(m.mouse)) {
const auto LIBINPUTDEV = (libinput_device*)wlr_libinput_get_device_handle(m.mouse);
double touchw = 0, touchh = 0;
const auto ISTOUCHPAD = libinput_device_has_capability(LIBINPUTDEV, LIBINPUT_DEVICE_CAP_POINTER) &&
libinput_device_get_size(LIBINPUTDEV, &touchw, &touchh) == 0; // pointer with size is a touchpad
if (g_pConfigManager->getDeviceInt(devname, "clickfinger_behavior", "input:touchpad:clickfinger_behavior") == 0) // toggle software buttons or clickfinger
libinput_device_config_click_set_method(LIBINPUTDEV, LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS);
else
libinput_device_config_click_set_method(LIBINPUTDEV, LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER);
if (g_pConfigManager->getDeviceInt(devname, "left_handed", "input:left_handed") == 0)
libinput_device_config_left_handed_set(LIBINPUTDEV, 0);
else
libinput_device_config_left_handed_set(LIBINPUTDEV, 1);
if (libinput_device_config_middle_emulation_is_available(LIBINPUTDEV)) { // middleclick on r+l mouse button pressed
if (g_pConfigManager->getDeviceInt(devname, "middle_button_emulation", "input:touchpad:middle_button_emulation") == 1)
libinput_device_config_middle_emulation_set_enabled(LIBINPUTDEV, LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
else
libinput_device_config_middle_emulation_set_enabled(LIBINPUTDEV, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
const auto TAP_MAP = g_pConfigManager->getDeviceString(devname, "tap_button_map", "input:touchpad:tap_button_map");
if (TAP_MAP == "" || TAP_MAP == "lrm")
libinput_device_config_tap_set_button_map(LIBINPUTDEV, LIBINPUT_CONFIG_TAP_MAP_LRM);
else if (TAP_MAP == "lmr")
libinput_device_config_tap_set_button_map(LIBINPUTDEV, LIBINPUT_CONFIG_TAP_MAP_LMR);
else
Debug::log(WARN, "Tap button mapping unknown");
}
const auto SCROLLMETHOD = g_pConfigManager->getDeviceString(devname, "scroll_method", "input:scroll_method");
if (SCROLLMETHOD == "") {
libinput_device_config_scroll_set_method(LIBINPUTDEV, libinput_device_config_scroll_get_default_method(LIBINPUTDEV));
} else if (SCROLLMETHOD == "no_scroll") {
libinput_device_config_scroll_set_method(LIBINPUTDEV, LIBINPUT_CONFIG_SCROLL_NO_SCROLL);
} else if (SCROLLMETHOD == "2fg") {
libinput_device_config_scroll_set_method(LIBINPUTDEV, LIBINPUT_CONFIG_SCROLL_2FG);
} else if (SCROLLMETHOD == "edge") {
libinput_device_config_scroll_set_method(LIBINPUTDEV, LIBINPUT_CONFIG_SCROLL_EDGE);
} else if (SCROLLMETHOD == "on_button_down") {
libinput_device_config_scroll_set_method(LIBINPUTDEV, LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
} else {
Debug::log(WARN, "Scroll method unknown");
}
if (g_pConfigManager->getDeviceInt(devname, "tap-and-drag", "input:touchpad:tap-and-drag") == 0)
libinput_device_config_tap_set_drag_enabled(LIBINPUTDEV, LIBINPUT_CONFIG_DRAG_DISABLED);
else
libinput_device_config_tap_set_drag_enabled(LIBINPUTDEV, LIBINPUT_CONFIG_DRAG_ENABLED);
if (g_pConfigManager->getDeviceInt(devname, "drag_lock", "input:touchpad:drag_lock") == 0)
libinput_device_config_tap_set_drag_lock_enabled(LIBINPUTDEV, LIBINPUT_CONFIG_DRAG_LOCK_DISABLED);
else
libinput_device_config_tap_set_drag_lock_enabled(LIBINPUTDEV, LIBINPUT_CONFIG_DRAG_LOCK_ENABLED);
if (libinput_device_config_tap_get_finger_count(LIBINPUTDEV)) // this is for tapping (like on a laptop)
if (g_pConfigManager->getDeviceInt(devname, "tap-to-click", "input:touchpad:tap-to-click") == 1)
libinput_device_config_tap_set_enabled(LIBINPUTDEV, LIBINPUT_CONFIG_TAP_ENABLED);
if (libinput_device_config_scroll_has_natural_scroll(LIBINPUTDEV)) {
if (ISTOUCHPAD)
libinput_device_config_scroll_set_natural_scroll_enabled(LIBINPUTDEV,
g_pConfigManager->getDeviceInt(devname, "natural_scroll", "input:touchpad:natural_scroll"));
else
libinput_device_config_scroll_set_natural_scroll_enabled(LIBINPUTDEV, g_pConfigManager->getDeviceInt(devname, "natural_scroll", "input:natural_scroll"));
}
if (libinput_device_config_dwt_is_available(LIBINPUTDEV)) {
const auto DWT =
static_cast<enum libinput_config_dwt_state>(g_pConfigManager->getDeviceInt(devname, "disable_while_typing", "input:touchpad:disable_while_typing") != 0);
libinput_device_config_dwt_set_enabled(LIBINPUTDEV, DWT);
}
const auto LIBINPUTSENS = std::clamp(g_pConfigManager->getDeviceFloat(devname, "sensitivity", "input:sensitivity"), -1.f, 1.f);
libinput_device_config_accel_set_speed(LIBINPUTDEV, LIBINPUTSENS);
const auto ACCELPROFILE = g_pConfigManager->getDeviceString(devname, "accel_profile", "input:accel_profile");
const auto SCROLLPOINTS = g_pConfigManager->getDeviceString(devname, "scroll_points", "input:scroll_points");
if (ACCELPROFILE.empty()) {
libinput_device_config_accel_set_profile(LIBINPUTDEV, libinput_device_config_accel_get_default_profile(LIBINPUTDEV));
} else if (ACCELPROFILE == "adaptive") {
libinput_device_config_accel_set_profile(LIBINPUTDEV, LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE);
} else if (ACCELPROFILE == "flat") {
libinput_device_config_accel_set_profile(LIBINPUTDEV, LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT);
} else if (ACCELPROFILE.starts_with("custom")) {
CVarList accelValues = {ACCELPROFILE, 0, ' '};
try {
double accelStep = std::stod(accelValues[1]);
std::vector<double> accelPoints;
for (size_t i = 2; i < accelValues.size(); ++i) {
accelPoints.push_back(std::stod(accelValues[i]));
}
const auto CONFIG = libinput_config_accel_create(LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM);
if (!SCROLLPOINTS.empty()) {
CVarList scrollValues = {SCROLLPOINTS, 0, ' '};
try {
double scrollStep = std::stod(scrollValues[0]);
std::vector<double> scrollPoints;
for (size_t i = 1; i < scrollValues.size(); ++i) {
scrollPoints.push_back(std::stod(scrollValues[i]));
}
libinput_config_accel_set_points(CONFIG, LIBINPUT_ACCEL_TYPE_SCROLL, scrollStep, scrollPoints.size(), scrollPoints.data());
} catch (std::exception& e) { Debug::log(ERR, "Invalid values in scroll_points"); }
}
libinput_config_accel_set_points(CONFIG, LIBINPUT_ACCEL_TYPE_MOTION, accelStep, accelPoints.size(), accelPoints.data());
libinput_device_config_accel_apply(LIBINPUTDEV, CONFIG);
libinput_config_accel_destroy(CONFIG);
} catch (std::exception& e) { Debug::log(ERR, "Invalid values in custom accel profile"); }
} else {
Debug::log(WARN, "Unknown acceleration profile, falling back to default");
}
const auto SCROLLBUTTON = g_pConfigManager->getDeviceInt(devname, "scroll_button", "input:scroll_button");
libinput_device_config_scroll_set_button(LIBINPUTDEV, SCROLLBUTTON == 0 ? libinput_device_config_scroll_get_default_button(LIBINPUTDEV) : SCROLLBUTTON);
const auto SCROLLBUTTONLOCK = g_pConfigManager->getDeviceInt(devname, "scroll_button_lock", "input:scroll_button_lock");
libinput_device_config_scroll_set_button_lock(LIBINPUTDEV,
SCROLLBUTTONLOCK == 0 ? LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED : LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED);
Debug::log(LOG, "Applied config to mouse {}, sens {:.2f}", m.name, LIBINPUTSENS);
}
}
}
void CInputManager::destroyKeyboard(SKeyboard* pKeyboard) {
pKeyboard->hyprListener_keyboardDestroy.removeCallback();
pKeyboard->hyprListener_keyboardMod.removeCallback();
pKeyboard->hyprListener_keyboardKey.removeCallback();
xkb_state_unref(pKeyboard->xkbTranslationState);
pKeyboard->xkbTranslationState = nullptr;
m_lKeyboards.remove(*pKeyboard);
if (m_lKeyboards.size() > 0) {
m_pActiveKeyboard = &m_lKeyboards.back();
m_pActiveKeyboard->active = true;
wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, wlr_keyboard_from_input_device(m_pActiveKeyboard->keyboard));
} else {
m_pActiveKeyboard = nullptr;
wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, nullptr);
}
}
void CInputManager::destroyMouse(wlr_input_device* mouse) {
for (auto& m : m_lMice) {
if (m.mouse == mouse) {
m_lMice.remove(m);
break;
}
}
g_pCompositor->m_sSeat.mouse = m_lMice.size() > 0 ? &m_lMice.front() : nullptr;
if (g_pCompositor->m_sSeat.mouse)
unconstrainMouse();
}
void CInputManager::updateKeyboardsLeds(wlr_input_device* pKeyboard) {
auto keyboard = wlr_keyboard_from_input_device(pKeyboard);
if (keyboard->xkb_state == NULL) {
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& kb : m_lKeyboards) {
if ((kb.isVirtual && shouldIgnoreVirtualKeyboard(&kb)) || kb.keyboard == pKeyboard)
continue;
wlr_keyboard_led_update(wlr_keyboard_from_input_device(kb.keyboard), leds);
}
}
void CInputManager::onKeyboardKey(wlr_keyboard_key_event* e, SKeyboard* pKeyboard) {
if (!pKeyboard->enabled)
return;
const bool DISALLOWACTION = pKeyboard->isVirtual && shouldIgnoreVirtualKeyboard(pKeyboard);
const auto EMAP = std::unordered_map<std::string, std::any>{{"keyboard", pKeyboard}, {"event", e}};
EMIT_HOOK_EVENT_CANCELLABLE("keyPress", EMAP);
static auto PDPMS = CConfigValue<Hyprlang::INT>("misc:key_press_enables_dpms");
if (*PDPMS && !g_pCompositor->m_bDPMSStateON) {
// enable dpms
g_pKeybindManager->dpms("on");
}
bool passEvent = DISALLOWACTION || g_pKeybindManager->onKeyEvent(e, pKeyboard);
PROTO::idle->onActivity();
if (passEvent) {
const auto IME = m_sIMERelay.m_pIME.lock();
if (IME && IME->hasGrab() && !DISALLOWACTION) {
IME->setKeyboard(wlr_keyboard_from_input_device(pKeyboard->keyboard));
IME->sendKey(e->time_msec, e->keycode, e->state);
} else {
wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, wlr_keyboard_from_input_device(pKeyboard->keyboard));
wlr_seat_keyboard_notify_key(g_pCompositor->m_sSeat.seat, e->time_msec, e->keycode, e->state);
}
updateKeyboardsLeds(pKeyboard->keyboard);
}
}
void CInputManager::onKeyboardMod(void* data, SKeyboard* pKeyboard) {
if (!pKeyboard->enabled)
return;
const bool DISALLOWACTION = pKeyboard->isVirtual && shouldIgnoreVirtualKeyboard(pKeyboard);
const auto ALLMODS = accumulateModsFromAllKBs();
auto MODS = wlr_keyboard_from_input_device(pKeyboard->keyboard)->modifiers;
MODS.depressed = ALLMODS;
const auto IME = m_sIMERelay.m_pIME.lock();
if (IME && IME->hasGrab() && !DISALLOWACTION) {
IME->setKeyboard(wlr_keyboard_from_input_device(pKeyboard->keyboard));
IME->sendMods(MODS.depressed, MODS.latched, MODS.locked, MODS.group);
} else {
wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, wlr_keyboard_from_input_device(pKeyboard->keyboard));
wlr_seat_keyboard_notify_modifiers(g_pCompositor->m_sSeat.seat, &MODS);
}
updateKeyboardsLeds(pKeyboard->keyboard);
const auto PWLRKB = wlr_keyboard_from_input_device(pKeyboard->keyboard);
if (PWLRKB->modifiers.group != pKeyboard->activeLayout) {
pKeyboard->activeLayout = PWLRKB->modifiers.group;
const auto LAYOUT = getActiveLayoutForKeyboard(pKeyboard);
pKeyboard->updateXKBTranslationState();
g_pEventManager->postEvent(SHyprIPCEvent{"activelayout", pKeyboard->name + "," + LAYOUT});
EMIT_HOOK_EVENT("activeLayout", (std::vector<void*>{pKeyboard, (void*)&LAYOUT}));
}
}
bool CInputManager::shouldIgnoreVirtualKeyboard(SKeyboard* pKeyboard) {
if (!pKeyboard->isVirtual)
return false;
return !pKeyboard || (!m_sIMERelay.m_pIME.expired() && m_sIMERelay.m_pIME.lock()->grabClient() == pKeyboard->virtKeyboard.lock()->client());
}
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_pCompositor->m_sSeat.mouse)
return;
for (auto& c : m_vConstraints) {
const auto C = c.lock();
if (!C)
continue;
if (!C->isActive())
continue;
C->deactivate();
}
}
bool CInputManager::isConstrained() {
for (auto& c : m_vConstraints) {
const auto C = c.lock();
if (!C)
continue;
if (!C->isActive() || C->owner()->wlr() != g_pCompositor->m_pLastFocus)
continue;
return true;
}
return false;
}
void CInputManager::updateCapabilities() {
uint32_t caps = 0;
if (!m_lKeyboards.empty())
caps |= WL_SEAT_CAPABILITY_KEYBOARD;
if (!m_lMice.empty())
caps |= WL_SEAT_CAPABILITY_POINTER;
if (!m_lTouchDevices.empty())
caps |= WL_SEAT_CAPABILITY_TOUCH;
if (!m_lTabletTools.empty())
caps |= WL_SEAT_CAPABILITY_POINTER;
wlr_seat_set_capabilities(g_pCompositor->m_sSeat.seat, caps);
m_uiCapabilities = caps;
}
uint32_t CInputManager::accumulateModsFromAllKBs() {
uint32_t finalMask = 0;
for (auto& kb : m_lKeyboards) {
if (kb.isVirtual && shouldIgnoreVirtualKeyboard(&kb))
continue;
if (!kb.enabled)
continue;
finalMask |= wlr_keyboard_get_modifiers(wlr_keyboard_from_input_device(kb.keyboard));
}
return finalMask;
}
std::string CInputManager::getActiveLayoutForKeyboard(SKeyboard* pKeyboard) {
const auto WLRKB = wlr_keyboard_from_input_device(pKeyboard->keyboard);
const auto KEYMAP = WLRKB->keymap;
const auto STATE = WLRKB->xkb_state;
const auto LAYOUTSNUM = xkb_keymap_num_layouts(KEYMAP);
for (uint32_t i = 0; i < LAYOUTSNUM; ++i) {
if (xkb_state_layout_index_is_active(STATE, i, XKB_STATE_LAYOUT_EFFECTIVE)) {
const auto LAYOUTNAME = xkb_keymap_layout_get_name(KEYMAP, i);
if (LAYOUTNAME)
return std::string(LAYOUTNAME);
return "error";
}
}
return "none";
}
void CInputManager::disableAllKeyboards(bool virt) {
for (auto& k : m_lKeyboards) {
if (k.isVirtual != virt)
continue;
k.active = false;
}
}
void CInputManager::newTouchDevice(wlr_input_device* pDevice) {
const auto PNEWDEV = &m_lTouchDevices.emplace_back();
PNEWDEV->pWlrDevice = pDevice;
try {
PNEWDEV->name = getNameForNewDevice(pDevice->name);
} catch (std::exception& e) {
Debug::log(ERR, "Touch Device had no name???"); // logic error
}
setTouchDeviceConfigs(PNEWDEV);
wlr_cursor_attach_input_device(g_pCompositor->m_sWLRCursor, pDevice);
Debug::log(LOG, "New touch device added at {:x}", (uintptr_t)PNEWDEV);
PNEWDEV->hyprListener_destroy.initCallback(
&pDevice->events.destroy, [&](void* owner, void* data) { destroyTouchDevice((STouchDevice*)data); }, PNEWDEV, "TouchDevice");
}
void CInputManager::setTouchDeviceConfigs(STouchDevice* dev) {
auto setConfig = [&](STouchDevice* const PTOUCHDEV) -> void {
if (wlr_input_device_is_libinput(PTOUCHDEV->pWlrDevice)) {
const auto LIBINPUTDEV = (libinput_device*)wlr_libinput_get_device_handle(PTOUCHDEV->pWlrDevice);
const auto ENABLED = g_pConfigManager->getDeviceInt(PTOUCHDEV->name, "enabled", "input:touchdevice:enabled");
const auto mode = ENABLED ? LIBINPUT_CONFIG_SEND_EVENTS_ENABLED : LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
if (libinput_device_config_send_events_get_mode(LIBINPUTDEV) != mode)
libinput_device_config_send_events_set_mode(LIBINPUTDEV, mode);
const int ROTATION = std::clamp(g_pConfigManager->getDeviceInt(PTOUCHDEV->name, "transform", "input:touchdevice:transform"), 0, 7);
Debug::log(LOG, "Setting calibration matrix for device {}", PTOUCHDEV->name);
if (libinput_device_config_calibration_has_matrix(LIBINPUTDEV))
libinput_device_config_calibration_set_matrix(LIBINPUTDEV, MATRICES[ROTATION]);
auto output = g_pConfigManager->getDeviceString(PTOUCHDEV->name, "output", "input:touchdevice:output");
bool bound = !output.empty() && output != STRVAL_EMPTY;
const bool AUTODETECT = output == "[[Auto]]";
if (!bound && AUTODETECT) {
const auto DEFAULTOUTPUT = wlr_touch_from_input_device(PTOUCHDEV->pWlrDevice)->output_name;
if (DEFAULTOUTPUT) {
output = DEFAULTOUTPUT;
bound = true;
}
}
PTOUCHDEV->boundOutput = bound ? output : "";
const auto PMONITOR = bound ? g_pCompositor->getMonitorFromName(output) : nullptr;
if (PMONITOR) {
Debug::log(LOG, "Binding touch device {} to output {}", PTOUCHDEV->name, PMONITOR->szName);
wlr_cursor_map_input_to_output(g_pCompositor->m_sWLRCursor, PTOUCHDEV->pWlrDevice, PMONITOR->output);
} else if (bound)
Debug::log(ERR, "Failed to bind touch device {} to output '{}': monitor not found", PTOUCHDEV->name, output);
}
};
if (dev) {
setConfig(dev);
return;
}
for (auto& m : m_lTouchDevices) {
const auto PTOUCHDEV = &m;
setConfig(PTOUCHDEV);
}
}
void CInputManager::setTabletConfigs() {
for (auto& t : m_lTablets) {
if (wlr_input_device_is_libinput(t.wlrDevice)) {
const auto LIBINPUTDEV = (libinput_device*)wlr_libinput_get_device_handle(t.wlrDevice);
const auto RELINPUT = g_pConfigManager->getDeviceInt(t.name, "relative_input", "input:tablet:relative_input");
t.relativeInput = RELINPUT;
const int ROTATION = std::clamp(g_pConfigManager->getDeviceInt(t.name, "transform", "input:tablet:transform"), 0, 7);
Debug::log(LOG, "Setting calibration matrix for device {}", t.name);
libinput_device_config_calibration_set_matrix(LIBINPUTDEV, MATRICES[ROTATION]);
if (g_pConfigManager->getDeviceInt(t.name, "left_handed", "input:tablet:left_handed") == 0)
libinput_device_config_left_handed_set(LIBINPUTDEV, 0);
else
libinput_device_config_left_handed_set(LIBINPUTDEV, 1);
const auto OUTPUT = g_pConfigManager->getDeviceString(t.name, "output", "input:tablet:output");
const auto PMONITOR = g_pCompositor->getMonitorFromString(OUTPUT);
if (!OUTPUT.empty() && OUTPUT != STRVAL_EMPTY && PMONITOR) {
Debug::log(LOG, "Binding tablet {} to output {}", t.name, PMONITOR->szName);
wlr_cursor_map_input_to_output(g_pCompositor->m_sWLRCursor, t.wlrDevice, PMONITOR->output);
wlr_cursor_map_input_to_region(g_pCompositor->m_sWLRCursor, t.wlrDevice, nullptr);
t.boundOutput = OUTPUT;
} else if (!PMONITOR)
Debug::log(ERR, "Failed to bind tablet {} to output '{}': monitor not found", t.name, OUTPUT);
const auto REGION_POS = g_pConfigManager->getDeviceVec(t.name, "region_position", "input:tablet:region_position");
const auto REGION_SIZE = g_pConfigManager->getDeviceVec(t.name, "region_size", "input:tablet:region_size");
auto regionBox = CBox{REGION_POS.x, REGION_POS.y, REGION_SIZE.x, REGION_SIZE.y};
if (!regionBox.empty())
wlr_cursor_map_input_to_region(g_pCompositor->m_sWLRCursor, t.wlrDevice, regionBox.pWlr());
const auto ACTIVE_AREA_SIZE = g_pConfigManager->getDeviceVec(t.name, "active_area_size", "input:tablet:active_area_size");
const auto ACTIVE_AREA_POS = g_pConfigManager->getDeviceVec(t.name, "active_area_position", "input:tablet:active_area_position");
if (ACTIVE_AREA_SIZE.x != 0 || ACTIVE_AREA_SIZE.y != 0) {
t.activeArea = CBox{ACTIVE_AREA_POS.x / t.wlrTablet->width_mm, ACTIVE_AREA_POS.y / t.wlrTablet->height_mm,
(ACTIVE_AREA_POS.x + ACTIVE_AREA_SIZE.x) / t.wlrTablet->width_mm, (ACTIVE_AREA_POS.y + ACTIVE_AREA_SIZE.y) / t.wlrTablet->height_mm};
}
}
}
}
void CInputManager::destroyTouchDevice(STouchDevice* pDevice) {
Debug::log(LOG, "Touch device at {:x} removed", (uintptr_t)pDevice);
m_lTouchDevices.remove(*pDevice);
}
void CInputManager::newSwitch(wlr_input_device* pDevice) {
const auto PNEWDEV = &m_lSwitches.emplace_back();
PNEWDEV->pWlrDevice = pDevice;
Debug::log(LOG, "New switch with name \"{}\" added", pDevice->name);
PNEWDEV->hyprListener_destroy.initCallback(
&pDevice->events.destroy, [&](void* owner, void* data) { destroySwitch((SSwitchDevice*)owner); }, PNEWDEV, "SwitchDevice");
const auto PSWITCH = wlr_switch_from_input_device(pDevice);
PNEWDEV->hyprListener_toggle.initCallback(
&PSWITCH->events.toggle,
[&](void* owner, void* data) {
const auto PDEVICE = (SSwitchDevice*)owner;
const auto NAME = std::string(PDEVICE->pWlrDevice->name);
const auto E = (wlr_switch_toggle_event*)data;
if (PDEVICE->status != -1 && PDEVICE->status == E->switch_state)
return;
Debug::log(LOG, "Switch {} fired, triggering binds.", NAME);
g_pKeybindManager->onSwitchEvent(NAME);
switch (E->switch_state) {
case WLR_SWITCH_STATE_ON:
Debug::log(LOG, "Switch {} turn on, triggering binds.", NAME);
g_pKeybindManager->onSwitchOnEvent(NAME);
break;
case WLR_SWITCH_STATE_OFF:
Debug::log(LOG, "Switch {} turn off, triggering binds.", NAME);
g_pKeybindManager->onSwitchOffEvent(NAME);
break;
}
PDEVICE->status = E->switch_state;
},
PNEWDEV, "SwitchDevice");
}
void CInputManager::destroySwitch(SSwitchDevice* pDevice) {
m_lSwitches.remove(*pDevice);
}
void CInputManager::setCursorImageUntilUnset(std::string name) {
g_pHyprRenderer->setCursorFromName(name);
m_bCursorImageOverridden = true;
m_sCursorSurfaceInfo.inUse = false;
}
void CInputManager::unsetCursorImage() {
if (!m_bCursorImageOverridden)
return;
m_bCursorImageOverridden = false;
restoreCursorIconToApp();
}
std::string CInputManager::deviceNameToInternalString(std::string in) {
std::replace(in.begin(), in.end(), ' ', '-');
std::replace(in.begin(), in.end(), '\n', '-');
std::transform(in.begin(), in.end(), in.begin(), ::tolower);
return in;
}
std::string CInputManager::getNameForNewDevice(std::string internalName) {
auto proposedNewName = deviceNameToInternalString(internalName);
int dupeno = 0;
while (std::find_if(m_lKeyboards.begin(), m_lKeyboards.end(),
[&](const SKeyboard& other) { return other.name == proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno))); }) != m_lKeyboards.end())
dupeno++;
while (std::find_if(m_lMice.begin(), m_lMice.end(), [&](const SMouse& other) { return other.name == proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno))); }) !=
m_lMice.end())
dupeno++;
while (std::find_if(m_lTouchDevices.begin(), m_lTouchDevices.end(),
[&](const STouchDevice& other) { return other.name == proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno))); }) != m_lTouchDevices.end())
dupeno++;
while (std::find_if(m_lTabletPads.begin(), m_lTabletPads.end(),
[&](const STabletPad& other) { return other.name == proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno))); }) != m_lTabletPads.end())
dupeno++;
while (std::find_if(m_lTablets.begin(), m_lTablets.end(),
[&](const STablet& other) { return other.name == proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno))); }) != m_lTablets.end())
dupeno++;
while (std::find_if(m_lTabletTools.begin(), m_lTabletTools.end(),
[&](const STabletTool& other) { return other.name == proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno))); }) != m_lTabletTools.end())
dupeno++;
return proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno)));
}
void CInputManager::releaseAllMouseButtons() {
const auto buttonsCopy = m_lCurrentlyHeldButtons;
if (g_pInputManager->m_sDrag.drag)
return;
for (auto& mb : buttonsCopy) {
wlr_seat_pointer_notify_button(g_pCompositor->m_sSeat.seat, 0, mb, WL_POINTER_BUTTON_STATE_RELEASED);
}
m_lCurrentlyHeldButtons.clear();
}
void CInputManager::setCursorIconOnBorder(PHLWINDOW w) {
// do not override cursor icons set by mouse binds
if (g_pKeybindManager->m_bIsMouseBindActive) {
m_eBorderIconDirection = BORDERICON_NONE;
return;
}
static auto PEXTENDBORDERGRAB = CConfigValue<Hyprlang::INT>("general:extend_border_grab_area");
const int BORDERSIZE = w->getRealBorderSize();
const int ROUNDING = w->rounding();
// give a small leeway (10 px) for corner icon
const auto CORNER = ROUNDING + BORDERSIZE + 10;
const auto mouseCoords = getMouseCoordsInternal();
CBox box = w->getWindowMainSurfaceBox();
eBorderIconDirection direction = BORDERICON_NONE;
CBox boxFullGrabInput = {box.x - *PEXTENDBORDERGRAB - BORDERSIZE, box.y - *PEXTENDBORDERGRAB - BORDERSIZE, box.width + 2 * (*PEXTENDBORDERGRAB + BORDERSIZE),
box.height + 2 * (*PEXTENDBORDERGRAB + BORDERSIZE)};
if (w->hasPopupAt(mouseCoords))
direction = BORDERICON_NONE;
else if (!boxFullGrabInput.containsPoint(mouseCoords) || (!m_lCurrentlyHeldButtons.empty() && currentlyDraggedWindow.expired()))
direction = BORDERICON_NONE;
else {
bool onDeco = false;
for (auto& wd : w->m_dWindowDecorations) {
if (!(wd->getDecorationFlags() & DECORATION_ALLOWS_MOUSE_INPUT))
continue;
if (g_pDecorationPositioner->getWindowDecorationBox(wd.get()).containsPoint(mouseCoords)) {
onDeco = true;
break;
}
}
if (onDeco)
direction = BORDERICON_NONE;
else {
if (box.containsPoint(mouseCoords)) {
if (!w->isInCurvedCorner(mouseCoords.x, mouseCoords.y)) {
direction = BORDERICON_NONE;
} else {
if (mouseCoords.y < box.y + CORNER) {
if (mouseCoords.x < box.x + CORNER)
direction = BORDERICON_UP_LEFT;
else
direction = BORDERICON_UP_RIGHT;
} else {
if (mouseCoords.x < box.x + CORNER)
direction = BORDERICON_DOWN_LEFT;
else
direction = BORDERICON_DOWN_RIGHT;
}
}
} else {
if (mouseCoords.y < box.y + CORNER) {
if (mouseCoords.x < box.x + CORNER)
direction = BORDERICON_UP_LEFT;
else if (mouseCoords.x > box.x + box.width - CORNER)
direction = BORDERICON_UP_RIGHT;
else
direction = BORDERICON_UP;
} else if (mouseCoords.y > box.y + box.height - CORNER) {
if (mouseCoords.x < box.x + CORNER)
direction = BORDERICON_DOWN_LEFT;
else if (mouseCoords.x > box.x + box.width - CORNER)
direction = BORDERICON_DOWN_RIGHT;
else
direction = BORDERICON_DOWN;
} else {
if (mouseCoords.x < box.x + CORNER)
direction = BORDERICON_LEFT;
else if (mouseCoords.x > box.x + box.width - CORNER)
direction = BORDERICON_RIGHT;
}
}
}
}
if (direction == m_eBorderIconDirection)
return;
m_eBorderIconDirection = direction;
switch (direction) {
case BORDERICON_NONE: unsetCursorImage(); break;
case BORDERICON_UP: setCursorImageUntilUnset("top_side"); break;
case BORDERICON_DOWN: setCursorImageUntilUnset("bottom_side"); break;
case BORDERICON_LEFT: setCursorImageUntilUnset("left_side"); break;
case BORDERICON_RIGHT: setCursorImageUntilUnset("right_side"); break;
case BORDERICON_UP_LEFT: setCursorImageUntilUnset("top_left_corner"); break;
case BORDERICON_DOWN_LEFT: setCursorImageUntilUnset("bottom_left_corner"); break;
case BORDERICON_UP_RIGHT: setCursorImageUntilUnset("top_right_corner"); break;
case BORDERICON_DOWN_RIGHT: setCursorImageUntilUnset("bottom_right_corner"); break;
}
}