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)