14#include <config-kwin.h>
33#include <KDecoration2/Decoration>
35#if KWIN_BUILD_SCREENLOCKER
36#include <KScreenLocker/KsldApp>
39#include <KLocalizedString>
45#include <linux/input.h>
52static bool screenContainsPos(
const QPointF &pos)
55 for (
const Output *output :
outputs) {
63static QPointF confineToBoundingBox(
const QPointF &pos,
const QRectF &boundingBox)
66 std::clamp(pos.x(), boundingBox.left(), boundingBox.right() - 1.0),
67 std::clamp(pos.y(), boundingBox.top(), boundingBox.bottom() - 1.0));
89 if (!
input()->hasPointer()) {
93 if (
input()->hasPointer()) {
108#if KWIN_BUILD_SCREENLOCKER
110 connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged,
this, [
this]() {
119 connect(
workspace(), &QObject::destroyed,
this, [
this] {
131 auto setupMoveResizeConnection = [
this](
Window *
window) {
136 std::for_each(clients.begin(), clients.end(), setupMoveResizeConnection);
141 warp(output->geometry().center());
146void PointerInputRedirection::updateOnStartMoveResize()
148 breakPointerConstraints(
focus() ?
focus()->surface() : nullptr);
149 disconnectPointerConstraintsConnection();
153void PointerInputRedirection::updateToReset()
156 QHoverEvent event(QEvent::HoverLeave, QPointF(), QPointF());
161 if (
focus()->isClient()) {
164 disconnect(m_focusGeometryConnection);
165 m_focusGeometryConnection = QMetaObject::Connection();
166 breakPointerConstraints(
focus()->surface());
167 disconnectPointerConstraintsConnection();
183 if (s_counter == 0) {
184 if (!s_scheduledPositions.isEmpty()) {
185 const auto pos = s_scheduledPositions.takeFirst();
186 m_pointer->processMotionInternal(pos.pos, pos.delta, pos.deltaNonAccelerated, pos.time,
nullptr);
193 return s_counter > 0;
196 static void schedulePosition(
const QPointF &pos,
const QPointF &delta,
const QPointF &deltaNonAccelerated, std::chrono::microseconds time)
198 s_scheduledPositions.append({pos, delta, deltaNonAccelerated, time});
202 static int s_counter;
203 struct ScheduledPosition
207 QPointF deltaNonAccelerated;
208 std::chrono::microseconds time;
210 static QList<ScheduledPosition> s_scheduledPositions;
212 PointerInputRedirection *m_pointer;
215int PositionUpdateBlocker::s_counter = 0;
216QList<PositionUpdateBlocker::ScheduledPosition> PositionUpdateBlocker::s_scheduledPositions;
220 processMotionInternal(
pos, QPointF(), QPointF(), time, device);
225 processMotionInternal(m_pos + delta, delta, deltaNonAccelerated, time, device);
228void PointerInputRedirection::processMotionInternal(
const QPointF &pos,
const QPointF &delta,
const QPointF &deltaNonAccelerated, std::chrono::microseconds time,
InputDevice *device)
241 MouseEvent event(QEvent::MouseMove, m_pos, Qt::NoButton, m_qtButtons,
242 input()->keyboardModifiers(), time,
243 delta, deltaNonAccelerated, device);
244 event.setModifiersRelevantForGlobalShortcuts(
input()->modifiersRelevantForGlobalShortcuts());
257 type = QEvent::MouseButtonRelease;
260 type = QEvent::MouseButtonPress;
268 updateButton(button, state);
271 input()->keyboardModifiers(), time, QPointF(), QPointF(), device);
272 event.setModifiersRelevantForGlobalShortcuts(
input()->modifiersRelevantForGlobalShortcuts());
273 event.setNativeButton(button);
296 WheelEvent wheelEvent(m_pos, delta, deltaV120,
448 for (
auto state : m_buttons) {
466 if (
input()->isSelectingWindow()) {
477 disconnect(m_decorationGeometryConnection);
478 m_decorationGeometryConnection = QMetaObject::Connection();
480 disconnect(m_decorationDestroyedConnection);
481 m_decorationDestroyedConnection = QMetaObject::Connection();
483 disconnect(m_decorationClosedConnection);
484 m_decorationClosedConnection = QMetaObject::Connection();
488 QHoverEvent event(QEvent::HoverLeave, QPointF(-1, -1), QPointF());
489 QCoreApplication::instance()->sendEvent(old->decoration(), &event);
497 QHoverEvent event(QEvent::HoverEnter,
pos, QPointF(-1, -1));
498 QCoreApplication::instance()->sendEvent(now->decoration(), &event);
507 const QPointF p = m_pos -
decoration()->window()->pos();
508 QHoverEvent event(QEvent::HoverMove, p, p);
513 auto resetDecoration = [
this]() {
519 m_decorationDestroyedConnection = connect(now, &QObject::destroyed,
this, resetDecoration);
522void PointerInputRedirection::focusUpdate(
Window *focusOld,
Window *focusNow)
524 if (focusOld && focusOld->isClient()) {
525 focusOld->pointerLeaveEvent();
526 breakPointerConstraints(focusOld->surface());
527 disconnectPointerConstraintsConnection();
529 disconnect(m_focusGeometryConnection);
530 m_focusGeometryConnection = QMetaObject::Connection();
532 if (focusNow && focusNow->isClient()) {
533 focusNow->pointerEnterEvent(m_pos);
537 if (!focusNow || !focusNow->surface()) {
538 seat->notifyPointerLeave();
542 seat->notifyPointerEnter(focusNow->surface(), m_pos, focusNow->inputTransformation());
555 if (
focus()->surface() !=
seat->focusedPointerSurface()) {
558 seat->setFocusedPointerSurfaceTransformation(
focus()->inputTransformation());
568void PointerInputRedirection::breakPointerConstraints(SurfaceInterface *surface)
572 auto c = surface->confinedPointer();
573 if (c && c->isConfined()) {
574 c->setConfined(
false);
576 auto l = surface->lockedPointer();
577 if (l && l->isLocked()) {
581 disconnectConfinedPointerRegionConnection();
586void PointerInputRedirection::disconnectConfinedPointerRegionConnection()
588 disconnect(m_confinedPointerRegionConnection);
589 m_confinedPointerRegionConnection = QMetaObject::Connection();
592void PointerInputRedirection::disconnectLockedPointerAboutToBeUnboundConnection()
594 disconnect(m_lockedPointerAboutToBeUnboundConnection);
595 m_lockedPointerAboutToBeUnboundConnection = QMetaObject::Connection();
598void PointerInputRedirection::disconnectPointerConstraintsConnection()
600 disconnect(m_constraintsConnection);
601 m_constraintsConnection = QMetaObject::Connection();
603 disconnect(m_constraintsActivatedConnection);
604 m_constraintsActivatedConnection = QMetaObject::Connection();
609 if (m_enableConstraints == set) {
612 m_enableConstraints = set;
632 const auto cf = s->confinedPointer();
634 if (cf->isConfined()) {
636 cf->setConfined(
false);
638 disconnectConfinedPointerRegionConnection();
642 if (canConstrain && cf->region().contains(
flooredPoint(
focus()->mapToLocal(m_pos)))) {
643 cf->setConfined(
true);
659 if (!cf->isConfined()) {
660 cf->setConfined(
true);
669 disconnectConfinedPointerRegionConnection();
671 const auto lock = s->lockedPointer();
673 if (lock->isLocked()) {
675 const auto hint = lock->cursorPositionHint();
676 lock->setLocked(
false);
678 disconnectLockedPointerAboutToBeUnboundConnection();
679 if (!(hint.x() < 0 || hint.y() < 0) &&
focus()) {
685 if (canConstrain && lock->region().contains(
flooredPoint(
focus()->mapToLocal(m_pos)))) {
686 lock->setLocked(
true);
692 const auto hint = lock->cursorPositionHint();
693 if (hint.x() < 0 || hint.y() < 0 || !
focus()) {
699 connect(lock, &LockedPointerV1Interface::destroyed,
this, [
this, globalHint]() {
707 disconnectLockedPointerAboutToBeUnboundConnection();
711QPointF PointerInputRedirection::applyPointerConfinement(
const QPointF &pos)
const
724 if (!cf->isConfined()) {
736 QPointF p(currentPos.x(), localPos.y());
741 p = QPointF(localPos.x(), currentPos.y());
749void PointerInputRedirection::updatePosition(
const QPointF &pos)
757 QPointF p = confineToBoundingBox(
pos, currentOutput->geometry());
758 p = applyPointerConfinement(p);
764 if (!screenContainsPos(p)) {
778 m_buttons[button] = state;
781 m_qtButtons = Qt::NoButton;
782 for (
auto it = m_buttons.constBegin(); it != m_buttons.constEnd(); ++it) {
811 if (m_lastOutputWasPlaceholder) {
816 if (screenContainsPos(m_pos)) {
830QPointF PointerInputRedirection::position()
const
878 m_effectsCursor = std::make_unique<ShapeCursorSource>();
879 m_fallbackCursor = std::make_unique<ShapeCursorSource>();
880 m_moveResizeCursor = std::make_unique<ShapeCursorSource>();
881 m_windowSelectionCursor = std::make_unique<ShapeCursorSource>();
882 m_decoration.cursor = std::make_unique<ShapeCursorSource>();
883 m_serverCursor.surface = std::make_unique<SurfaceCursorSource>();
884 m_serverCursor.shape = std::make_unique<ShapeCursorSource>();
886#if KWIN_BUILD_SCREENLOCKER
888 connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged,
this, &CursorImage::reevaluteSource);
893 auto setupMoveResizeConnection = [
this](
Window *window) {
898 std::for_each(clients.begin(), clients.end(), setupMoveResizeConnection);
901 m_fallbackCursor->setShape(Qt::ArrowCursor);
903 m_effectsCursor->setTheme(m_waylandImage.
theme());
904 m_fallbackCursor->setTheme(m_waylandImage.
theme());
905 m_moveResizeCursor->setTheme(m_waylandImage.
theme());
906 m_windowSelectionCursor->setTheme(m_waylandImage.
theme());
907 m_decoration.cursor->setTheme(m_waylandImage.
theme());
908 m_serverCursor.shape->setTheme(m_waylandImage.
theme());
911 m_effectsCursor->setTheme(m_waylandImage.
theme());
912 m_fallbackCursor->setTheme(m_waylandImage.
theme());
913 m_moveResizeCursor->setTheme(m_waylandImage.
theme());
914 m_windowSelectionCursor->setTheme(m_waylandImage.
theme());
915 m_decoration.cursor->setTheme(m_waylandImage.
theme());
916 m_serverCursor.shape->setTheme(m_waylandImage.
theme());
922 this, &CursorImage::handleFocusedSurfaceChanged);
931 if (m_currentSource == m_serverCursor.surface.get()) {
932 auto cursorSurface = m_serverCursor.surface->surface();
934 const QRectF cursorGeometry(pos - m_currentSource->
hotspot(), m_currentSource->
size());
935 cursorSurface->setOutputs(
waylandServer()->display()->outputsIntersecting(cursorGeometry.toAlignedRect()),
936 waylandServer()->display()->largestIntersectingOutput(cursorGeometry.toAlignedRect()));
943 if (m_currentSource == m_serverCursor.surface.get()) {
944 if (
auto cursorSurface = m_serverCursor.surface->surface()) {
946 surface->frameRendered(timestamp.count());
952void CursorImage::handleFocusedSurfaceChanged()
955 disconnect(m_serverCursor.connection);
959 this, &CursorImage::updateServerCursor);
961 m_serverCursor.connection = QMetaObject::Connection();
966void CursorImage::updateDecoration()
968 disconnect(m_decoration.connection);
970 Window *window = deco ? deco->window() :
nullptr;
974 m_decoration.connection = QMetaObject::Connection();
976 updateDecorationCursor();
979void CursorImage::updateDecorationCursor()
982 if (
Window *window = deco ? deco->window() : nullptr) {
983 m_decoration.cursor->setShape(window->cursor().name());
988void CursorImage::updateMoveResize()
991 m_moveResizeCursor->setShape(window->cursor().name());
996void CursorImage::updateServerCursor(
const PointerCursor &cursor)
998 if (
auto surfaceCursor = std::get_if<PointerSurfaceCursor *>(&
cursor)) {
999 m_serverCursor.surface->update((*surfaceCursor)->surface(), (*surfaceCursor)->hotspot());
1000 m_serverCursor.cursor = m_serverCursor.surface.get();
1001 }
else if (
auto shapeCursor = std::get_if<QByteArray>(&
cursor)) {
1002 m_serverCursor.shape->setShape(*shapeCursor);
1003 m_serverCursor.cursor = m_serverCursor.shape.get();
1010 m_effectsCursor->setShape(
shape);
1021 if (
shape.isEmpty()) {
1022 m_windowSelectionCursor->setShape(Qt::CrossCursor);
1024 m_windowSelectionCursor->setShape(
shape);
1038 updateCursorTheme();
1046 return m_cursorTheme;
1049void WaylandCursorImage::updateCursorTheme()
1052 qreal targetDevicePixelRatio = 1;
1055 for (
const Output *output : outputs) {
1056 if (output->scale() > targetDevicePixelRatio) {
1057 targetDevicePixelRatio = output->scale();
1061 m_cursorTheme = KXcursorTheme(pointerCursor->
themeName(), pointerCursor->
themeSize(), targetDevicePixelRatio);
1062 if (m_cursorTheme.
isEmpty()) {
1069void CursorImage::reevaluteSource()
1075 if (
input()->isSelectingWindow()) {
1076 setSource(m_windowSelectionCursor.get());
1092 if (pointer && pointer->focusedSurface()) {
1101 return m_currentSource;
1106 if (m_currentSource ==
source) {
1109 m_currentSource =
source;
1115 return m_waylandImage.
theme();
1120 , m_currentButtons(Qt::NoButton)
1124 this, &InputRedirectionCursor::slotPosChanged);
1126 this, &InputRedirectionCursor::slotPointerButtonChanged);
1129 this, &InputRedirectionCursor::slotModifiersChanged);
1139 if (
input()->supportsPointerWarping()) {
1142 slotPosChanged(
input()->globalPointer());
1146void InputRedirectionCursor::slotPosChanged(
const QPointF &pos)
1151 input()->keyboardModifiers(),
input()->keyboardModifiers());
1154void InputRedirectionCursor::slotModifiersChanged(Qt::KeyboardModifiers mods, Qt::KeyboardModifiers oldMods)
1159void InputRedirectionCursor::slotPointerButtonChanged()
1161 const Qt::MouseButtons oldButtons = m_currentButtons;
1169#include "moc_pointer_input.cpp"
void setConfined(bool confined)
void mouseChanged(const QPointF &pos, const QPointF &oldpos, Qt::MouseButtons buttons, Qt::MouseButtons oldbuttons, Qt::KeyboardModifiers modifiers, Qt::KeyboardModifiers oldmodifiers)
static int defaultThemeSize()
void setSource(CursorSource *source)
int themeSize() const
The size of the currently used Cursor theme.
static QString defaultThemeName()
void posChanged(const QPointF &pos)
const QString & themeName() const
The name of the currently used Cursor theme.
void updatePos(const QPointF &pos)
const QPointF & currentPos() const
void rendered(std::chrono::milliseconds timestamp)
std::unique_ptr< ShapeCursorSource > shape
CursorSource * source() const
void setEffectsOverrideCursor(Qt::CursorShape shape)
KXcursorTheme theme() const
CursorImage(PointerInputRedirection *parent=nullptr)
std::unique_ptr< SurfaceCursorSource > surface
void removeWindowSelectionCursor()
void markAsRendered(std::chrono::milliseconds timestamp)
std::unique_ptr< ShapeCursorSource > cursor
void updateCursorOutputs(const QPointF &pos)
void removeEffectsOverrideCursor()
void setWindowSelectionCursor(const QByteArray &shape)
void setSource(CursorSource *source)
void setMouse(Cursor *mouse)
bool isMouseInterception() const
void aboutToBeDestroyed()
bool isPlaceholder() const
SurfaceInterface * focusedSurface() const
void cursorChanged(const PointerCursor &cursor)
void focusedSurfaceChanged()
PositionUpdateBlocker(PointerInputRedirection *pointer)
static void schedulePosition(const QPointF &pos, const QPointF &delta, const QPointF &deltaNonAccelerated, std::chrono::microseconds time)
static bool isPositionBlocked()
void cancelPointerSwipeGesture()
void setHasPointer(bool has)
PointerInterface * pointer() const
void cancelPointerPinchGesture()
Resource representing a wl_surface.
void pointerConstraintsChanged()
ConfinedPointerV1Interface * confinedPointer() const
KXcursorTheme theme() const
WaylandCursorImage(QObject *parent=nullptr)
SeatInterface * seat() const
void setModifiersRelevantForGlobalShortcuts(const Qt::KeyboardModifiers &mods)
void interactiveMoveResizeStarted()
void inputTransformationChanged()
QPointF mapToLocal(const QPointF &point) const
SurfaceInterface * surface() const
void processDecorationMove(const QPointF &localPos, const QPointF &globalPos)
void interactiveMoveResizeFinished()
virtual void pointerLeaveEvent()
QPointF mapFromLocal(const QPointF &point) const
void moveResizedChanged()
void frameGeometryChanged(const QRectF &oldGeometry)
void moveResizeCursorChanged(CursorShape)
Window * activeWindow() const
QList< Output * > outputOrder() const
void windowActivated(KWin::Window *)
void windowAdded(KWin::Window *)
QList< Output * > outputs() const
const QList< Window * > windows() const
Output * outputAt(const QPointF &pos) const
void setActiveCursorOutput(Output *output)
KWayland::Client::Seat * seat
QList< KWayland::Client::Output * > outputs
std::variant< PointerSurfaceCursor *, QByteArray > PointerCursor
WaylandServer * waylandServer()
InputRedirection * input()
KWIN_EXPORT QPoint flooredPoint(const QPointF &point)
Qt::MouseButton buttonToQtMouseButton(uint32_t button)