2020-08-02 22:22:19 +00:00
|
|
|
/*
|
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
2013-06-26 08:15:20 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
|
|
|
|
SPDX-FileCopyrightText: 2018 Roman Gilg <subdiff@gmail.com>
|
|
|
|
SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
2013-06-26 08:15:20 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
2013-06-26 08:15:20 +00:00
|
|
|
#include "input.h"
|
2021-10-27 09:13:09 +00:00
|
|
|
#include "backends/fakeinput/fakeinputbackend.h"
|
|
|
|
#include "backends/libinput/connection.h"
|
|
|
|
#include "backends/libinput/device.h"
|
2019-12-01 17:51:15 +00:00
|
|
|
#include "effects.h"
|
|
|
|
#include "gestures.h"
|
|
|
|
#include "globalshortcuts.h"
|
2016-10-07 12:47:25 +00:00
|
|
|
#include "input_event.h"
|
2016-12-27 19:16:50 +00:00
|
|
|
#include "input_event_spy.h"
|
2021-10-02 08:26:51 +00:00
|
|
|
#include "inputbackend.h"
|
2021-12-13 17:23:23 +00:00
|
|
|
#include "inputmethod.h"
|
2016-02-15 12:42:48 +00:00
|
|
|
#include "keyboard_input.h"
|
2019-12-01 17:51:15 +00:00
|
|
|
#include "main.h"
|
2016-02-12 12:30:00 +00:00
|
|
|
#include "pointer_input.h"
|
2021-01-30 13:22:07 +00:00
|
|
|
#include "session.h"
|
2019-12-01 17:51:15 +00:00
|
|
|
#include "tablet_input.h"
|
2021-11-29 14:49:38 +00:00
|
|
|
#include "hide_cursor_spy.h"
|
2019-12-01 17:51:15 +00:00
|
|
|
#include "touch_input.h"
|
2019-09-24 08:48:08 +00:00
|
|
|
#include "x11client.h"
|
2013-07-10 11:20:43 +00:00
|
|
|
#ifdef KWIN_BUILD_TABBOX
|
|
|
|
#include "tabbox/tabbox.h"
|
|
|
|
#endif
|
2020-03-17 14:21:35 +00:00
|
|
|
#include "internal_client.h"
|
2016-04-07 07:24:17 +00:00
|
|
|
#include "platform.h"
|
2017-03-25 17:41:28 +00:00
|
|
|
#include "popup_input_filter.h"
|
2020-03-17 14:21:35 +00:00
|
|
|
#include "screenedge.h"
|
|
|
|
#include "screens.h"
|
|
|
|
#include "unmanaged.h"
|
2021-08-12 14:16:08 +00:00
|
|
|
#include "virtualdesktops.h"
|
2015-02-26 14:32:44 +00:00
|
|
|
#include "wayland_server.h"
|
2020-03-17 14:21:35 +00:00
|
|
|
#include "workspace.h"
|
2018-08-22 12:56:48 +00:00
|
|
|
#include "xwl/xwayland_interface.h"
|
2020-03-17 14:21:35 +00:00
|
|
|
#include "cursor.h"
|
|
|
|
#include <KDecoration2/Decoration>
|
|
|
|
#include <KGlobalAccel>
|
2021-07-26 08:37:48 +00:00
|
|
|
#include <KLocalizedString>
|
2020-04-29 15:18:41 +00:00
|
|
|
#include <KWaylandServer/display.h>
|
2021-12-13 17:23:23 +00:00
|
|
|
#include <KWaylandServer/inputmethod_v1_interface.h>
|
2020-04-29 15:18:41 +00:00
|
|
|
#include <KWaylandServer/seat_interface.h>
|
2021-07-20 19:37:03 +00:00
|
|
|
#include <KWaylandServer/shmclientbuffer.h>
|
2020-04-29 15:18:41 +00:00
|
|
|
#include <KWaylandServer/surface_interface.h>
|
2020-11-04 17:17:04 +00:00
|
|
|
#include <KWaylandServer/tablet_v2_interface.h>
|
2015-06-03 16:09:54 +00:00
|
|
|
#include <decorations/decoratedclient.h>
|
2017-05-06 23:40:48 +00:00
|
|
|
|
2016-02-16 11:54:21 +00:00
|
|
|
//screenlocker
|
|
|
|
#include <KScreenLocker/KsldApp>
|
2014-12-02 12:50:26 +00:00
|
|
|
// Qt
|
2021-10-02 08:26:51 +00:00
|
|
|
#include <QDBusConnection>
|
|
|
|
#include <QDBusMessage>
|
|
|
|
#include <QDBusPendingCall>
|
2014-12-02 12:50:26 +00:00
|
|
|
#include <QKeyEvent>
|
2021-09-27 10:10:37 +00:00
|
|
|
#include <QThread>
|
2021-07-15 09:08:19 +00:00
|
|
|
#include <qpa/qwindowsysteminterface.h>
|
2016-02-09 16:18:58 +00:00
|
|
|
|
2014-03-28 08:41:48 +00:00
|
|
|
#include <xkbcommon/xkbcommon.h>
|
2013-06-26 08:15:20 +00:00
|
|
|
|
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
|
2021-02-22 10:51:26 +00:00
|
|
|
static KWaylandServer::PointerAxisSource kwinAxisSourceToKWaylandAxisSource(InputRedirection::PointerAxisSource source)
|
|
|
|
{
|
|
|
|
switch (source) {
|
|
|
|
case KWin::InputRedirection::PointerAxisSourceWheel:
|
|
|
|
return KWaylandServer::PointerAxisSource::Wheel;
|
|
|
|
case KWin::InputRedirection::PointerAxisSourceFinger:
|
|
|
|
return KWaylandServer::PointerAxisSource::Finger;
|
|
|
|
case KWin::InputRedirection::PointerAxisSourceContinuous:
|
|
|
|
return KWaylandServer::PointerAxisSource::Continuous;
|
|
|
|
case KWin::InputRedirection::PointerAxisSourceWheelTilt:
|
|
|
|
return KWaylandServer::PointerAxisSource::WheelTilt;
|
|
|
|
case KWin::InputRedirection::PointerAxisSourceUnknown:
|
|
|
|
default:
|
|
|
|
return KWaylandServer::PointerAxisSource::Unknown;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-03 14:17:49 +00:00
|
|
|
InputEventFilter::InputEventFilter() = default;
|
|
|
|
|
|
|
|
InputEventFilter::~InputEventFilter()
|
|
|
|
{
|
|
|
|
if (input()) {
|
|
|
|
input()->uninstallInputEventFilter(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InputEventFilter::pointerEvent(QMouseEvent *event, quint32 nativeButton)
|
|
|
|
{
|
|
|
|
Q_UNUSED(event)
|
|
|
|
Q_UNUSED(nativeButton)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InputEventFilter::wheelEvent(QWheelEvent *event)
|
|
|
|
{
|
|
|
|
Q_UNUSED(event)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InputEventFilter::keyEvent(QKeyEvent *event)
|
|
|
|
{
|
|
|
|
Q_UNUSED(event)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-08-11 19:57:45 +00:00
|
|
|
bool InputEventFilter::touchDown(qint32 id, const QPointF &point, quint32 time)
|
2016-02-03 14:17:49 +00:00
|
|
|
{
|
|
|
|
Q_UNUSED(id)
|
|
|
|
Q_UNUSED(point)
|
|
|
|
Q_UNUSED(time)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-08-11 19:57:45 +00:00
|
|
|
bool InputEventFilter::touchMotion(qint32 id, const QPointF &point, quint32 time)
|
2016-02-03 14:17:49 +00:00
|
|
|
{
|
|
|
|
Q_UNUSED(id)
|
|
|
|
Q_UNUSED(point)
|
|
|
|
Q_UNUSED(time)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-08-11 19:57:45 +00:00
|
|
|
bool InputEventFilter::touchUp(qint32 id, quint32 time)
|
2016-02-03 14:17:49 +00:00
|
|
|
{
|
|
|
|
Q_UNUSED(id)
|
|
|
|
Q_UNUSED(time)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-08-05 12:35:33 +00:00
|
|
|
bool InputEventFilter::pinchGestureBegin(int fingerCount, quint32 time)
|
|
|
|
{
|
|
|
|
Q_UNUSED(fingerCount)
|
|
|
|
Q_UNUSED(time)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InputEventFilter::pinchGestureUpdate(qreal scale, qreal angleDelta, const QSizeF &delta, quint32 time)
|
|
|
|
{
|
|
|
|
Q_UNUSED(scale)
|
|
|
|
Q_UNUSED(angleDelta)
|
|
|
|
Q_UNUSED(delta)
|
|
|
|
Q_UNUSED(time)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InputEventFilter::pinchGestureEnd(quint32 time)
|
|
|
|
{
|
|
|
|
Q_UNUSED(time)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InputEventFilter::pinchGestureCancelled(quint32 time)
|
|
|
|
{
|
|
|
|
Q_UNUSED(time)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InputEventFilter::swipeGestureBegin(int fingerCount, quint32 time)
|
|
|
|
{
|
|
|
|
Q_UNUSED(fingerCount)
|
|
|
|
Q_UNUSED(time)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InputEventFilter::swipeGestureUpdate(const QSizeF &delta, quint32 time)
|
|
|
|
{
|
|
|
|
Q_UNUSED(delta)
|
|
|
|
Q_UNUSED(time)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InputEventFilter::swipeGestureEnd(quint32 time)
|
|
|
|
{
|
|
|
|
Q_UNUSED(time)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InputEventFilter::swipeGestureCancelled(quint32 time)
|
|
|
|
{
|
|
|
|
Q_UNUSED(time)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-08-05 08:31:08 +00:00
|
|
|
bool InputEventFilter::holdGestureBegin(int fingerCount, quint32 time)
|
|
|
|
{
|
|
|
|
Q_UNUSED(fingerCount)
|
|
|
|
Q_UNUSED(time)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InputEventFilter::holdGestureEnd(quint32 time)
|
|
|
|
{
|
|
|
|
Q_UNUSED(time)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InputEventFilter::holdGestureCancelled(quint32 time)
|
|
|
|
{
|
|
|
|
Q_UNUSED(time)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-12-27 19:25:36 +00:00
|
|
|
bool InputEventFilter::switchEvent(SwitchEvent *event)
|
|
|
|
{
|
|
|
|
Q_UNUSED(event)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-03-17 14:21:35 +00:00
|
|
|
bool InputEventFilter::tabletToolEvent(TabletEvent *event)
|
2019-12-01 17:51:15 +00:00
|
|
|
{
|
|
|
|
Q_UNUSED(event)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-11-20 01:28:05 +00:00
|
|
|
bool InputEventFilter::tabletToolButtonEvent(uint button, bool pressed, const TabletToolId &tabletId)
|
2019-12-01 17:51:15 +00:00
|
|
|
{
|
2020-11-20 01:28:05 +00:00
|
|
|
Q_UNUSED(button)
|
|
|
|
Q_UNUSED(pressed)
|
|
|
|
Q_UNUSED(tabletId)
|
2019-12-01 17:51:15 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-12-22 16:34:10 +00:00
|
|
|
bool InputEventFilter::tabletPadButtonEvent(uint button, bool pressed, const TabletPadId &tabletPadId)
|
2019-12-01 17:51:15 +00:00
|
|
|
{
|
2020-11-20 01:28:05 +00:00
|
|
|
Q_UNUSED(button)
|
|
|
|
Q_UNUSED(pressed)
|
2020-12-22 16:34:10 +00:00
|
|
|
Q_UNUSED(tabletPadId)
|
2019-12-01 17:51:15 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-12-22 16:34:10 +00:00
|
|
|
bool InputEventFilter::tabletPadStripEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId)
|
2019-12-01 17:51:15 +00:00
|
|
|
{
|
|
|
|
Q_UNUSED(number)
|
|
|
|
Q_UNUSED(position)
|
|
|
|
Q_UNUSED(isFinger)
|
2020-12-22 16:34:10 +00:00
|
|
|
Q_UNUSED(tabletPadId)
|
2019-12-01 17:51:15 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-12-22 16:34:10 +00:00
|
|
|
bool InputEventFilter::tabletPadRingEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId)
|
2019-12-01 17:51:15 +00:00
|
|
|
{
|
|
|
|
Q_UNUSED(number)
|
|
|
|
Q_UNUSED(position)
|
|
|
|
Q_UNUSED(isFinger)
|
2020-12-22 16:34:10 +00:00
|
|
|
Q_UNUSED(tabletPadId)
|
2019-12-01 17:51:15 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-09-15 06:47:01 +00:00
|
|
|
void InputEventFilter::passToWaylandServer(QKeyEvent *event)
|
|
|
|
{
|
|
|
|
Q_ASSERT(waylandServer());
|
|
|
|
if (event->isAutoRepeat()) {
|
|
|
|
return;
|
|
|
|
}
|
2021-03-24 10:07:09 +00:00
|
|
|
|
|
|
|
KWaylandServer::SeatInterface *seat = waylandServer()->seat();
|
2016-09-15 06:47:01 +00:00
|
|
|
switch (event->type()) {
|
|
|
|
case QEvent::KeyPress:
|
2021-03-24 10:07:09 +00:00
|
|
|
seat->notifyKeyboardKey(event->nativeScanCode(), KWaylandServer::KeyboardKeyState::Pressed);
|
2016-09-15 06:47:01 +00:00
|
|
|
break;
|
|
|
|
case QEvent::KeyRelease:
|
2021-03-24 10:07:09 +00:00
|
|
|
seat->notifyKeyboardKey(event->nativeScanCode(), KWaylandServer::KeyboardKeyState::Released);
|
2016-09-15 06:47:01 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-04 03:38:25 +00:00
|
|
|
bool InputEventFilter::passToInputMethod(QKeyEvent *event)
|
|
|
|
{
|
|
|
|
auto *inputmethod = InputMethod::self();
|
|
|
|
|
|
|
|
if (!inputmethod) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (auto keyboardGrab = inputmethod->keyboardGrab()) {
|
|
|
|
if (event->isAutoRepeat()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
auto newState = event->type() == QEvent::KeyPress ? KWaylandServer::KeyboardKeyState::Pressed : KWaylandServer::KeyboardKeyState::Released;
|
|
|
|
keyboardGrab->sendKey(waylandServer()->display()->nextSerial(), event->timestamp(), event->nativeScanCode(), newState);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-02-03 14:17:49 +00:00
|
|
|
class VirtualTerminalFilter : public InputEventFilter {
|
|
|
|
public:
|
|
|
|
bool keyEvent(QKeyEvent *event) override {
|
|
|
|
// really on press and not on release? X11 switches on press.
|
2016-02-19 06:53:20 +00:00
|
|
|
if (event->type() == QEvent::KeyPress && !event->isAutoRepeat()) {
|
2016-02-03 14:17:49 +00:00
|
|
|
const xkb_keysym_t keysym = event->nativeVirtualKey();
|
|
|
|
if (keysym >= XKB_KEY_XF86Switch_VT_1 && keysym <= XKB_KEY_XF86Switch_VT_12) {
|
2021-01-30 13:22:07 +00:00
|
|
|
kwinApp()->platform()->session()->switchTo(keysym - XKB_KEY_XF86Switch_VT_1 + 1);
|
2016-02-03 14:17:49 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-03-10 19:30:49 +00:00
|
|
|
class TerminateServerFilter : public InputEventFilter {
|
|
|
|
public:
|
|
|
|
bool keyEvent(QKeyEvent *event) override {
|
|
|
|
if (event->type() == QEvent::KeyPress && !event->isAutoRepeat()) {
|
|
|
|
if (event->nativeVirtualKey() == XKB_KEY_Terminate_Server) {
|
|
|
|
qCWarning(KWIN_CORE) << "Request to terminate server";
|
2021-04-12 18:09:10 +00:00
|
|
|
QMetaObject::invokeMethod(QCoreApplication::instance(), &QCoreApplication::quit, Qt::QueuedConnection);
|
2016-03-10 19:30:49 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-02-03 14:17:49 +00:00
|
|
|
class LockScreenFilter : public InputEventFilter {
|
|
|
|
public:
|
|
|
|
bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override {
|
|
|
|
if (!waylandServer()->isScreenLocked()) {
|
|
|
|
return false;
|
|
|
|
}
|
2021-04-07 16:54:51 +00:00
|
|
|
|
|
|
|
auto client = qobject_cast<AbstractClient *>(input()->findToplevel(event->globalPos()));
|
|
|
|
if (client && client->isLockScreen()) {
|
|
|
|
workspace()->activateClient(client);
|
|
|
|
}
|
|
|
|
|
2016-02-03 14:17:49 +00:00
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
seat->setTimestamp(event->timestamp());
|
|
|
|
if (event->type() == QEvent::MouseMove) {
|
2016-02-09 16:18:58 +00:00
|
|
|
if (pointerSurfaceAllowed()) {
|
2018-09-15 00:00:24 +00:00
|
|
|
// TODO: should the pointer position always stay in sync, i.e. not do the check?
|
2021-03-11 08:44:03 +00:00
|
|
|
seat->notifyPointerMotion(event->screenPos().toPoint());
|
|
|
|
seat->notifyPointerFrame();
|
2016-02-03 14:17:49 +00:00
|
|
|
}
|
|
|
|
} else if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease) {
|
2016-02-09 16:18:58 +00:00
|
|
|
if (pointerSurfaceAllowed()) {
|
2018-09-15 00:00:24 +00:00
|
|
|
// TODO: can we leak presses/releases here when we move the mouse in between from an allowed surface to
|
|
|
|
// disallowed one or vice versa?
|
2021-03-15 18:10:34 +00:00
|
|
|
const auto state = event->type() == QEvent::MouseButtonPress
|
|
|
|
? KWaylandServer::PointerButtonState::Pressed
|
|
|
|
: KWaylandServer::PointerButtonState::Released;
|
|
|
|
seat->notifyPointerButton(nativeButton, state);
|
2021-03-11 08:44:03 +00:00
|
|
|
seat->notifyPointerFrame();
|
2016-02-03 14:17:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool wheelEvent(QWheelEvent *event) override {
|
|
|
|
if (!waylandServer()->isScreenLocked()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
auto seat = waylandServer()->seat();
|
2016-02-09 16:18:58 +00:00
|
|
|
if (pointerSurfaceAllowed()) {
|
2021-02-22 10:51:26 +00:00
|
|
|
const WheelEvent *wheelEvent = static_cast<WheelEvent *>(event);
|
|
|
|
seat->setTimestamp(wheelEvent->timestamp());
|
2021-03-11 08:44:03 +00:00
|
|
|
seat->notifyPointerAxis(wheelEvent->orientation(), wheelEvent->delta(),
|
|
|
|
wheelEvent->discreteDelta(),
|
|
|
|
kwinAxisSourceToKWaylandAxisSource(wheelEvent->axisSource()));
|
|
|
|
seat->notifyPointerFrame();
|
2016-02-03 14:17:49 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool keyEvent(QKeyEvent * event) override {
|
|
|
|
if (!waylandServer()->isScreenLocked()) {
|
|
|
|
return false;
|
|
|
|
}
|
2016-02-19 06:53:20 +00:00
|
|
|
if (event->isAutoRepeat()) {
|
|
|
|
// wayland client takes care of it
|
|
|
|
return true;
|
|
|
|
}
|
2016-02-16 11:54:21 +00:00
|
|
|
// send event to KSldApp for global accel
|
|
|
|
// if event is set to accepted it means a whitelisted shortcut was triggered
|
|
|
|
// in that case we filter it out and don't process it further
|
|
|
|
event->setAccepted(false);
|
|
|
|
QCoreApplication::sendEvent(ScreenLocker::KSldApp::self(), event);
|
|
|
|
if (event->isAccepted()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// continue normal processing
|
2016-02-15 12:42:48 +00:00
|
|
|
input()->keyboard()->update();
|
2016-02-16 16:14:28 +00:00
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
seat->setTimestamp(event->timestamp());
|
2016-02-10 13:40:56 +00:00
|
|
|
if (!keyboardSurfaceAllowed()) {
|
|
|
|
// don't pass event to seat
|
|
|
|
return true;
|
2016-02-03 14:17:49 +00:00
|
|
|
}
|
|
|
|
switch (event->type()) {
|
|
|
|
case QEvent::KeyPress:
|
2021-03-24 10:07:09 +00:00
|
|
|
seat->notifyKeyboardKey(event->nativeScanCode(), KWaylandServer::KeyboardKeyState::Pressed);
|
2016-02-03 14:17:49 +00:00
|
|
|
break;
|
|
|
|
case QEvent::KeyRelease:
|
2021-03-24 10:07:09 +00:00
|
|
|
seat->notifyKeyboardKey(event->nativeScanCode(), KWaylandServer::KeyboardKeyState::Released);
|
2016-02-03 14:17:49 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchDown(qint32 id, const QPointF &pos, quint32 time) override {
|
2016-02-10 15:43:06 +00:00
|
|
|
if (!waylandServer()->isScreenLocked()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
seat->setTimestamp(time);
|
|
|
|
if (touchSurfaceAllowed()) {
|
2021-03-11 08:44:03 +00:00
|
|
|
seat->notifyTouchDown(id, pos);
|
2016-02-10 15:43:06 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchMotion(qint32 id, const QPointF &pos, quint32 time) override {
|
2016-02-10 15:43:06 +00:00
|
|
|
if (!waylandServer()->isScreenLocked()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
seat->setTimestamp(time);
|
|
|
|
if (touchSurfaceAllowed()) {
|
2021-03-11 08:44:03 +00:00
|
|
|
seat->notifyTouchMotion(id, pos);
|
2016-02-10 15:43:06 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchUp(qint32 id, quint32 time) override {
|
2016-02-10 15:43:06 +00:00
|
|
|
if (!waylandServer()->isScreenLocked()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
seat->setTimestamp(time);
|
|
|
|
if (touchSurfaceAllowed()) {
|
2021-03-11 08:44:03 +00:00
|
|
|
seat->notifyTouchUp(id);
|
2016-02-10 15:43:06 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2016-10-27 07:10:08 +00:00
|
|
|
bool pinchGestureBegin(int fingerCount, quint32 time) override {
|
|
|
|
Q_UNUSED(fingerCount)
|
|
|
|
Q_UNUSED(time)
|
|
|
|
// no touchpad multi-finger gestures on lock screen
|
|
|
|
return waylandServer()->isScreenLocked();
|
|
|
|
}
|
|
|
|
bool pinchGestureUpdate(qreal scale, qreal angleDelta, const QSizeF &delta, quint32 time) override {
|
|
|
|
Q_UNUSED(scale)
|
|
|
|
Q_UNUSED(angleDelta)
|
|
|
|
Q_UNUSED(delta)
|
|
|
|
Q_UNUSED(time)
|
|
|
|
// no touchpad multi-finger gestures on lock screen
|
|
|
|
return waylandServer()->isScreenLocked();
|
|
|
|
}
|
|
|
|
bool pinchGestureEnd(quint32 time) override {
|
|
|
|
Q_UNUSED(time)
|
|
|
|
// no touchpad multi-finger gestures on lock screen
|
|
|
|
return waylandServer()->isScreenLocked();
|
|
|
|
}
|
|
|
|
bool pinchGestureCancelled(quint32 time) override {
|
|
|
|
Q_UNUSED(time)
|
|
|
|
// no touchpad multi-finger gestures on lock screen
|
|
|
|
return waylandServer()->isScreenLocked();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool swipeGestureBegin(int fingerCount, quint32 time) override {
|
|
|
|
Q_UNUSED(fingerCount)
|
|
|
|
Q_UNUSED(time)
|
|
|
|
// no touchpad multi-finger gestures on lock screen
|
|
|
|
return waylandServer()->isScreenLocked();
|
|
|
|
}
|
|
|
|
bool swipeGestureUpdate(const QSizeF &delta, quint32 time) override {
|
|
|
|
Q_UNUSED(delta)
|
|
|
|
Q_UNUSED(time)
|
|
|
|
// no touchpad multi-finger gestures on lock screen
|
|
|
|
return waylandServer()->isScreenLocked();
|
|
|
|
}
|
|
|
|
bool swipeGestureEnd(quint32 time) override {
|
|
|
|
Q_UNUSED(time)
|
|
|
|
// no touchpad multi-finger gestures on lock screen
|
|
|
|
return waylandServer()->isScreenLocked();
|
|
|
|
}
|
|
|
|
bool swipeGestureCancelled(quint32 time) override {
|
|
|
|
Q_UNUSED(time)
|
|
|
|
// no touchpad multi-finger gestures on lock screen
|
|
|
|
return waylandServer()->isScreenLocked();
|
|
|
|
}
|
2021-08-05 08:31:08 +00:00
|
|
|
bool holdGestureBegin(int fingerCount, quint32 time) override {
|
|
|
|
Q_UNUSED(fingerCount)
|
|
|
|
Q_UNUSED(time)
|
|
|
|
// no touchpad multi-finger gestures on lock screen
|
|
|
|
return waylandServer()->isScreenLocked();
|
|
|
|
}
|
|
|
|
bool holdGestureEnd(quint32 time) override {
|
|
|
|
Q_UNUSED(time)
|
|
|
|
// no touchpad multi-finger gestures on lock screen
|
|
|
|
return waylandServer()->isScreenLocked();
|
|
|
|
}
|
2016-02-09 16:18:58 +00:00
|
|
|
private:
|
2020-04-29 15:18:41 +00:00
|
|
|
bool surfaceAllowed(KWaylandServer::SurfaceInterface *(KWaylandServer::SeatInterface::*method)() const) const {
|
|
|
|
if (KWaylandServer::SurfaceInterface *s = (waylandServer()->seat()->*method)()) {
|
2016-02-09 16:18:58 +00:00
|
|
|
if (Toplevel *t = waylandServer()->findClient(s)) {
|
|
|
|
return t->isLockScreen() || t->isInputMethod();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2016-02-10 13:40:56 +00:00
|
|
|
bool pointerSurfaceAllowed() const {
|
2020-04-29 15:18:41 +00:00
|
|
|
return surfaceAllowed(&KWaylandServer::SeatInterface::focusedPointerSurface);
|
2016-02-10 13:40:56 +00:00
|
|
|
}
|
|
|
|
bool keyboardSurfaceAllowed() const {
|
2020-04-29 15:18:41 +00:00
|
|
|
return surfaceAllowed(&KWaylandServer::SeatInterface::focusedKeyboardSurface);
|
2016-02-10 13:40:56 +00:00
|
|
|
}
|
2016-02-10 15:43:06 +00:00
|
|
|
bool touchSurfaceAllowed() const {
|
2020-04-29 15:18:41 +00:00
|
|
|
return surfaceAllowed(&KWaylandServer::SeatInterface::focusedTouchSurface);
|
2016-02-10 15:43:06 +00:00
|
|
|
}
|
2016-02-03 14:17:49 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class EffectsFilter : public InputEventFilter {
|
|
|
|
public:
|
|
|
|
bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override {
|
|
|
|
Q_UNUSED(nativeButton)
|
|
|
|
if (!effects) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return static_cast<EffectsHandlerImpl*>(effects)->checkInputWindowEvent(event);
|
|
|
|
}
|
2019-11-14 12:50:45 +00:00
|
|
|
bool wheelEvent(QWheelEvent *event) override {
|
|
|
|
if (!effects) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return static_cast<EffectsHandlerImpl*>(effects)->checkInputWindowEvent(event);
|
|
|
|
}
|
2016-02-03 14:17:49 +00:00
|
|
|
bool keyEvent(QKeyEvent *event) override {
|
|
|
|
if (!effects || !static_cast< EffectsHandlerImpl* >(effects)->hasKeyboardGrab()) {
|
|
|
|
return false;
|
|
|
|
}
|
2016-08-11 09:17:09 +00:00
|
|
|
waylandServer()->seat()->setFocusedKeyboardSurface(nullptr);
|
2016-09-15 06:47:01 +00:00
|
|
|
passToWaylandServer(event);
|
2016-02-03 14:17:49 +00:00
|
|
|
static_cast< EffectsHandlerImpl* >(effects)->grabbedKeyboardEvent(event);
|
|
|
|
return true;
|
|
|
|
}
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchDown(qint32 id, const QPointF &pos, quint32 time) override {
|
2016-08-15 13:44:10 +00:00
|
|
|
if (!effects) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return static_cast< EffectsHandlerImpl* >(effects)->touchDown(id, pos, time);
|
|
|
|
}
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchMotion(qint32 id, const QPointF &pos, quint32 time) override {
|
2016-08-15 13:44:10 +00:00
|
|
|
if (!effects) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return static_cast< EffectsHandlerImpl* >(effects)->touchMotion(id, pos, time);
|
|
|
|
}
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchUp(qint32 id, quint32 time) override {
|
2016-08-15 13:44:10 +00:00
|
|
|
if (!effects) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return static_cast< EffectsHandlerImpl* >(effects)->touchUp(id, time);
|
|
|
|
}
|
2022-01-19 18:15:31 +00:00
|
|
|
bool tabletToolEvent(TabletEvent *event) override {
|
|
|
|
if (!effects) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return static_cast< EffectsHandlerImpl* >(effects)->tabletToolEvent(event);
|
|
|
|
}
|
|
|
|
bool tabletToolButtonEvent(uint button, bool pressed, const TabletToolId &tabletToolId) override {
|
|
|
|
if (!effects) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return static_cast< EffectsHandlerImpl* >(effects)->tabletToolButtonEvent(button, pressed, tabletToolId);
|
|
|
|
}
|
|
|
|
bool tabletPadButtonEvent(uint button, bool pressed, const TabletPadId &tabletPadId) override {
|
|
|
|
if (!effects) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return static_cast< EffectsHandlerImpl* >(effects)->tabletPadButtonEvent(button, pressed, tabletPadId);
|
|
|
|
}
|
|
|
|
bool tabletPadStripEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId) override {
|
|
|
|
if (!effects) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return static_cast< EffectsHandlerImpl* >(effects)->tabletPadStripEvent(number, position, isFinger, tabletPadId);
|
|
|
|
}
|
|
|
|
bool tabletPadRingEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId) override {
|
|
|
|
if (!effects) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return static_cast< EffectsHandlerImpl* >(effects)->tabletPadRingEvent(number, position, isFinger, tabletPadId);
|
|
|
|
}
|
2016-02-03 14:17:49 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class MoveResizeFilter : public InputEventFilter {
|
|
|
|
public:
|
|
|
|
bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override {
|
|
|
|
Q_UNUSED(nativeButton)
|
2019-04-18 12:28:11 +00:00
|
|
|
AbstractClient *c = workspace()->moveResizeClient();
|
2016-02-03 14:17:49 +00:00
|
|
|
if (!c) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
switch (event->type()) {
|
|
|
|
case QEvent::MouseMove:
|
2021-04-30 18:06:58 +00:00
|
|
|
c->updateInteractiveMoveResize(event->screenPos().toPoint());
|
2016-02-03 14:17:49 +00:00
|
|
|
break;
|
|
|
|
case QEvent::MouseButtonRelease:
|
|
|
|
if (event->buttons() == Qt::NoButton) {
|
2021-04-30 18:06:58 +00:00
|
|
|
c->endInteractiveMoveResize();
|
2016-02-03 14:17:49 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool wheelEvent(QWheelEvent *event) override {
|
|
|
|
Q_UNUSED(event)
|
|
|
|
// filter out while moving a window
|
2019-04-18 12:28:11 +00:00
|
|
|
return workspace()->moveResizeClient() != nullptr;
|
2016-02-03 14:17:49 +00:00
|
|
|
}
|
|
|
|
bool keyEvent(QKeyEvent *event) override {
|
2019-04-18 12:28:11 +00:00
|
|
|
AbstractClient *c = workspace()->moveResizeClient();
|
2016-02-03 14:17:49 +00:00
|
|
|
if (!c) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (event->type() == QEvent::KeyPress) {
|
|
|
|
c->keyPressEvent(event->key() | event->modifiers());
|
2021-04-30 18:06:58 +00:00
|
|
|
if (c->isInteractiveMove() || c->isInteractiveResize()) {
|
2016-02-03 14:17:49 +00:00
|
|
|
// only update if mode didn't end
|
2021-04-30 18:06:58 +00:00
|
|
|
c->updateInteractiveMoveResize(input()->globalPointer());
|
2016-02-03 14:17:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchDown(qint32 id, const QPointF &pos, quint32 time) override {
|
2018-09-15 00:00:24 +00:00
|
|
|
Q_UNUSED(id)
|
|
|
|
Q_UNUSED(pos)
|
|
|
|
Q_UNUSED(time)
|
2019-04-18 12:28:11 +00:00
|
|
|
AbstractClient *c = workspace()->moveResizeClient();
|
2018-09-15 00:00:24 +00:00
|
|
|
if (!c) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchMotion(qint32 id, const QPointF &pos, quint32 time) override {
|
2018-09-15 00:00:24 +00:00
|
|
|
Q_UNUSED(time)
|
2019-04-18 12:28:11 +00:00
|
|
|
AbstractClient *c = workspace()->moveResizeClient();
|
2018-09-15 00:00:24 +00:00
|
|
|
if (!c) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!m_set) {
|
|
|
|
m_id = id;
|
|
|
|
m_set = true;
|
|
|
|
}
|
|
|
|
if (m_id == id) {
|
2021-04-30 18:06:58 +00:00
|
|
|
c->updateInteractiveMoveResize(pos.toPoint());
|
2018-09-15 00:00:24 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchUp(qint32 id, quint32 time) override {
|
2018-09-15 00:00:24 +00:00
|
|
|
Q_UNUSED(time)
|
2019-04-18 12:28:11 +00:00
|
|
|
AbstractClient *c = workspace()->moveResizeClient();
|
2018-09-15 00:00:24 +00:00
|
|
|
if (!c) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (m_id == id || !m_set) {
|
2021-04-30 18:06:58 +00:00
|
|
|
c->endInteractiveMoveResize();
|
2018-09-15 00:00:24 +00:00
|
|
|
m_set = false;
|
|
|
|
// pass through to update decoration filter later on
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
m_set = false;
|
|
|
|
return true;
|
|
|
|
}
|
2021-11-09 01:49:01 +00:00
|
|
|
|
|
|
|
bool tabletToolEvent(TabletEvent *event) override {
|
|
|
|
AbstractClient *c = workspace()->moveResizeClient();
|
|
|
|
if (!c) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
switch (event->type()) {
|
|
|
|
case QEvent::TabletMove:
|
|
|
|
c->updateInteractiveMoveResize(event->globalPos());
|
|
|
|
break;
|
|
|
|
case QEvent::TabletRelease:
|
|
|
|
c->endInteractiveMoveResize();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Let TabletInputFilter receive the event, so the cursor position can be updated.
|
|
|
|
return false;
|
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
private:
|
2019-08-11 19:57:45 +00:00
|
|
|
qint32 m_id = 0;
|
2018-09-15 00:00:24 +00:00
|
|
|
bool m_set = false;
|
2016-02-03 14:17:49 +00:00
|
|
|
};
|
|
|
|
|
2016-11-15 13:23:51 +00:00
|
|
|
class WindowSelectorFilter : public InputEventFilter {
|
|
|
|
public:
|
|
|
|
bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override {
|
|
|
|
Q_UNUSED(nativeButton)
|
|
|
|
if (!m_active) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
switch (event->type()) {
|
|
|
|
case QEvent::MouseButtonRelease:
|
|
|
|
if (event->buttons() == Qt::NoButton) {
|
|
|
|
if (event->button() == Qt::RightButton) {
|
|
|
|
cancel();
|
|
|
|
} else {
|
2017-03-27 17:58:03 +00:00
|
|
|
accept(event->globalPos());
|
2016-11-15 13:23:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool wheelEvent(QWheelEvent *event) override {
|
|
|
|
Q_UNUSED(event)
|
|
|
|
// filter out while selecting a window
|
|
|
|
return m_active;
|
|
|
|
}
|
|
|
|
bool keyEvent(QKeyEvent *event) override {
|
|
|
|
Q_UNUSED(event)
|
|
|
|
if (!m_active) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
waylandServer()->seat()->setFocusedKeyboardSurface(nullptr);
|
|
|
|
passToWaylandServer(event);
|
|
|
|
|
|
|
|
if (event->type() == QEvent::KeyPress) {
|
|
|
|
// x11 variant does this on key press, so do the same
|
|
|
|
if (event->key() == Qt::Key_Escape) {
|
|
|
|
cancel();
|
|
|
|
} else if (event->key() == Qt::Key_Enter ||
|
|
|
|
event->key() == Qt::Key_Return ||
|
|
|
|
event->key() == Qt::Key_Space) {
|
2017-03-27 17:58:03 +00:00
|
|
|
accept(input()->globalPointer());
|
2016-11-15 13:23:51 +00:00
|
|
|
}
|
|
|
|
if (input()->supportsPointerWarping()) {
|
|
|
|
int mx = 0;
|
|
|
|
int my = 0;
|
|
|
|
if (event->key() == Qt::Key_Left) {
|
|
|
|
mx = -10;
|
|
|
|
}
|
|
|
|
if (event->key() == Qt::Key_Right) {
|
|
|
|
mx = 10;
|
|
|
|
}
|
|
|
|
if (event->key() == Qt::Key_Up) {
|
|
|
|
my = -10;
|
|
|
|
}
|
|
|
|
if (event->key() == Qt::Key_Down) {
|
|
|
|
my = 10;
|
|
|
|
}
|
|
|
|
if (event->modifiers() & Qt::ControlModifier) {
|
|
|
|
mx /= 10;
|
|
|
|
my /= 10;
|
|
|
|
}
|
|
|
|
input()->warpPointer(input()->globalPointer() + QPointF(mx, my));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// filter out while selecting a window
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchDown(qint32 id, const QPointF &pos, quint32 time) override {
|
2017-03-27 17:58:03 +00:00
|
|
|
Q_UNUSED(time)
|
|
|
|
if (!isActive()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
m_touchPoints.insert(id, pos);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchMotion(qint32 id, const QPointF &pos, quint32 time) override {
|
2017-03-27 17:58:03 +00:00
|
|
|
Q_UNUSED(time)
|
|
|
|
if (!isActive()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
auto it = m_touchPoints.find(id);
|
|
|
|
if (it != m_touchPoints.end()) {
|
|
|
|
*it = pos;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchUp(qint32 id, quint32 time) override {
|
2017-03-27 17:58:03 +00:00
|
|
|
Q_UNUSED(time)
|
|
|
|
if (!isActive()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
auto it = m_touchPoints.find(id);
|
|
|
|
if (it != m_touchPoints.end()) {
|
|
|
|
const auto pos = it.value();
|
|
|
|
m_touchPoints.erase(it);
|
|
|
|
if (m_touchPoints.isEmpty()) {
|
|
|
|
accept(pos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-11-15 13:23:51 +00:00
|
|
|
bool isActive() const {
|
|
|
|
return m_active;
|
|
|
|
}
|
|
|
|
void start(std::function<void(KWin::Toplevel*)> callback) {
|
|
|
|
Q_ASSERT(!m_active);
|
|
|
|
m_active = true;
|
|
|
|
m_callback = callback;
|
|
|
|
input()->keyboard()->update();
|
2021-10-28 07:53:36 +00:00
|
|
|
input()->touch()->cancel();
|
2016-11-15 13:23:51 +00:00
|
|
|
}
|
2016-11-23 14:53:17 +00:00
|
|
|
void start(std::function<void(const QPoint &)> callback) {
|
|
|
|
Q_ASSERT(!m_active);
|
|
|
|
m_active = true;
|
|
|
|
m_pointSelectionFallback = callback;
|
|
|
|
input()->keyboard()->update();
|
2021-10-28 07:53:36 +00:00
|
|
|
input()->touch()->cancel();
|
2016-11-23 14:53:17 +00:00
|
|
|
}
|
2016-11-15 13:23:51 +00:00
|
|
|
private:
|
|
|
|
void deactivate() {
|
|
|
|
m_active = false;
|
|
|
|
m_callback = std::function<void(KWin::Toplevel*)>();
|
2016-11-23 14:53:17 +00:00
|
|
|
m_pointSelectionFallback = std::function<void(const QPoint &)>();
|
2016-11-15 13:23:51 +00:00
|
|
|
input()->pointer()->removeWindowSelectionCursor();
|
|
|
|
input()->keyboard()->update();
|
2017-03-27 17:58:03 +00:00
|
|
|
m_touchPoints.clear();
|
2016-11-15 13:23:51 +00:00
|
|
|
}
|
|
|
|
void cancel() {
|
2016-11-23 14:53:17 +00:00
|
|
|
if (m_callback) {
|
|
|
|
m_callback(nullptr);
|
|
|
|
}
|
|
|
|
if (m_pointSelectionFallback) {
|
|
|
|
m_pointSelectionFallback(QPoint(-1, -1));
|
|
|
|
}
|
2016-11-15 13:23:51 +00:00
|
|
|
deactivate();
|
|
|
|
}
|
2017-03-27 17:58:03 +00:00
|
|
|
void accept(const QPoint &pos) {
|
2016-11-23 14:53:17 +00:00
|
|
|
if (m_callback) {
|
|
|
|
// TODO: this ignores shaped windows
|
2017-03-27 17:58:03 +00:00
|
|
|
m_callback(input()->findToplevel(pos));
|
2016-11-23 14:53:17 +00:00
|
|
|
}
|
|
|
|
if (m_pointSelectionFallback) {
|
2017-03-27 17:58:03 +00:00
|
|
|
m_pointSelectionFallback(pos);
|
2016-11-23 14:53:17 +00:00
|
|
|
}
|
2016-11-15 13:23:51 +00:00
|
|
|
deactivate();
|
|
|
|
}
|
2017-03-27 17:58:03 +00:00
|
|
|
void accept(const QPointF &pos) {
|
|
|
|
accept(pos.toPoint());
|
|
|
|
}
|
2016-11-15 13:23:51 +00:00
|
|
|
bool m_active = false;
|
|
|
|
std::function<void(KWin::Toplevel*)> m_callback;
|
2016-11-23 14:53:17 +00:00
|
|
|
std::function<void(const QPoint &)> m_pointSelectionFallback;
|
2017-03-27 17:58:03 +00:00
|
|
|
QMap<quint32, QPointF> m_touchPoints;
|
2016-11-15 13:23:51 +00:00
|
|
|
};
|
|
|
|
|
2016-02-03 14:17:49 +00:00
|
|
|
class GlobalShortcutFilter : public InputEventFilter {
|
|
|
|
public:
|
2020-04-02 10:09:47 +00:00
|
|
|
GlobalShortcutFilter() {
|
|
|
|
m_powerDown = new QTimer;
|
|
|
|
m_powerDown->setSingleShot(true);
|
|
|
|
m_powerDown->setInterval(1000);
|
|
|
|
}
|
|
|
|
~GlobalShortcutFilter() {
|
|
|
|
delete m_powerDown;
|
|
|
|
}
|
|
|
|
|
2016-02-03 14:17:49 +00:00
|
|
|
bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override {
|
|
|
|
Q_UNUSED(nativeButton);
|
|
|
|
if (event->type() == QEvent::MouseButtonPress) {
|
|
|
|
if (input()->shortcuts()->processPointerPressed(event->modifiers(), event->buttons())) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool wheelEvent(QWheelEvent *event) override {
|
|
|
|
if (event->modifiers() == Qt::NoModifier) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
PointerAxisDirection direction = PointerAxisUp;
|
|
|
|
if (event->angleDelta().x() < 0) {
|
|
|
|
direction = PointerAxisRight;
|
|
|
|
} else if (event->angleDelta().x() > 0) {
|
|
|
|
direction = PointerAxisLeft;
|
|
|
|
} else if (event->angleDelta().y() < 0) {
|
|
|
|
direction = PointerAxisDown;
|
|
|
|
} else if (event->angleDelta().y() > 0) {
|
|
|
|
direction = PointerAxisUp;
|
|
|
|
}
|
|
|
|
return input()->shortcuts()->processAxis(event->modifiers(), direction);
|
|
|
|
}
|
|
|
|
bool keyEvent(QKeyEvent *event) override {
|
2020-02-06 11:12:30 +00:00
|
|
|
if (event->key() == Qt::Key_PowerOff) {
|
2020-04-02 10:09:47 +00:00
|
|
|
const auto modifiers = static_cast<KeyEvent*>(event)->modifiersRelevantForGlobalShortcuts();
|
|
|
|
if (event->type() == QEvent::KeyPress && !event->isAutoRepeat()) {
|
|
|
|
QObject::connect(m_powerDown, &QTimer::timeout, input()->shortcuts(), [this, modifiers] {
|
|
|
|
QObject::disconnect(m_powerDown, &QTimer::timeout, input()->shortcuts(), nullptr);
|
|
|
|
m_powerDown->stop();
|
|
|
|
input()->shortcuts()->processKey(modifiers, Qt::Key_PowerDown);
|
|
|
|
});
|
|
|
|
m_powerDown->start();
|
|
|
|
return true;
|
2020-02-06 11:12:30 +00:00
|
|
|
} else if (event->type() == QEvent::KeyRelease) {
|
2020-04-02 10:09:47 +00:00
|
|
|
const bool ret = !m_powerDown->isActive() || input()->shortcuts()->processKey(modifiers, event->key());
|
|
|
|
m_powerDown->stop();
|
|
|
|
return ret;
|
2020-02-06 11:12:30 +00:00
|
|
|
}
|
|
|
|
} else if (event->type() == QEvent::KeyPress) {
|
2020-04-29 08:45:02 +00:00
|
|
|
if (!waylandServer()->isKeyboardShortcutsInhibited()) {
|
|
|
|
return input()->shortcuts()->processKey(static_cast<KeyEvent*>(event)->modifiersRelevantForGlobalShortcuts(), event->key());
|
|
|
|
}
|
2016-02-03 14:17:49 +00:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2017-03-18 10:00:30 +00:00
|
|
|
bool swipeGestureBegin(int fingerCount, quint32 time) override {
|
|
|
|
Q_UNUSED(time)
|
|
|
|
input()->shortcuts()->processSwipeStart(fingerCount);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool swipeGestureUpdate(const QSizeF &delta, quint32 time) override {
|
|
|
|
Q_UNUSED(time)
|
|
|
|
input()->shortcuts()->processSwipeUpdate(delta);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool swipeGestureCancelled(quint32 time) override {
|
|
|
|
Q_UNUSED(time)
|
|
|
|
input()->shortcuts()->processSwipeCancel();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool swipeGestureEnd(quint32 time) override {
|
|
|
|
Q_UNUSED(time)
|
|
|
|
input()->shortcuts()->processSwipeEnd();
|
|
|
|
return false;
|
|
|
|
}
|
2020-02-06 11:12:30 +00:00
|
|
|
|
|
|
|
private:
|
2020-04-02 10:09:47 +00:00
|
|
|
QTimer* m_powerDown = nullptr;
|
2016-02-03 14:17:49 +00:00
|
|
|
};
|
|
|
|
|
2018-02-11 09:49:31 +00:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
enum class MouseAction {
|
|
|
|
ModifierOnly,
|
|
|
|
ModifierAndWindow
|
|
|
|
};
|
|
|
|
std::pair<bool, bool> performClientMouseAction(QMouseEvent *event, AbstractClient *client, MouseAction action = MouseAction::ModifierOnly)
|
|
|
|
{
|
|
|
|
Options::MouseCommand command = Options::MouseNothing;
|
|
|
|
bool wasAction = false;
|
|
|
|
if (static_cast<MouseEvent*>(event)->modifiersRelevantForGlobalShortcuts() == options->commandAllModifier()) {
|
2018-10-07 19:02:09 +00:00
|
|
|
if (!input()->pointer()->isConstrained() && !workspace()->globalShortcutsDisabled()) {
|
2018-10-06 16:03:19 +00:00
|
|
|
wasAction = true;
|
|
|
|
switch (event->button()) {
|
|
|
|
case Qt::LeftButton:
|
|
|
|
command = options->commandAll1();
|
|
|
|
break;
|
|
|
|
case Qt::MiddleButton:
|
|
|
|
command = options->commandAll2();
|
|
|
|
break;
|
|
|
|
case Qt::RightButton:
|
|
|
|
command = options->commandAll3();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// nothing
|
|
|
|
break;
|
|
|
|
}
|
2018-02-11 09:49:31 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (action == MouseAction::ModifierAndWindow) {
|
|
|
|
command = client->getMouseCommand(event->button(), &wasAction);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (wasAction) {
|
|
|
|
return std::make_pair(wasAction, !client->performMouseCommand(command, event->globalPos()));
|
|
|
|
}
|
|
|
|
return std::make_pair(wasAction, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::pair<bool, bool> performClientWheelAction(QWheelEvent *event, AbstractClient *c, MouseAction action = MouseAction::ModifierOnly)
|
|
|
|
{
|
|
|
|
bool wasAction = false;
|
|
|
|
Options::MouseCommand command = Options::MouseNothing;
|
|
|
|
if (static_cast<WheelEvent*>(event)->modifiersRelevantForGlobalShortcuts() == options->commandAllModifier()) {
|
2018-10-07 19:02:09 +00:00
|
|
|
if (!input()->pointer()->isConstrained() && !workspace()->globalShortcutsDisabled()) {
|
|
|
|
wasAction = true;
|
|
|
|
command = options->operationWindowMouseWheel(-1 * event->angleDelta().y());
|
|
|
|
}
|
2018-02-11 09:49:31 +00:00
|
|
|
} else {
|
|
|
|
if (action == MouseAction::ModifierAndWindow) {
|
|
|
|
command = c->getWheelCommand(Qt::Vertical, &wasAction);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (wasAction) {
|
|
|
|
return std::make_pair(wasAction, !c->performMouseCommand(command, event->globalPos()));
|
|
|
|
}
|
|
|
|
return std::make_pair(wasAction, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-02-03 14:17:49 +00:00
|
|
|
class InternalWindowEventFilter : public InputEventFilter {
|
|
|
|
bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override {
|
|
|
|
Q_UNUSED(nativeButton)
|
2021-11-29 20:19:38 +00:00
|
|
|
if (!input()->pointer()->focus() || !input()->pointer()->focus()->isInternal()) {
|
2016-02-03 14:17:49 +00:00
|
|
|
return false;
|
|
|
|
}
|
2021-11-29 20:19:38 +00:00
|
|
|
QWindow *internal = static_cast<InternalClient *>(input()->pointer()->focus())->internalWindow();
|
|
|
|
QMouseEvent mouseEvent(event->type(),
|
|
|
|
event->pos() - internal->position(),
|
|
|
|
event->globalPos(),
|
|
|
|
event->button(), event->buttons(), event->modifiers());
|
|
|
|
QCoreApplication::sendEvent(internal, &mouseEvent);
|
|
|
|
return mouseEvent.isAccepted();
|
2016-02-03 14:17:49 +00:00
|
|
|
}
|
|
|
|
bool wheelEvent(QWheelEvent *event) override {
|
2021-11-29 20:19:38 +00:00
|
|
|
if (!input()->pointer()->focus() || !input()->pointer()->focus()->isInternal()) {
|
2016-02-03 14:17:49 +00:00
|
|
|
return false;
|
|
|
|
}
|
2021-11-29 20:19:38 +00:00
|
|
|
QWindow *internal = static_cast<InternalClient *>(input()->pointer()->focus())->internalWindow();
|
2021-05-13 15:05:53 +00:00
|
|
|
const QPointF localPos = event->globalPosF() - internal->position();
|
2016-02-03 14:17:49 +00:00
|
|
|
const Qt::Orientation orientation = (event->angleDelta().x() != 0) ? Qt::Horizontal : Qt::Vertical;
|
|
|
|
const int delta = event->angleDelta().x() != 0 ? event->angleDelta().x() : event->angleDelta().y();
|
2021-11-29 20:19:38 +00:00
|
|
|
QWheelEvent wheelEvent(localPos, event->globalPosF(), QPoint(),
|
|
|
|
event->angleDelta() * -1,
|
|
|
|
delta * -1,
|
|
|
|
orientation,
|
|
|
|
event->buttons(),
|
|
|
|
event->modifiers());
|
|
|
|
QCoreApplication::sendEvent(internal, &wheelEvent);
|
|
|
|
return wheelEvent.isAccepted();
|
2016-02-03 14:17:49 +00:00
|
|
|
}
|
|
|
|
bool keyEvent(QKeyEvent *event) override {
|
2021-07-15 09:08:19 +00:00
|
|
|
const QList<InternalClient *> &clients = workspace()->internalClients();
|
2016-02-17 12:34:24 +00:00
|
|
|
QWindow *found = nullptr;
|
2021-07-15 09:08:19 +00:00
|
|
|
for (auto it = clients.crbegin(); it != clients.crend(); ++it) {
|
2016-02-17 12:34:24 +00:00
|
|
|
if (QWindow *w = (*it)->internalWindow()) {
|
|
|
|
if (!w->isVisible()) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-08-30 07:52:27 +00:00
|
|
|
if (!workspace()->geometry().contains(w->geometry())) {
|
2016-03-09 21:21:10 +00:00
|
|
|
continue;
|
|
|
|
}
|
2016-08-29 13:34:02 +00:00
|
|
|
if (w->property("_q_showWithoutActivating").toBool()) {
|
|
|
|
continue;
|
|
|
|
}
|
2018-01-02 20:51:18 +00:00
|
|
|
if (w->property("outputOnly").toBool()) {
|
|
|
|
continue;
|
|
|
|
}
|
2018-05-01 14:21:12 +00:00
|
|
|
if (w->flags().testFlag(Qt::ToolTip)) {
|
|
|
|
continue;
|
|
|
|
}
|
2016-02-17 12:34:24 +00:00
|
|
|
found = w;
|
|
|
|
break;
|
|
|
|
}
|
2021-07-15 09:08:19 +00:00
|
|
|
}
|
2021-07-23 06:20:43 +00:00
|
|
|
if (QGuiApplication::focusWindow() != found) {
|
2021-07-15 09:08:19 +00:00
|
|
|
QWindowSystemInterface::handleWindowActivated(found);
|
|
|
|
}
|
2016-02-17 12:34:24 +00:00
|
|
|
if (!found) {
|
2016-02-03 14:17:49 +00:00
|
|
|
return false;
|
|
|
|
}
|
2017-07-22 10:49:57 +00:00
|
|
|
auto xkb = input()->keyboard()->xkb();
|
2021-03-15 17:34:41 +00:00
|
|
|
Qt::Key key = xkb->toQtKey( xkb->toKeysym(event->nativeScanCode()),
|
|
|
|
event->nativeScanCode(),
|
|
|
|
Qt::KeyboardModifiers(),
|
|
|
|
true /* workaround for QTBUG-62102 */ );
|
2017-07-22 10:49:57 +00:00
|
|
|
QKeyEvent internalEvent(event->type(), key,
|
|
|
|
event->modifiers(), event->nativeScanCode(), event->nativeVirtualKey(),
|
|
|
|
event->nativeModifiers(), event->text());
|
|
|
|
internalEvent.setAccepted(false);
|
|
|
|
if (QCoreApplication::sendEvent(found, &internalEvent)) {
|
2016-08-11 09:17:09 +00:00
|
|
|
waylandServer()->seat()->setFocusedKeyboardSurface(nullptr);
|
2016-09-15 06:47:01 +00:00
|
|
|
passToWaylandServer(event);
|
2016-08-11 09:17:09 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2016-02-03 14:17:49 +00:00
|
|
|
}
|
2016-05-23 15:07:08 +00:00
|
|
|
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchDown(qint32 id, const QPointF &pos, quint32 time) override {
|
2016-05-23 15:07:08 +00:00
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
if (seat->isTouchSequence()) {
|
|
|
|
// something else is getting the events
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
auto touch = input()->touch();
|
|
|
|
if (touch->internalPressId() != -1) {
|
2018-09-15 00:00:24 +00:00
|
|
|
// already on internal window, ignore further touch points, but filter out
|
2021-05-12 16:50:35 +00:00
|
|
|
m_pressedIds.insert(id);
|
2016-05-23 15:07:08 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// a new touch point
|
|
|
|
seat->setTimestamp(time);
|
2021-11-29 20:19:38 +00:00
|
|
|
if (!input()->touch()->focus() || !input()->touch()->focus()->isInternal()) {
|
2016-05-23 15:07:08 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
touch->setInternalPressId(id);
|
|
|
|
// Qt's touch event API is rather complex, let's do fake mouse events instead
|
2021-11-29 20:19:38 +00:00
|
|
|
QWindow *internal = static_cast<InternalClient *>(input()->touch()->focus())->internalWindow();
|
2016-05-23 15:07:08 +00:00
|
|
|
m_lastGlobalTouchPos = pos;
|
2021-05-13 15:05:53 +00:00
|
|
|
m_lastLocalTouchPos = pos - internal->position();
|
2018-09-15 00:00:24 +00:00
|
|
|
|
|
|
|
QEnterEvent enterEvent(m_lastLocalTouchPos, m_lastLocalTouchPos, pos);
|
2021-01-28 14:45:57 +00:00
|
|
|
QCoreApplication::sendEvent(internal, &enterEvent);
|
2018-09-15 00:00:24 +00:00
|
|
|
|
2016-05-23 15:07:08 +00:00
|
|
|
QMouseEvent e(QEvent::MouseButtonPress, m_lastLocalTouchPos, pos, Qt::LeftButton, Qt::LeftButton, input()->keyboardModifiers());
|
|
|
|
e.setAccepted(false);
|
2021-01-28 14:45:57 +00:00
|
|
|
QCoreApplication::sendEvent(internal, &e);
|
2016-05-23 15:07:08 +00:00
|
|
|
return true;
|
|
|
|
}
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchMotion(qint32 id, const QPointF &pos, quint32 time) override {
|
2016-05-23 15:07:08 +00:00
|
|
|
auto touch = input()->touch();
|
2021-11-29 20:19:38 +00:00
|
|
|
if (!input()->touch()->focus() || !input()->touch()->focus()->isInternal()) {
|
2016-05-23 15:07:08 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (touch->internalPressId() == -1) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
waylandServer()->seat()->setTimestamp(time);
|
2021-05-12 16:50:35 +00:00
|
|
|
if (touch->internalPressId() != qint32(id) || m_pressedIds.contains(id)) {
|
2016-05-23 15:07:08 +00:00
|
|
|
// ignore, but filter out
|
|
|
|
return true;
|
|
|
|
}
|
2021-11-29 20:19:38 +00:00
|
|
|
QWindow *internal = static_cast<InternalClient *>(input()->touch()->focus())->internalWindow();
|
2016-05-23 15:07:08 +00:00
|
|
|
m_lastGlobalTouchPos = pos;
|
|
|
|
m_lastLocalTouchPos = pos - QPointF(internal->x(), internal->y());
|
2018-09-15 00:00:24 +00:00
|
|
|
|
2016-05-23 15:07:08 +00:00
|
|
|
QMouseEvent e(QEvent::MouseMove, m_lastLocalTouchPos, m_lastGlobalTouchPos, Qt::LeftButton, Qt::LeftButton, input()->keyboardModifiers());
|
2021-01-28 14:45:57 +00:00
|
|
|
QCoreApplication::instance()->sendEvent(internal, &e);
|
2016-05-23 15:07:08 +00:00
|
|
|
return true;
|
|
|
|
}
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchUp(qint32 id, quint32 time) override {
|
2016-05-23 15:07:08 +00:00
|
|
|
auto touch = input()->touch();
|
2021-05-12 16:50:35 +00:00
|
|
|
const bool removed = m_pressedIds.remove(id);
|
2016-05-23 15:07:08 +00:00
|
|
|
if (touch->internalPressId() == -1) {
|
2021-05-12 16:50:35 +00:00
|
|
|
return removed;
|
2016-05-23 15:07:08 +00:00
|
|
|
}
|
|
|
|
waylandServer()->seat()->setTimestamp(time);
|
|
|
|
if (touch->internalPressId() != qint32(id)) {
|
|
|
|
// ignore, but filter out
|
|
|
|
return true;
|
|
|
|
}
|
2021-11-29 20:19:38 +00:00
|
|
|
if (!input()->touch()->focus() || !input()->touch()->focus()->isInternal()) {
|
|
|
|
return removed;
|
|
|
|
}
|
|
|
|
QWindow *internal = static_cast<InternalClient *>(input()->touch()->focus())->internalWindow();
|
2016-05-23 15:07:08 +00:00
|
|
|
// send mouse up
|
|
|
|
QMouseEvent e(QEvent::MouseButtonRelease, m_lastLocalTouchPos, m_lastGlobalTouchPos, Qt::LeftButton, Qt::MouseButtons(), input()->keyboardModifiers());
|
|
|
|
e.setAccepted(false);
|
2021-01-28 14:45:57 +00:00
|
|
|
QCoreApplication::sendEvent(internal, &e);
|
2016-05-23 15:07:08 +00:00
|
|
|
|
2018-09-15 00:00:24 +00:00
|
|
|
QEvent leaveEvent(QEvent::Leave);
|
2021-01-28 14:45:57 +00:00
|
|
|
QCoreApplication::sendEvent(internal, &leaveEvent);
|
2018-09-15 00:00:24 +00:00
|
|
|
|
2016-05-23 15:07:08 +00:00
|
|
|
m_lastGlobalTouchPos = QPointF();
|
|
|
|
m_lastLocalTouchPos = QPointF();
|
|
|
|
input()->touch()->setInternalPressId(-1);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
private:
|
2021-05-12 16:50:35 +00:00
|
|
|
QSet<qint32> m_pressedIds;
|
2016-05-23 15:07:08 +00:00
|
|
|
QPointF m_lastGlobalTouchPos;
|
|
|
|
QPointF m_lastLocalTouchPos;
|
2016-02-03 14:17:49 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class DecorationEventFilter : public InputEventFilter {
|
|
|
|
public:
|
|
|
|
bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override {
|
|
|
|
Q_UNUSED(nativeButton)
|
2016-02-12 12:30:00 +00:00
|
|
|
auto decoration = input()->pointer()->decoration();
|
2016-02-03 14:17:49 +00:00
|
|
|
if (!decoration) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const QPointF p = event->globalPos() - decoration->client()->pos();
|
|
|
|
switch (event->type()) {
|
|
|
|
case QEvent::MouseMove: {
|
|
|
|
QHoverEvent e(QEvent::HoverMove, p, p);
|
|
|
|
QCoreApplication::instance()->sendEvent(decoration->decoration(), &e);
|
2016-03-11 11:48:01 +00:00
|
|
|
decoration->client()->processDecorationMove(p.toPoint(), event->globalPos());
|
2016-02-03 14:17:49 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
case QEvent::MouseButtonPress:
|
|
|
|
case QEvent::MouseButtonRelease: {
|
2017-11-11 11:28:10 +00:00
|
|
|
const auto actionResult = performClientMouseAction(event, decoration->client());
|
|
|
|
if (actionResult.first) {
|
|
|
|
return actionResult.second;
|
|
|
|
}
|
2016-02-03 14:17:49 +00:00
|
|
|
QMouseEvent e(event->type(), p, event->globalPos(), event->button(), event->buttons(), event->modifiers());
|
2022-01-30 17:28:39 +00:00
|
|
|
e.setTimestamp(event->timestamp());
|
2016-02-03 14:17:49 +00:00
|
|
|
e.setAccepted(false);
|
|
|
|
QCoreApplication::sendEvent(decoration->decoration(), &e);
|
|
|
|
if (!e.isAccepted() && event->type() == QEvent::MouseButtonPress) {
|
|
|
|
decoration->client()->processDecorationButtonPress(&e);
|
|
|
|
}
|
|
|
|
if (event->type() == QEvent::MouseButtonRelease) {
|
|
|
|
decoration->client()->processDecorationButtonRelease(&e);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool wheelEvent(QWheelEvent *event) override {
|
2016-02-12 12:30:00 +00:00
|
|
|
auto decoration = input()->pointer()->decoration();
|
2016-02-03 14:17:49 +00:00
|
|
|
if (!decoration) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-11-11 11:28:10 +00:00
|
|
|
if (event->angleDelta().y() != 0) {
|
|
|
|
// client window action only on vertical scrolling
|
|
|
|
const auto actionResult = performClientWheelAction(event, decoration->client());
|
|
|
|
if (actionResult.first) {
|
|
|
|
return actionResult.second;
|
|
|
|
}
|
|
|
|
}
|
2016-02-03 14:17:49 +00:00
|
|
|
const QPointF localPos = event->globalPosF() - decoration->client()->pos();
|
|
|
|
const Qt::Orientation orientation = (event->angleDelta().x() != 0) ? Qt::Horizontal : Qt::Vertical;
|
|
|
|
const int delta = event->angleDelta().x() != 0 ? event->angleDelta().x() : event->angleDelta().y();
|
|
|
|
QWheelEvent e(localPos, event->globalPosF(), QPoint(),
|
|
|
|
event->angleDelta(),
|
|
|
|
delta,
|
|
|
|
orientation,
|
|
|
|
event->buttons(),
|
|
|
|
event->modifiers());
|
|
|
|
e.setAccepted(false);
|
2021-01-28 14:45:57 +00:00
|
|
|
QCoreApplication::sendEvent(decoration, &e);
|
2016-02-03 14:17:49 +00:00
|
|
|
if (e.isAccepted()) {
|
|
|
|
return true;
|
|
|
|
}
|
2016-05-12 08:28:40 +00:00
|
|
|
if ((orientation == Qt::Vertical) && decoration->client()->titlebarPositionUnderMouse()) {
|
2016-02-03 14:17:49 +00:00
|
|
|
decoration->client()->performMouseCommand(options->operationTitlebarMouseWheel(delta * -1),
|
|
|
|
event->globalPosF().toPoint());
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchDown(qint32 id, const QPointF &pos, quint32 time) override {
|
2016-05-12 14:33:03 +00:00
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
if (seat->isTouchSequence()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (input()->touch()->decorationPressId() != -1) {
|
|
|
|
// already on a decoration, ignore further touch points, but filter out
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
seat->setTimestamp(time);
|
|
|
|
auto decoration = input()->touch()->decoration();
|
|
|
|
if (!decoration) {
|
|
|
|
return false;
|
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
|
2016-05-12 14:33:03 +00:00
|
|
|
input()->touch()->setDecorationPressId(id);
|
|
|
|
m_lastGlobalTouchPos = pos;
|
|
|
|
m_lastLocalTouchPos = pos - decoration->client()->pos();
|
2018-09-15 00:00:24 +00:00
|
|
|
|
|
|
|
QHoverEvent hoverEvent(QEvent::HoverMove, m_lastLocalTouchPos, m_lastLocalTouchPos);
|
|
|
|
QCoreApplication::sendEvent(decoration->decoration(), &hoverEvent);
|
|
|
|
|
2016-05-12 14:33:03 +00:00
|
|
|
QMouseEvent e(QEvent::MouseButtonPress, m_lastLocalTouchPos, pos, Qt::LeftButton, Qt::LeftButton, input()->keyboardModifiers());
|
|
|
|
e.setAccepted(false);
|
|
|
|
QCoreApplication::sendEvent(decoration->decoration(), &e);
|
|
|
|
if (!e.isAccepted()) {
|
|
|
|
decoration->client()->processDecorationButtonPress(&e);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchMotion(qint32 id, const QPointF &pos, quint32 time) override {
|
2016-05-12 14:33:03 +00:00
|
|
|
Q_UNUSED(time)
|
|
|
|
auto decoration = input()->touch()->decoration();
|
|
|
|
if (!decoration) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (input()->touch()->decorationPressId() == -1) {
|
|
|
|
return false;
|
|
|
|
}
|
2016-06-29 09:03:43 +00:00
|
|
|
if (input()->touch()->decorationPressId() != qint32(id)) {
|
2016-05-12 14:33:03 +00:00
|
|
|
// ignore, but filter out
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
m_lastGlobalTouchPos = pos;
|
|
|
|
m_lastLocalTouchPos = pos - decoration->client()->pos();
|
2018-09-15 00:00:24 +00:00
|
|
|
|
|
|
|
QHoverEvent e(QEvent::HoverMove, m_lastLocalTouchPos, m_lastLocalTouchPos);
|
|
|
|
QCoreApplication::instance()->sendEvent(decoration->decoration(), &e);
|
|
|
|
decoration->client()->processDecorationMove(m_lastLocalTouchPos.toPoint(), pos.toPoint());
|
2016-05-12 14:33:03 +00:00
|
|
|
return true;
|
|
|
|
}
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchUp(qint32 id, quint32 time) override {
|
2016-05-12 14:33:03 +00:00
|
|
|
Q_UNUSED(time);
|
|
|
|
auto decoration = input()->touch()->decoration();
|
|
|
|
if (!decoration) {
|
2021-06-30 22:30:32 +00:00
|
|
|
// can happen when quick tiling
|
|
|
|
if (input()->touch()->decorationPressId() == id) {
|
|
|
|
m_lastGlobalTouchPos = QPointF();
|
|
|
|
m_lastLocalTouchPos = QPointF();
|
|
|
|
input()->touch()->setDecorationPressId(-1);
|
2021-10-21 07:46:34 +00:00
|
|
|
return true;
|
2021-06-30 22:30:32 +00:00
|
|
|
}
|
2016-05-12 14:33:03 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (input()->touch()->decorationPressId() == -1) {
|
|
|
|
return false;
|
|
|
|
}
|
2016-06-29 09:03:43 +00:00
|
|
|
if (input()->touch()->decorationPressId() != qint32(id)) {
|
2016-05-12 14:33:03 +00:00
|
|
|
// ignore, but filter out
|
|
|
|
return true;
|
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
|
2016-05-12 14:33:03 +00:00
|
|
|
// send mouse up
|
2018-09-15 00:00:24 +00:00
|
|
|
QMouseEvent e(QEvent::MouseButtonRelease, m_lastLocalTouchPos, m_lastGlobalTouchPos, Qt::LeftButton, Qt::MouseButtons(), input()->keyboardModifiers());
|
|
|
|
e.setAccepted(false);
|
|
|
|
QCoreApplication::sendEvent(decoration->decoration(), &e);
|
|
|
|
decoration->client()->processDecorationButtonRelease(&e);
|
|
|
|
|
|
|
|
QHoverEvent leaveEvent(QEvent::HoverLeave, QPointF(), QPointF());
|
|
|
|
QCoreApplication::sendEvent(decoration->decoration(), &leaveEvent);
|
2016-05-12 14:33:03 +00:00
|
|
|
|
|
|
|
m_lastGlobalTouchPos = QPointF();
|
|
|
|
m_lastLocalTouchPos = QPointF();
|
|
|
|
input()->touch()->setDecorationPressId(-1);
|
|
|
|
return true;
|
|
|
|
}
|
2021-11-09 01:49:01 +00:00
|
|
|
bool tabletToolEvent(TabletEvent *event) override {
|
|
|
|
auto decoration = input()->tablet()->decoration();
|
|
|
|
if (!decoration) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const QPointF p = event->globalPos() - decoration->client()->pos();
|
|
|
|
switch (event->type()) {
|
|
|
|
case QEvent::TabletMove:
|
|
|
|
case QEvent::TabletEnterProximity: {
|
|
|
|
QHoverEvent e(QEvent::HoverMove, p, p);
|
|
|
|
QCoreApplication::instance()->sendEvent(decoration->decoration(), &e);
|
|
|
|
decoration->client()->processDecorationMove(p.toPoint(), event->globalPos());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QEvent::TabletPress:
|
|
|
|
case QEvent::TabletRelease: {
|
|
|
|
const bool isPressed = event->type() == QEvent::TabletPress;
|
|
|
|
QMouseEvent e(isPressed ? QEvent::MouseButtonPress : QEvent::MouseButtonRelease,
|
|
|
|
p,
|
|
|
|
event->globalPos(),
|
|
|
|
Qt::LeftButton,
|
|
|
|
isPressed ? Qt::LeftButton : Qt::MouseButtons(),
|
|
|
|
input()->keyboardModifiers());
|
|
|
|
e.setAccepted(false);
|
|
|
|
QCoreApplication::sendEvent(decoration->decoration(), &e);
|
|
|
|
if (!e.isAccepted() && isPressed) {
|
|
|
|
decoration->client()->processDecorationButtonPress(&e);
|
|
|
|
}
|
|
|
|
if (event->type() == QEvent::TabletRelease) {
|
|
|
|
decoration->client()->processDecorationButtonRelease(&e);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QEvent::TabletLeaveProximity: {
|
|
|
|
QHoverEvent leaveEvent(QEvent::HoverLeave, QPointF(), QPointF());
|
|
|
|
QCoreApplication::sendEvent(decoration->decoration(), &leaveEvent);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Let TabletInputFilter receive the event, so the tablet can be registered and the cursor position can be updated.
|
|
|
|
return false;
|
|
|
|
}
|
2016-05-12 14:33:03 +00:00
|
|
|
private:
|
|
|
|
QPointF m_lastGlobalTouchPos;
|
|
|
|
QPointF m_lastLocalTouchPos;
|
2016-02-03 14:17:49 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef KWIN_BUILD_TABBOX
|
|
|
|
class TabBoxInputFilter : public InputEventFilter
|
|
|
|
{
|
|
|
|
public:
|
2016-03-04 13:15:27 +00:00
|
|
|
bool pointerEvent(QMouseEvent *event, quint32 button) override {
|
|
|
|
Q_UNUSED(button)
|
|
|
|
if (!TabBox::TabBox::self() || !TabBox::TabBox::self()->isGrabbed()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return TabBox::TabBox::self()->handleMouseEvent(event);
|
|
|
|
}
|
2016-02-03 14:17:49 +00:00
|
|
|
bool keyEvent(QKeyEvent *event) override {
|
|
|
|
if (!TabBox::TabBox::self() || !TabBox::TabBox::self()->isGrabbed()) {
|
|
|
|
return false;
|
|
|
|
}
|
2016-09-15 06:31:05 +00:00
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
seat->setFocusedKeyboardSurface(nullptr);
|
2018-07-15 18:44:21 +00:00
|
|
|
input()->pointer()->setEnableConstraints(false);
|
2016-09-15 06:31:05 +00:00
|
|
|
// pass the key event to the seat, so that it has a proper model of the currently hold keys
|
|
|
|
// this is important for combinations like alt+shift to ensure that shift is not considered pressed
|
2016-09-15 06:47:01 +00:00
|
|
|
passToWaylandServer(event);
|
2016-09-15 06:31:05 +00:00
|
|
|
|
2016-09-14 11:19:43 +00:00
|
|
|
if (event->type() == QEvent::KeyPress) {
|
2016-03-04 14:38:51 +00:00
|
|
|
TabBox::TabBox::self()->keyPress(event->modifiers() | event->key());
|
2016-12-26 13:19:56 +00:00
|
|
|
} else if (static_cast<KeyEvent*>(event)->modifiersRelevantForGlobalShortcuts() == Qt::NoModifier) {
|
2016-09-14 11:19:43 +00:00
|
|
|
TabBox::TabBox::self()->modifiersReleased();
|
|
|
|
}
|
2016-02-03 14:17:49 +00:00
|
|
|
return true;
|
|
|
|
}
|
2016-03-04 13:15:27 +00:00
|
|
|
bool wheelEvent(QWheelEvent *event) override {
|
|
|
|
if (!TabBox::TabBox::self() || !TabBox::TabBox::self()->isGrabbed()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return TabBox::TabBox::self()->handleWheelEvent(event);
|
|
|
|
}
|
2016-02-03 14:17:49 +00:00
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2016-02-11 14:51:25 +00:00
|
|
|
class ScreenEdgeInputFilter : public InputEventFilter
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override {
|
|
|
|
Q_UNUSED(nativeButton)
|
|
|
|
ScreenEdges::self()->isEntered(event);
|
|
|
|
// always forward
|
|
|
|
return false;
|
|
|
|
}
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchDown(qint32 id, const QPointF &pos, quint32 time) override {
|
2017-03-19 10:40:03 +00:00
|
|
|
Q_UNUSED(time)
|
2018-08-29 18:02:16 +00:00
|
|
|
// TODO: better check whether a touch sequence is in progress
|
2017-03-19 10:40:03 +00:00
|
|
|
if (m_touchInProgress || waylandServer()->seat()->isTouchSequence()) {
|
|
|
|
// cancel existing touch
|
|
|
|
ScreenEdges::self()->gestureRecognizer()->cancelSwipeGesture();
|
|
|
|
m_touchInProgress = false;
|
|
|
|
m_id = 0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (ScreenEdges::self()->gestureRecognizer()->startSwipeGesture(pos) > 0) {
|
|
|
|
m_touchInProgress = true;
|
|
|
|
m_id = id;
|
|
|
|
m_lastPos = pos;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchMotion(qint32 id, const QPointF &pos, quint32 time) override {
|
2017-03-19 10:40:03 +00:00
|
|
|
Q_UNUSED(time)
|
|
|
|
if (m_touchInProgress && m_id == id) {
|
|
|
|
ScreenEdges::self()->gestureRecognizer()->updateSwipeGesture(QSizeF(pos.x() - m_lastPos.x(), pos.y() - m_lastPos.y()));
|
|
|
|
m_lastPos = pos;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchUp(qint32 id, quint32 time) override {
|
2017-03-19 10:40:03 +00:00
|
|
|
Q_UNUSED(time)
|
|
|
|
if (m_touchInProgress && m_id == id) {
|
|
|
|
ScreenEdges::self()->gestureRecognizer()->endSwipeGesture();
|
|
|
|
m_touchInProgress = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
bool m_touchInProgress = false;
|
2019-08-11 19:57:45 +00:00
|
|
|
qint32 m_id = 0;
|
2017-03-19 10:40:03 +00:00
|
|
|
QPointF m_lastPos;
|
2016-02-11 14:51:25 +00:00
|
|
|
};
|
|
|
|
|
2016-02-18 08:57:47 +00:00
|
|
|
/**
|
|
|
|
* This filter implements window actions. If the event should not be passed to the
|
|
|
|
* current pointer window it will filter out the event
|
2019-07-29 18:58:33 +00:00
|
|
|
*/
|
2016-02-18 08:57:47 +00:00
|
|
|
class WindowActionInputFilter : public InputEventFilter
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override {
|
|
|
|
Q_UNUSED(nativeButton)
|
|
|
|
if (event->type() != QEvent::MouseButtonPress) {
|
|
|
|
return false;
|
|
|
|
}
|
2020-09-07 08:11:07 +00:00
|
|
|
AbstractClient *c = dynamic_cast<AbstractClient*>(input()->pointer()->focus());
|
2016-02-18 08:57:47 +00:00
|
|
|
if (!c) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-11-11 11:28:10 +00:00
|
|
|
const auto actionResult = performClientMouseAction(event, c, MouseAction::ModifierAndWindow);
|
|
|
|
if (actionResult.first) {
|
|
|
|
return actionResult.second;
|
2016-02-18 08:57:47 +00:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool wheelEvent(QWheelEvent *event) override {
|
|
|
|
if (event->angleDelta().y() == 0) {
|
|
|
|
// only actions on vertical scroll
|
|
|
|
return false;
|
|
|
|
}
|
2020-09-07 08:11:07 +00:00
|
|
|
AbstractClient *c = dynamic_cast<AbstractClient*>(input()->pointer()->focus());
|
2016-02-18 08:57:47 +00:00
|
|
|
if (!c) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-11-11 11:28:10 +00:00
|
|
|
const auto actionResult = performClientWheelAction(event, c, MouseAction::ModifierAndWindow);
|
|
|
|
if (actionResult.first) {
|
|
|
|
return actionResult.second;
|
2016-02-18 08:57:47 +00:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchDown(qint32 id, const QPointF &pos, quint32 time) override {
|
2016-02-18 08:57:47 +00:00
|
|
|
Q_UNUSED(id)
|
|
|
|
Q_UNUSED(time)
|
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
if (seat->isTouchSequence()) {
|
|
|
|
return false;
|
|
|
|
}
|
2020-09-07 08:11:07 +00:00
|
|
|
AbstractClient *c = dynamic_cast<AbstractClient*>(input()->touch()->focus());
|
2016-02-18 08:57:47 +00:00
|
|
|
if (!c) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool wasAction = false;
|
|
|
|
const Options::MouseCommand command = c->getMouseCommand(Qt::LeftButton, &wasAction);
|
|
|
|
if (wasAction) {
|
|
|
|
return !c->performMouseCommand(command, pos.toPoint());
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2021-11-09 01:49:01 +00:00
|
|
|
bool tabletToolEvent(TabletEvent *event) override {
|
|
|
|
if (event->type() != QEvent::TabletPress) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
AbstractClient *c = dynamic_cast<AbstractClient*>(input()->tablet()->focus());
|
|
|
|
if (!c) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool wasAction = false;
|
|
|
|
const Options::MouseCommand command = c->getMouseCommand(Qt::LeftButton, &wasAction);
|
|
|
|
if (wasAction) {
|
|
|
|
return !c->performMouseCommand(command, event->globalPos());
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2016-02-18 08:57:47 +00:00
|
|
|
};
|
|
|
|
|
2021-12-13 17:23:23 +00:00
|
|
|
class InputKeyboardFilter : public InputEventFilter
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
bool keyEvent(QKeyEvent *event) override
|
|
|
|
{
|
2022-01-04 03:38:25 +00:00
|
|
|
return passToInputMethod(event);
|
2021-12-13 17:23:23 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-02-03 14:17:49 +00:00
|
|
|
/**
|
|
|
|
* The remaining default input filter which forwards events to other windows
|
2019-07-29 18:58:33 +00:00
|
|
|
*/
|
2016-02-03 14:17:49 +00:00
|
|
|
class ForwardInputFilter : public InputEventFilter
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override {
|
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
seat->setTimestamp(event->timestamp());
|
|
|
|
switch (event->type()) {
|
2016-10-07 12:47:25 +00:00
|
|
|
case QEvent::MouseMove: {
|
2021-03-11 08:44:03 +00:00
|
|
|
seat->notifyPointerMotion(event->globalPos());
|
2016-10-07 12:47:25 +00:00
|
|
|
MouseEvent *e = static_cast<MouseEvent*>(event);
|
|
|
|
if (e->delta() != QSizeF()) {
|
|
|
|
seat->relativePointerMotion(e->delta(), e->deltaUnaccelerated(), e->timestampMicroseconds());
|
|
|
|
}
|
2021-03-11 08:44:03 +00:00
|
|
|
seat->notifyPointerFrame();
|
2016-02-03 14:17:49 +00:00
|
|
|
break;
|
2016-10-07 12:47:25 +00:00
|
|
|
}
|
2016-02-18 08:57:47 +00:00
|
|
|
case QEvent::MouseButtonPress:
|
2021-03-15 18:10:34 +00:00
|
|
|
seat->notifyPointerButton(nativeButton, KWaylandServer::PointerButtonState::Pressed);
|
2021-03-11 08:44:03 +00:00
|
|
|
seat->notifyPointerFrame();
|
2016-02-03 14:17:49 +00:00
|
|
|
break;
|
|
|
|
case QEvent::MouseButtonRelease:
|
2021-03-15 18:10:34 +00:00
|
|
|
seat->notifyPointerButton(nativeButton, KWaylandServer::PointerButtonState::Released);
|
2021-03-11 08:44:03 +00:00
|
|
|
seat->notifyPointerFrame();
|
2016-02-03 14:17:49 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool wheelEvent(QWheelEvent *event) override {
|
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
seat->setTimestamp(event->timestamp());
|
Send axis_source, axis_discrete, and axis_stop
Summary:
So far KWin didn't send axis_source, axis_discrete, and axis_stop. Even
though most of those events are optional, clients need them to work as
expected. For example, one needs axis_source and axis_stop to implement
kinetic scrolling; Xwayland needs axis_discrete to prevent multiple
scroll events when the compositor sends axis deltas greater than 10, etc.
BUG: 404152
FIXED-IN: 5.17.0
Test Plan:
* Content of a webpage in Firefox is moved by one line per each mouse
wheel "click";
* Scrolled gedit using 2 fingers on GNOME Shell, sway, and KDE Plasma;
in all three cases wayland debug looked the same (except diagonal scroll
motions).
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D19000
2019-02-12 09:14:51 +00:00
|
|
|
auto _event = static_cast<WheelEvent *>(event);
|
2021-03-11 08:44:03 +00:00
|
|
|
seat->notifyPointerAxis(_event->orientation(), _event->delta(), _event->discreteDelta(),
|
|
|
|
kwinAxisSourceToKWaylandAxisSource(_event->axisSource()));
|
|
|
|
seat->notifyPointerFrame();
|
2016-02-03 14:17:49 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool keyEvent(QKeyEvent *event) override {
|
2016-02-19 06:53:20 +00:00
|
|
|
if (event->isAutoRepeat()) {
|
|
|
|
// handled by Wayland client
|
|
|
|
return false;
|
|
|
|
}
|
2016-02-03 14:17:49 +00:00
|
|
|
auto seat = waylandServer()->seat();
|
2016-02-15 12:42:48 +00:00
|
|
|
input()->keyboard()->update();
|
2016-02-03 14:17:49 +00:00
|
|
|
seat->setTimestamp(event->timestamp());
|
2016-09-15 06:47:01 +00:00
|
|
|
passToWaylandServer(event);
|
2016-02-03 14:17:49 +00:00
|
|
|
return true;
|
|
|
|
}
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchDown(qint32 id, const QPointF &pos, quint32 time) override {
|
2016-02-03 14:17:49 +00:00
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
seat->setTimestamp(time);
|
2021-03-11 08:44:03 +00:00
|
|
|
seat->notifyTouchDown(id, pos);
|
2016-02-03 14:17:49 +00:00
|
|
|
return true;
|
|
|
|
}
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchMotion(qint32 id, const QPointF &pos, quint32 time) override {
|
2016-02-03 14:17:49 +00:00
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
seat->setTimestamp(time);
|
2021-03-11 08:44:03 +00:00
|
|
|
seat->notifyTouchMotion(id, pos);
|
2016-02-03 14:17:49 +00:00
|
|
|
return true;
|
|
|
|
}
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchUp(qint32 id, quint32 time) override {
|
2016-02-03 14:17:49 +00:00
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
seat->setTimestamp(time);
|
2021-03-11 08:44:03 +00:00
|
|
|
seat->notifyTouchUp(id);
|
2016-02-03 14:17:49 +00:00
|
|
|
return true;
|
|
|
|
}
|
2016-10-27 07:10:08 +00:00
|
|
|
bool pinchGestureBegin(int fingerCount, quint32 time) override {
|
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
seat->setTimestamp(time);
|
|
|
|
seat->startPointerPinchGesture(fingerCount);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool pinchGestureUpdate(qreal scale, qreal angleDelta, const QSizeF &delta, quint32 time) override {
|
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
seat->setTimestamp(time);
|
|
|
|
seat->updatePointerPinchGesture(delta, scale, angleDelta);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool pinchGestureEnd(quint32 time) override {
|
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
seat->setTimestamp(time);
|
|
|
|
seat->endPointerPinchGesture();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool pinchGestureCancelled(quint32 time) override {
|
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
seat->setTimestamp(time);
|
|
|
|
seat->cancelPointerPinchGesture();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool swipeGestureBegin(int fingerCount, quint32 time) override {
|
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
seat->setTimestamp(time);
|
|
|
|
seat->startPointerSwipeGesture(fingerCount);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool swipeGestureUpdate(const QSizeF &delta, quint32 time) override {
|
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
seat->setTimestamp(time);
|
|
|
|
seat->updatePointerSwipeGesture(delta);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool swipeGestureEnd(quint32 time) override {
|
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
seat->setTimestamp(time);
|
|
|
|
seat->endPointerSwipeGesture();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool swipeGestureCancelled(quint32 time) override {
|
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
seat->setTimestamp(time);
|
|
|
|
seat->cancelPointerSwipeGesture();
|
|
|
|
return true;
|
|
|
|
}
|
2021-08-05 08:31:08 +00:00
|
|
|
bool holdGestureBegin(int fingerCount, quint32 time) override
|
|
|
|
{
|
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
seat->setTimestamp(time);
|
2021-11-12 12:15:13 +00:00
|
|
|
seat->startPointerHoldGesture(fingerCount);
|
2021-08-05 08:31:08 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool holdGestureEnd(quint32 time) override
|
|
|
|
{
|
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
seat->setTimestamp(time);
|
|
|
|
seat->endPointerHoldGesture();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool holdGestureCancelled(quint32 time) override
|
|
|
|
{
|
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
seat->setTimestamp(time);
|
|
|
|
seat->cancelPointerHoldGesture();
|
|
|
|
return true;
|
|
|
|
}
|
2016-02-03 14:17:49 +00:00
|
|
|
};
|
|
|
|
|
2020-04-29 15:18:41 +00:00
|
|
|
static KWaylandServer::SeatInterface *findSeat()
|
2020-03-17 14:21:35 +00:00
|
|
|
{
|
|
|
|
auto server = waylandServer();
|
|
|
|
if (!server) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return server->seat();
|
|
|
|
}
|
|
|
|
|
2021-07-02 22:00:24 +00:00
|
|
|
class SurfaceCursor : public Cursor
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
explicit SurfaceCursor(QObject *parent) : Cursor(parent)
|
|
|
|
{}
|
|
|
|
|
|
|
|
void updateCursorSurface(KWaylandServer::SurfaceInterface *surface, const QPoint &hotspot) {
|
|
|
|
if (m_surface == surface && hotspot == m_hotspot) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_surface) {
|
|
|
|
disconnect(m_surface, nullptr, this, nullptr);
|
|
|
|
}
|
|
|
|
m_surface = surface;
|
2021-12-09 15:58:03 +00:00
|
|
|
m_hotspot = hotspot;
|
2021-07-02 22:00:24 +00:00
|
|
|
connect(m_surface, &KWaylandServer::SurfaceInterface::committed, this, &SurfaceCursor::refresh);
|
|
|
|
|
|
|
|
refresh();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void refresh()
|
|
|
|
{
|
2021-07-20 19:37:03 +00:00
|
|
|
auto buffer = qobject_cast<KWaylandServer::ShmClientBuffer *>(m_surface->buffer());
|
2021-07-02 22:00:24 +00:00
|
|
|
if (!buffer) {
|
|
|
|
updateCursor({}, {});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QImage cursorImage;
|
|
|
|
cursorImage = buffer->data().copy();
|
|
|
|
cursorImage.setDevicePixelRatio(m_surface->bufferScale());
|
|
|
|
updateCursor(cursorImage, m_hotspot);
|
|
|
|
}
|
|
|
|
|
|
|
|
QPointer<KWaylandServer::SurfaceInterface> m_surface;
|
|
|
|
QPoint m_hotspot;
|
|
|
|
};
|
|
|
|
|
2019-12-05 14:34:23 +00:00
|
|
|
/**
|
2020-07-03 15:44:13 +00:00
|
|
|
* Handles input coming from a tablet device (e.g. wacom) often with a pen
|
2019-12-05 14:34:23 +00:00
|
|
|
*/
|
2020-03-17 14:21:35 +00:00
|
|
|
class TabletInputFilter : public QObject, public InputEventFilter
|
2019-12-05 14:34:23 +00:00
|
|
|
{
|
|
|
|
public:
|
2020-03-17 14:21:35 +00:00
|
|
|
TabletInputFilter()
|
|
|
|
{
|
2021-10-02 08:26:51 +00:00
|
|
|
const auto devices = input()->devices();
|
|
|
|
for (InputDevice *device : devices) {
|
|
|
|
integrateDevice(device);
|
|
|
|
}
|
|
|
|
connect(input(), &InputRedirection::deviceAdded, this, &TabletInputFilter::integrateDevice);
|
|
|
|
connect(input(), &InputRedirection::deviceRemoved, this, &TabletInputFilter::removeDevice);
|
2020-03-17 14:21:35 +00:00
|
|
|
}
|
|
|
|
|
2020-11-04 17:17:04 +00:00
|
|
|
static KWaylandServer::TabletSeatV2Interface *findTabletSeat()
|
2020-03-17 14:21:35 +00:00
|
|
|
{
|
|
|
|
auto server = waylandServer();
|
|
|
|
if (!server) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2020-11-04 17:17:04 +00:00
|
|
|
KWaylandServer::TabletManagerV2Interface *manager = server->tabletManagerV2();
|
2020-03-17 14:21:35 +00:00
|
|
|
return manager->seat(findSeat());
|
|
|
|
}
|
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
void integrateDevice(InputDevice *inputDevice)
|
2020-03-17 14:21:35 +00:00
|
|
|
{
|
2021-10-02 08:26:51 +00:00
|
|
|
auto device = qobject_cast<LibInput::Device *>(inputDevice);
|
|
|
|
if (!device || (!device->isTabletTool() && !device->isTabletPad())) {
|
2020-12-22 16:34:10 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-11-20 01:28:05 +00:00
|
|
|
|
2020-12-22 16:34:10 +00:00
|
|
|
KWaylandServer::TabletSeatV2Interface *tabletSeat = findTabletSeat();
|
|
|
|
if (!tabletSeat) {
|
|
|
|
qCCritical(KWIN_CORE) << "Could not find tablet seat";
|
|
|
|
return;
|
2020-03-17 14:21:35 +00:00
|
|
|
}
|
2020-12-22 16:34:10 +00:00
|
|
|
struct udev_device *const udev_device = libinput_device_get_udev_device(device->device());
|
|
|
|
const char *devnode = udev_device_get_syspath(udev_device);
|
2020-11-20 01:28:05 +00:00
|
|
|
|
2020-12-22 16:34:10 +00:00
|
|
|
auto deviceGroup = libinput_device_get_device_group(device->device());
|
|
|
|
auto tablet = static_cast<KWaylandServer::TabletV2Interface *>(libinput_device_group_get_user_data(deviceGroup));
|
|
|
|
if (!tablet) {
|
|
|
|
tablet = tabletSeat->addTablet(device->vendor(), device->product(), device->sysName(), device->name(), {QString::fromUtf8(devnode)});
|
|
|
|
libinput_device_group_set_user_data(deviceGroup, tablet);
|
|
|
|
}
|
2020-11-20 01:28:05 +00:00
|
|
|
|
2020-12-22 16:34:10 +00:00
|
|
|
if (device->isTabletPad()) {
|
2020-11-20 01:28:05 +00:00
|
|
|
const int buttonsCount = libinput_device_tablet_pad_get_num_buttons(device->device());
|
|
|
|
const int ringsCount = libinput_device_tablet_pad_get_num_rings(device->device());
|
|
|
|
const int stripsCount = libinput_device_tablet_pad_get_num_strips(device->device());
|
|
|
|
const int modes = libinput_device_tablet_pad_get_num_mode_groups(device->device());
|
|
|
|
|
|
|
|
auto firstGroup = libinput_device_tablet_pad_get_mode_group(device->device(), 0);
|
2020-12-22 16:34:10 +00:00
|
|
|
tabletSeat->addTabletPad(device->sysName(), device->name(), {QString::fromUtf8(devnode)}, buttonsCount, ringsCount, stripsCount, modes, libinput_tablet_pad_mode_group_get_mode(firstGroup), tablet);
|
2020-11-20 01:28:05 +00:00
|
|
|
}
|
2020-03-17 14:21:35 +00:00
|
|
|
}
|
2021-04-28 14:20:55 +00:00
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
void removeDevice(InputDevice *inputDevice)
|
2019-12-05 14:34:23 +00:00
|
|
|
{
|
2021-10-02 08:26:51 +00:00
|
|
|
auto device = qobject_cast<LibInput::Device *>(inputDevice);
|
|
|
|
if (device) {
|
|
|
|
auto deviceGroup = libinput_device_get_device_group(device->device());
|
|
|
|
libinput_device_group_set_user_data(deviceGroup, nullptr);
|
|
|
|
|
|
|
|
KWaylandServer::TabletSeatV2Interface *tabletSeat = findTabletSeat();
|
|
|
|
if (tabletSeat) {
|
|
|
|
tabletSeat->removeDevice(device->sysName());
|
|
|
|
} else {
|
|
|
|
qCCritical(KWIN_CORE) << "Could not find tablet to remove" << device->sysName();
|
|
|
|
}
|
|
|
|
}
|
2019-12-05 14:34:23 +00:00
|
|
|
}
|
|
|
|
|
2021-03-14 22:58:17 +00:00
|
|
|
KWaylandServer::TabletToolV2Interface::Type getType(const KWin::TabletToolId &tabletToolId) {
|
|
|
|
using Type = KWaylandServer::TabletToolV2Interface::Type;
|
|
|
|
switch (tabletToolId.m_toolType) {
|
|
|
|
case InputRedirection::Pen:
|
|
|
|
return Type::Pen;
|
|
|
|
case InputRedirection::Eraser:
|
|
|
|
return Type::Eraser;
|
|
|
|
case InputRedirection::Brush:
|
|
|
|
return Type::Brush;
|
|
|
|
case InputRedirection::Pencil:
|
|
|
|
return Type::Pencil;
|
|
|
|
case InputRedirection::Airbrush:
|
|
|
|
return Type::Airbrush;
|
|
|
|
case InputRedirection::Finger:
|
|
|
|
return Type::Finger;
|
|
|
|
case InputRedirection::Mouse:
|
|
|
|
return Type::Mouse;
|
|
|
|
case InputRedirection::Lens:
|
|
|
|
return Type::Lens;
|
|
|
|
case InputRedirection::Totem:
|
|
|
|
return Type::Totem;
|
|
|
|
}
|
|
|
|
return Type::Pen;
|
|
|
|
}
|
|
|
|
|
2020-11-20 01:28:05 +00:00
|
|
|
KWaylandServer::TabletToolV2Interface *createTool(const KWin::TabletToolId &tabletToolId)
|
|
|
|
{
|
|
|
|
using namespace KWaylandServer;
|
|
|
|
KWaylandServer::TabletSeatV2Interface *tabletSeat = findTabletSeat();
|
|
|
|
|
|
|
|
const auto f = [](InputRedirection::Capability cap) {
|
|
|
|
switch (cap) {
|
|
|
|
case InputRedirection::Tilt:
|
|
|
|
return TabletToolV2Interface::Tilt;
|
|
|
|
case InputRedirection::Pressure:
|
|
|
|
return TabletToolV2Interface::Pressure;
|
|
|
|
case InputRedirection::Distance:
|
|
|
|
return TabletToolV2Interface::Distance;
|
|
|
|
case InputRedirection::Rotation:
|
|
|
|
return TabletToolV2Interface::Rotation;
|
|
|
|
case InputRedirection::Slider:
|
|
|
|
return TabletToolV2Interface::Slider;
|
|
|
|
case InputRedirection::Wheel:
|
|
|
|
return TabletToolV2Interface::Wheel;
|
|
|
|
}
|
|
|
|
return TabletToolV2Interface::Wheel;
|
|
|
|
};
|
|
|
|
QVector<TabletToolV2Interface::Capability> ifaceCapabilities;
|
|
|
|
ifaceCapabilities.resize(tabletToolId.m_capabilities.size());
|
|
|
|
std::transform(tabletToolId.m_capabilities.constBegin(), tabletToolId.m_capabilities.constEnd(), ifaceCapabilities.begin(), f);
|
|
|
|
|
2021-03-14 22:58:17 +00:00
|
|
|
TabletToolV2Interface *tool = tabletSeat->addTool(getType(tabletToolId), tabletToolId.m_serialId, tabletToolId.m_uniqueId, ifaceCapabilities);
|
2020-11-20 01:28:05 +00:00
|
|
|
|
2021-07-02 22:00:24 +00:00
|
|
|
const auto cursor = new SurfaceCursor(tool);
|
2020-11-20 01:28:05 +00:00
|
|
|
Cursors::self()->addCursor(cursor);
|
|
|
|
m_cursorByTool[tool] = cursor;
|
|
|
|
|
|
|
|
connect(tool, &TabletToolV2Interface::cursorChanged, cursor, [cursor] (TabletCursorV2 *tcursor) {
|
|
|
|
static const auto createDefaultCursor = [] {
|
|
|
|
WaylandCursorImage defaultCursor;
|
|
|
|
WaylandCursorImage::Image ret;
|
|
|
|
defaultCursor.loadThemeCursor(CursorShape(Qt::CrossCursor), &ret);
|
|
|
|
return ret;
|
|
|
|
};
|
2021-04-15 00:35:54 +00:00
|
|
|
if (!tcursor || tcursor->enteredSerial() == 0) {
|
2021-07-02 18:54:57 +00:00
|
|
|
static const auto defaultCursor = createDefaultCursor();
|
2020-11-20 01:28:05 +00:00
|
|
|
cursor->updateCursor(defaultCursor.image, defaultCursor.hotspot);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto cursorSurface = tcursor->surface();
|
|
|
|
if (!cursorSurface) {
|
2020-12-18 12:09:35 +00:00
|
|
|
cursor->updateCursor({}, {});
|
2020-11-20 01:28:05 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-07-02 22:00:24 +00:00
|
|
|
cursor->updateCursorSurface(cursorSurface, tcursor->hotspot());
|
2020-11-20 01:28:05 +00:00
|
|
|
});
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT cursor->cursorChanged();
|
2020-11-20 01:28:05 +00:00
|
|
|
return tool;
|
|
|
|
}
|
|
|
|
|
2020-03-17 14:21:35 +00:00
|
|
|
bool tabletToolEvent(TabletEvent *event) override
|
|
|
|
{
|
|
|
|
if (!workspace()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-11-04 17:17:04 +00:00
|
|
|
KWaylandServer::TabletSeatV2Interface *tabletSeat = findTabletSeat();
|
2020-07-26 16:03:08 +00:00
|
|
|
if (!tabletSeat) {
|
|
|
|
qCCritical(KWIN_CORE) << "Could not find tablet manager";
|
|
|
|
return false;
|
|
|
|
}
|
2021-03-14 22:58:17 +00:00
|
|
|
auto tool = tabletSeat->toolByHardwareSerial(event->tabletId().m_serialId, getType(event->tabletId()));
|
2020-03-17 14:21:35 +00:00
|
|
|
if (!tool) {
|
2020-11-20 01:28:05 +00:00
|
|
|
tool = createTool(event->tabletId());
|
2020-03-17 14:21:35 +00:00
|
|
|
}
|
|
|
|
|
2021-09-17 13:01:30 +00:00
|
|
|
// NOTE: tablet will be nullptr as the device is removed (see ::removeDevice) but events from the tool
|
|
|
|
// may still happen (e.g. Release or ProximityOut events)
|
2020-12-22 16:34:10 +00:00
|
|
|
auto tablet = static_cast<KWaylandServer::TabletV2Interface *>(event->tabletId().m_deviceGroupData);
|
2020-03-17 14:21:35 +00:00
|
|
|
|
|
|
|
Toplevel *toplevel = input()->findToplevel(event->globalPos());
|
|
|
|
if (!toplevel || !toplevel->surface()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-04-29 15:18:41 +00:00
|
|
|
KWaylandServer::SurfaceInterface *surface = toplevel->surface();
|
2020-03-17 14:21:35 +00:00
|
|
|
tool->setCurrentSurface(surface);
|
|
|
|
|
2021-09-17 13:01:30 +00:00
|
|
|
if (!tool->isClientSupported() || (tablet && !tablet->isSurfaceSupported(surface))) {
|
2020-03-17 14:21:35 +00:00
|
|
|
return emulateTabletEvent(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (event->type()) {
|
|
|
|
case QEvent::TabletMove: {
|
2020-10-17 12:40:51 +00:00
|
|
|
const auto pos = toplevel->mapToLocal(event->globalPosF());
|
2020-03-17 14:21:35 +00:00
|
|
|
tool->sendMotion(pos);
|
2020-04-02 16:18:01 +00:00
|
|
|
m_cursorByTool[tool]->setPos(event->globalPos());
|
2020-03-17 14:21:35 +00:00
|
|
|
break;
|
|
|
|
} case QEvent::TabletEnterProximity: {
|
2021-11-29 16:12:58 +00:00
|
|
|
const QPoint pos = event->globalPos();
|
|
|
|
m_cursorByTool[tool]->setPos(pos);
|
2020-03-17 14:21:35 +00:00
|
|
|
tool->sendProximityIn(tablet);
|
2021-11-29 16:12:58 +00:00
|
|
|
tool->sendMotion(toplevel->mapToLocal(event->globalPosF()));
|
2020-03-17 14:21:35 +00:00
|
|
|
break;
|
|
|
|
} case QEvent::TabletLeaveProximity:
|
|
|
|
tool->sendProximityOut();
|
|
|
|
break;
|
2020-08-11 12:13:53 +00:00
|
|
|
case QEvent::TabletPress: {
|
2020-10-17 12:40:51 +00:00
|
|
|
const auto pos = toplevel->mapToLocal(event->globalPosF());
|
2020-08-11 12:13:53 +00:00
|
|
|
tool->sendMotion(pos);
|
|
|
|
m_cursorByTool[tool]->setPos(event->globalPos());
|
2020-03-17 14:21:35 +00:00
|
|
|
tool->sendDown();
|
|
|
|
break;
|
2020-08-11 12:13:53 +00:00
|
|
|
}
|
2020-03-17 14:21:35 +00:00
|
|
|
case QEvent::TabletRelease:
|
|
|
|
tool->sendUp();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
qCWarning(KWIN_CORE) << "Unexpected tablet event type" << event;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
const quint32 MAX_VAL = 65535;
|
|
|
|
tool->sendPressure(MAX_VAL * event->pressure());
|
|
|
|
tool->sendFrame(event->timestamp());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool emulateTabletEvent(TabletEvent *event)
|
2019-12-05 14:34:23 +00:00
|
|
|
{
|
2021-03-12 12:13:20 +00:00
|
|
|
if (!workspace()) {
|
2019-12-05 14:34:23 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (event->type()) {
|
|
|
|
case QEvent::TabletMove:
|
|
|
|
case QEvent::TabletEnterProximity:
|
2021-10-05 19:01:16 +00:00
|
|
|
input()->pointer()->processMotionAbsolute(event->globalPosF(), event->timestamp());
|
2019-12-05 14:34:23 +00:00
|
|
|
break;
|
|
|
|
case QEvent::TabletPress:
|
|
|
|
input()->pointer()->processButton(KWin::qtMouseButtonToButton(Qt::LeftButton),
|
|
|
|
InputRedirection::PointerButtonPressed, event->timestamp());
|
|
|
|
break;
|
|
|
|
case QEvent::TabletRelease:
|
|
|
|
input()->pointer()->processButton(KWin::qtMouseButtonToButton(Qt::LeftButton),
|
|
|
|
InputRedirection::PointerButtonReleased, event->timestamp());
|
|
|
|
break;
|
|
|
|
case QEvent::TabletLeaveProximity:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
qCWarning(KWIN_CORE) << "Unexpected tablet event type" << event;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2020-11-20 01:28:05 +00:00
|
|
|
|
|
|
|
bool tabletToolButtonEvent(uint button, bool pressed, const TabletToolId &tabletToolId) override
|
|
|
|
{
|
|
|
|
KWaylandServer::TabletSeatV2Interface *tabletSeat = findTabletSeat();
|
2021-03-14 22:58:17 +00:00
|
|
|
auto tool = tabletSeat->toolByHardwareSerial(tabletToolId.m_serialId, getType(tabletToolId));
|
2020-11-20 01:28:05 +00:00
|
|
|
if (!tool) {
|
|
|
|
tool = createTool(tabletToolId);
|
|
|
|
}
|
2021-09-24 21:19:55 +00:00
|
|
|
if (!tool->isClientSupported()) {
|
|
|
|
return false;
|
|
|
|
}
|
2020-11-20 01:28:05 +00:00
|
|
|
tool->sendButton(button, pressed);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-12-22 16:34:10 +00:00
|
|
|
KWaylandServer::TabletPadV2Interface *findAndAdoptPad(const TabletPadId &tabletPadId) const
|
2020-11-20 01:28:05 +00:00
|
|
|
{
|
|
|
|
Toplevel *toplevel = workspace()->activeClient();
|
|
|
|
auto seat = findTabletSeat();
|
|
|
|
if (!toplevel || !toplevel->surface() || !seat->isClientSupported(toplevel->surface()->client())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2020-12-22 16:34:10 +00:00
|
|
|
auto tablet = static_cast<KWaylandServer::TabletV2Interface *>(tabletPadId.data);
|
2020-11-20 01:28:05 +00:00
|
|
|
KWaylandServer::SurfaceInterface *surface = toplevel->surface();
|
2020-12-22 16:34:10 +00:00
|
|
|
auto pad = tablet->pad();
|
|
|
|
if (!pad) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
pad->setCurrentSurface(surface, tablet);
|
2020-11-20 01:28:05 +00:00
|
|
|
return pad;
|
|
|
|
}
|
|
|
|
|
2020-12-22 16:34:10 +00:00
|
|
|
bool tabletPadButtonEvent(uint button, bool pressed, const TabletPadId &tabletPadId) override
|
2020-11-20 01:28:05 +00:00
|
|
|
{
|
2020-12-22 16:34:10 +00:00
|
|
|
auto pad = findAndAdoptPad(tabletPadId);
|
2020-11-20 01:28:05 +00:00
|
|
|
if (!pad) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
pad->sendButton(QDateTime::currentMSecsSinceEpoch(), button, pressed);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-12-22 16:34:10 +00:00
|
|
|
bool tabletPadRingEvent(int number, int angle, bool isFinger, const TabletPadId &tabletPadId) override
|
2020-11-20 01:28:05 +00:00
|
|
|
{
|
2020-12-22 16:34:10 +00:00
|
|
|
auto pad = findAndAdoptPad(tabletPadId);
|
2020-11-20 01:28:05 +00:00
|
|
|
if (!pad) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
auto ring = pad->ring(number);
|
|
|
|
|
|
|
|
ring->sendAngle(angle);
|
|
|
|
if (isFinger) {
|
|
|
|
ring->sendSource(KWaylandServer::TabletPadRingV2Interface::SourceFinger);
|
|
|
|
}
|
|
|
|
ring->sendFrame(QDateTime::currentMSecsSinceEpoch());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-12-22 16:34:10 +00:00
|
|
|
bool tabletPadStripEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId) override
|
2020-11-20 01:28:05 +00:00
|
|
|
{
|
2020-12-22 16:34:10 +00:00
|
|
|
auto pad = findAndAdoptPad(tabletPadId);
|
2020-11-20 01:28:05 +00:00
|
|
|
if (!pad) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
auto strip = pad->strip(number);
|
|
|
|
|
|
|
|
strip->sendPosition(position);
|
|
|
|
if (isFinger) {
|
|
|
|
strip->sendSource(KWaylandServer::TabletPadStripV2Interface::SourceFinger);
|
|
|
|
}
|
|
|
|
strip->sendFrame(QDateTime::currentMSecsSinceEpoch());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-11-04 17:17:04 +00:00
|
|
|
QHash<KWaylandServer::TabletToolV2Interface*, Cursor*> m_cursorByTool;
|
2019-12-05 14:34:23 +00:00
|
|
|
};
|
|
|
|
|
2021-09-03 11:47:57 +00:00
|
|
|
static KWaylandServer::AbstractDropHandler *dropHandler(Toplevel *toplevel)
|
|
|
|
{
|
|
|
|
auto surface = toplevel->surface();
|
|
|
|
if (!surface) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
auto dropTarget = seat->dropHandlerForSurface(surface);
|
|
|
|
if (dropTarget) {return dropTarget;}
|
|
|
|
|
|
|
|
if (qobject_cast<X11Client*>(toplevel) && xwayland()) {
|
|
|
|
return xwayland()->xwlDropHandler();
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2021-02-19 11:32:35 +00:00
|
|
|
class DragAndDropInputFilter : public QObject, public InputEventFilter
|
2016-03-01 07:42:07 +00:00
|
|
|
{
|
2021-02-19 11:32:35 +00:00
|
|
|
Q_OBJECT
|
2016-03-01 07:42:07 +00:00
|
|
|
public:
|
2021-02-19 11:32:35 +00:00
|
|
|
DragAndDropInputFilter()
|
|
|
|
{
|
|
|
|
m_raiseTimer.setSingleShot(true);
|
|
|
|
m_raiseTimer.setInterval(250);
|
|
|
|
connect(&m_raiseTimer, &QTimer::timeout, this, &DragAndDropInputFilter::raiseDragTarget);
|
|
|
|
}
|
|
|
|
|
2016-03-01 07:42:07 +00:00
|
|
|
bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override {
|
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
if (!seat->isDragPointer()) {
|
|
|
|
return false;
|
|
|
|
}
|
2018-09-18 14:46:27 +00:00
|
|
|
if (seat->isDragTouch()) {
|
|
|
|
return true;
|
|
|
|
}
|
2016-03-01 07:42:07 +00:00
|
|
|
seat->setTimestamp(event->timestamp());
|
|
|
|
switch (event->type()) {
|
|
|
|
case QEvent::MouseMove: {
|
2018-09-14 10:54:40 +00:00
|
|
|
const auto pos = input()->globalPointer();
|
2021-03-11 08:44:03 +00:00
|
|
|
seat->notifyPointerMotion(pos);
|
|
|
|
seat->notifyPointerFrame();
|
2018-08-22 12:56:48 +00:00
|
|
|
|
|
|
|
const auto eventPos = event->globalPos();
|
|
|
|
// TODO: use InputDeviceHandler::at() here and check isClient()?
|
|
|
|
Toplevel *t = input()->findManagedToplevel(eventPos);
|
2021-08-29 15:08:43 +00:00
|
|
|
const auto dragTarget = qobject_cast<AbstractClient*>(t);
|
|
|
|
if (dragTarget) {
|
|
|
|
if (dragTarget != m_dragTarget) {
|
|
|
|
workspace()->takeActivity(dragTarget, Workspace::ActivityFlag::ActivityFocus);
|
|
|
|
m_raiseTimer.start();
|
|
|
|
}
|
|
|
|
if ((pos - m_lastPos).manhattanLength() > 10) {
|
|
|
|
m_lastPos = pos;
|
|
|
|
// reset timer to delay raising the window
|
|
|
|
m_raiseTimer.start();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_dragTarget = dragTarget;
|
|
|
|
|
2018-08-22 12:56:48 +00:00
|
|
|
if (auto *xwl = xwayland()) {
|
|
|
|
const auto ret = xwl->dragMoveFilter(t, eventPos);
|
|
|
|
if (ret == Xwl::DragEventReply::Ignore) {
|
|
|
|
return false;
|
|
|
|
} else if (ret == Xwl::DragEventReply::Take) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (t) {
|
2016-03-01 07:42:07 +00:00
|
|
|
// TODO: consider decorations
|
|
|
|
if (t->surface() != seat->dragSurface()) {
|
2021-09-03 11:47:57 +00:00
|
|
|
seat->setDragTarget(dropHandler(t), t->surface(), t->inputTransformation());
|
2016-03-01 07:42:07 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// no window at that place, if we have a surface we need to reset
|
2021-09-03 11:47:57 +00:00
|
|
|
seat->setDragTarget(nullptr, nullptr);
|
2021-02-19 11:32:35 +00:00
|
|
|
m_dragTarget = nullptr;
|
2016-03-01 07:42:07 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QEvent::MouseButtonPress:
|
2021-03-15 18:10:34 +00:00
|
|
|
seat->notifyPointerButton(nativeButton, KWaylandServer::PointerButtonState::Pressed);
|
2021-03-11 08:44:03 +00:00
|
|
|
seat->notifyPointerFrame();
|
2016-03-01 07:42:07 +00:00
|
|
|
break;
|
|
|
|
case QEvent::MouseButtonRelease:
|
2021-02-19 11:32:35 +00:00
|
|
|
raiseDragTarget();
|
2021-08-29 15:08:43 +00:00
|
|
|
m_dragTarget = nullptr;
|
2021-03-15 18:10:34 +00:00
|
|
|
seat->notifyPointerButton(nativeButton, KWaylandServer::PointerButtonState::Released);
|
2021-03-11 08:44:03 +00:00
|
|
|
seat->notifyPointerFrame();
|
2016-03-01 07:42:07 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// TODO: should we pass through effects?
|
|
|
|
return true;
|
|
|
|
}
|
2018-09-18 14:46:27 +00:00
|
|
|
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchDown(qint32 id, const QPointF &pos, quint32 time) override {
|
2018-09-18 14:46:27 +00:00
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
if (seat->isDragPointer()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (!seat->isDragTouch()) {
|
|
|
|
return false;
|
|
|
|
}
|
2019-01-04 19:30:24 +00:00
|
|
|
if (m_touchId != id) {
|
2018-09-18 14:46:27 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
seat->setTimestamp(time);
|
2021-03-11 08:44:03 +00:00
|
|
|
seat->notifyTouchDown(id, pos);
|
2021-02-19 11:32:35 +00:00
|
|
|
m_lastPos = pos;
|
2018-09-18 14:46:27 +00:00
|
|
|
return true;
|
|
|
|
}
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchMotion(qint32 id, const QPointF &pos, quint32 time) override {
|
2018-09-18 14:46:27 +00:00
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
if (seat->isDragPointer()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (!seat->isDragTouch()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (m_touchId < 0) {
|
|
|
|
// We take for now the first id appearing as a move after a drag
|
|
|
|
// started. We can optimize by specifying the id the drag is
|
|
|
|
// associated with by implementing a key-value getter in KWayland.
|
|
|
|
m_touchId = id;
|
|
|
|
}
|
2019-01-04 19:30:24 +00:00
|
|
|
if (m_touchId != id) {
|
2018-09-18 14:46:27 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
seat->setTimestamp(time);
|
2021-03-11 08:44:03 +00:00
|
|
|
seat->notifyTouchMotion(id, pos);
|
2018-09-18 14:46:27 +00:00
|
|
|
|
|
|
|
if (Toplevel *t = input()->findToplevel(pos.toPoint())) {
|
|
|
|
// TODO: consider decorations
|
|
|
|
if (t->surface() != seat->dragSurface()) {
|
2021-02-19 11:32:35 +00:00
|
|
|
if ((m_dragTarget = qobject_cast<AbstractClient*>(t))) {
|
|
|
|
workspace()->takeActivity(m_dragTarget, Workspace::ActivityFlag::ActivityFocus);
|
|
|
|
m_raiseTimer.start();
|
2018-09-18 14:46:27 +00:00
|
|
|
}
|
2021-09-03 11:47:57 +00:00
|
|
|
seat->setDragTarget(dropHandler(t), t->surface(), pos, t->inputTransformation());
|
2018-09-18 14:46:27 +00:00
|
|
|
}
|
2021-02-19 11:32:35 +00:00
|
|
|
if ((pos - m_lastPos).manhattanLength() > 10) {
|
|
|
|
m_lastPos = pos;
|
|
|
|
// reset timer to delay raising the window
|
|
|
|
m_raiseTimer.start();
|
|
|
|
}
|
2018-09-18 14:46:27 +00:00
|
|
|
} else {
|
|
|
|
// no window at that place, if we have a surface we need to reset
|
2021-09-03 11:47:57 +00:00
|
|
|
seat->setDragTarget(nullptr, nullptr);
|
2021-02-19 11:32:35 +00:00
|
|
|
m_dragTarget = nullptr;
|
2018-09-18 14:46:27 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2019-08-11 19:57:45 +00:00
|
|
|
bool touchUp(qint32 id, quint32 time) override {
|
2018-09-18 14:46:27 +00:00
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
if (!seat->isDragTouch()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
seat->setTimestamp(time);
|
2021-03-11 08:44:03 +00:00
|
|
|
seat->notifyTouchUp(id);
|
2019-01-04 19:30:24 +00:00
|
|
|
if (m_touchId == id) {
|
2018-09-18 14:46:27 +00:00
|
|
|
m_touchId = -1;
|
2021-02-19 11:32:35 +00:00
|
|
|
raiseDragTarget();
|
2018-09-18 14:46:27 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
private:
|
2021-02-19 11:32:35 +00:00
|
|
|
void raiseDragTarget()
|
|
|
|
{
|
|
|
|
m_raiseTimer.stop();
|
|
|
|
if (m_dragTarget) {
|
|
|
|
workspace()->takeActivity(m_dragTarget, Workspace::ActivityFlag::ActivityRaise);
|
|
|
|
}
|
|
|
|
}
|
2018-09-18 14:46:27 +00:00
|
|
|
qint32 m_touchId = -1;
|
2021-02-19 11:32:35 +00:00
|
|
|
QPointF m_lastPos = QPointF(-1, -1);
|
|
|
|
QPointer<AbstractClient> m_dragTarget;
|
|
|
|
QTimer m_raiseTimer;
|
2016-03-01 07:42:07 +00:00
|
|
|
};
|
|
|
|
|
2013-06-26 08:15:20 +00:00
|
|
|
KWIN_SINGLETON_FACTORY(InputRedirection)
|
|
|
|
|
2017-05-06 23:40:48 +00:00
|
|
|
static const QString s_touchpadComponent = QStringLiteral("kcm_touchpad");
|
|
|
|
|
2013-06-26 08:15:20 +00:00
|
|
|
InputRedirection::InputRedirection(QObject *parent)
|
|
|
|
: QObject(parent)
|
2016-02-15 12:42:48 +00:00
|
|
|
, m_keyboard(new KeyboardInputRedirection(this))
|
2016-02-12 12:30:00 +00:00
|
|
|
, m_pointer(new PointerInputRedirection(this))
|
2019-12-01 17:51:15 +00:00
|
|
|
, m_tablet(new TabletInputRedirection(this))
|
2016-02-15 08:36:59 +00:00
|
|
|
, m_touch(new TouchInputRedirection(this))
|
2013-07-10 09:41:16 +00:00
|
|
|
, m_shortcuts(new GlobalShortcutsManager(this))
|
2013-06-26 08:15:20 +00:00
|
|
|
{
|
2015-09-02 08:10:49 +00:00
|
|
|
qRegisterMetaType<KWin::InputRedirection::KeyboardKeyState>();
|
|
|
|
qRegisterMetaType<KWin::InputRedirection::PointerButtonState>();
|
|
|
|
qRegisterMetaType<KWin::InputRedirection::PointerAxis>();
|
2021-10-02 08:26:51 +00:00
|
|
|
setupInputBackends();
|
2015-06-13 15:54:08 +00:00
|
|
|
connect(kwinApp(), &Application::workspaceCreated, this, &InputRedirection::setupWorkspace);
|
2014-08-15 07:30:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
InputRedirection::~InputRedirection()
|
|
|
|
{
|
2021-10-02 08:26:51 +00:00
|
|
|
qDeleteAll(m_inputBackends);
|
|
|
|
m_inputBackends.clear();
|
|
|
|
m_inputDevices.clear();
|
2021-09-27 10:10:37 +00:00
|
|
|
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
s_self = nullptr;
|
2016-02-03 14:17:49 +00:00
|
|
|
qDeleteAll(m_filters);
|
2016-12-27 19:16:50 +00:00
|
|
|
qDeleteAll(m_spies);
|
2016-02-03 14:17:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void InputRedirection::installInputEventFilter(InputEventFilter *filter)
|
|
|
|
{
|
2017-01-02 18:55:03 +00:00
|
|
|
Q_ASSERT(!m_filters.contains(filter));
|
2016-02-03 14:17:49 +00:00
|
|
|
m_filters << filter;
|
|
|
|
}
|
|
|
|
|
2017-01-02 19:13:30 +00:00
|
|
|
void InputRedirection::prependInputEventFilter(InputEventFilter *filter)
|
2016-02-15 14:40:25 +00:00
|
|
|
{
|
2017-01-02 18:55:03 +00:00
|
|
|
Q_ASSERT(!m_filters.contains(filter));
|
2016-02-15 14:40:25 +00:00
|
|
|
m_filters.prepend(filter);
|
|
|
|
}
|
|
|
|
|
2016-02-03 14:17:49 +00:00
|
|
|
void InputRedirection::uninstallInputEventFilter(InputEventFilter *filter)
|
|
|
|
{
|
2017-01-02 18:55:03 +00:00
|
|
|
m_filters.removeOne(filter);
|
2014-08-15 07:30:08 +00:00
|
|
|
}
|
|
|
|
|
2016-12-27 19:16:50 +00:00
|
|
|
void InputRedirection::installInputEventSpy(InputEventSpy *spy)
|
|
|
|
{
|
|
|
|
m_spies << spy;
|
|
|
|
}
|
|
|
|
|
|
|
|
void InputRedirection::uninstallInputEventSpy(InputEventSpy *spy)
|
|
|
|
{
|
|
|
|
m_spies.removeOne(spy);
|
|
|
|
}
|
|
|
|
|
2015-06-26 11:47:08 +00:00
|
|
|
void InputRedirection::init()
|
|
|
|
{
|
2021-10-02 08:26:51 +00:00
|
|
|
m_inputConfigWatcher = KConfigWatcher::create(InputConfig::self()->inputConfig());
|
|
|
|
connect(m_inputConfigWatcher.data(), &KConfigWatcher::configChanged,
|
|
|
|
this, &InputRedirection::handleInputConfigChanged);
|
|
|
|
|
2015-06-26 11:47:08 +00:00
|
|
|
m_shortcuts->init();
|
|
|
|
}
|
|
|
|
|
2015-06-13 15:54:08 +00:00
|
|
|
void InputRedirection::setupWorkspace()
|
|
|
|
{
|
|
|
|
if (waylandServer()) {
|
2016-02-15 12:42:48 +00:00
|
|
|
m_keyboard->init();
|
2016-02-12 12:30:00 +00:00
|
|
|
m_pointer->init();
|
2016-02-15 08:36:59 +00:00
|
|
|
m_touch->init();
|
2019-12-01 17:51:15 +00:00
|
|
|
m_tablet->init();
|
2021-10-02 08:26:51 +00:00
|
|
|
|
|
|
|
updateLeds(m_keyboard->xkb()->leds());
|
|
|
|
connect(m_keyboard, &KeyboardInputRedirection::ledsChanged, this, &InputRedirection::updateLeds);
|
2021-12-01 08:47:03 +00:00
|
|
|
|
|
|
|
setupTouchpadShortcuts();
|
|
|
|
setupInputFilters();
|
2015-06-13 15:54:08 +00:00
|
|
|
}
|
2016-02-03 14:17:49 +00:00
|
|
|
}
|
|
|
|
|
2021-10-27 14:19:26 +00:00
|
|
|
class UserActivitySpy : public InputEventSpy
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
void pointerEvent(MouseEvent *event) override
|
|
|
|
{
|
|
|
|
Q_UNUSED(event)
|
|
|
|
notifyActivity();
|
|
|
|
}
|
|
|
|
void wheelEvent(WheelEvent *event) override
|
|
|
|
{
|
|
|
|
Q_UNUSED(event)
|
|
|
|
notifyActivity();
|
|
|
|
}
|
|
|
|
|
|
|
|
void keyEvent(KeyEvent *event) override
|
|
|
|
{
|
|
|
|
Q_UNUSED(event)
|
|
|
|
notifyActivity();
|
|
|
|
}
|
|
|
|
|
|
|
|
void touchDown(qint32 id, const QPointF &pos, quint32 time) override
|
|
|
|
{
|
|
|
|
Q_UNUSED(id)
|
|
|
|
Q_UNUSED(pos)
|
|
|
|
Q_UNUSED(time)
|
|
|
|
notifyActivity();
|
|
|
|
}
|
|
|
|
void touchMotion(qint32 id, const QPointF &pos, quint32 time) override
|
|
|
|
{
|
|
|
|
Q_UNUSED(id)
|
|
|
|
Q_UNUSED(pos)
|
|
|
|
Q_UNUSED(time)
|
|
|
|
notifyActivity();
|
|
|
|
}
|
|
|
|
void touchUp(qint32 id, quint32 time) override
|
|
|
|
{
|
|
|
|
Q_UNUSED(id)
|
|
|
|
Q_UNUSED(time)
|
|
|
|
notifyActivity();
|
|
|
|
}
|
|
|
|
|
|
|
|
void pinchGestureBegin(int fingerCount, quint32 time) override
|
|
|
|
{
|
|
|
|
Q_UNUSED(fingerCount)
|
|
|
|
Q_UNUSED(time)
|
|
|
|
notifyActivity();
|
|
|
|
}
|
|
|
|
void pinchGestureUpdate(qreal scale, qreal angleDelta, const QSizeF &delta, quint32 time) override
|
|
|
|
{
|
|
|
|
Q_UNUSED(scale)
|
|
|
|
Q_UNUSED(angleDelta)
|
|
|
|
Q_UNUSED(delta)
|
|
|
|
Q_UNUSED(time)
|
|
|
|
notifyActivity();
|
|
|
|
}
|
|
|
|
void pinchGestureEnd(quint32 time) override
|
|
|
|
{
|
|
|
|
Q_UNUSED(time)
|
|
|
|
notifyActivity();
|
|
|
|
}
|
|
|
|
void pinchGestureCancelled(quint32 time) override
|
|
|
|
{
|
|
|
|
Q_UNUSED(time)
|
|
|
|
notifyActivity();
|
|
|
|
}
|
|
|
|
|
|
|
|
void swipeGestureBegin(int fingerCount, quint32 time) override
|
|
|
|
{
|
|
|
|
Q_UNUSED(fingerCount)
|
|
|
|
Q_UNUSED(time)
|
|
|
|
notifyActivity();
|
|
|
|
}
|
|
|
|
void swipeGestureUpdate(const QSizeF &delta, quint32 time) override
|
|
|
|
{
|
|
|
|
Q_UNUSED(delta)
|
|
|
|
Q_UNUSED(time)
|
|
|
|
notifyActivity();
|
|
|
|
}
|
|
|
|
void swipeGestureEnd(quint32 time) override
|
|
|
|
{
|
|
|
|
Q_UNUSED(time)
|
|
|
|
notifyActivity();
|
|
|
|
}
|
|
|
|
void swipeGestureCancelled(quint32 time) override
|
|
|
|
{
|
|
|
|
Q_UNUSED(time)
|
|
|
|
notifyActivity();
|
|
|
|
}
|
|
|
|
|
|
|
|
void holdGestureBegin(int fingerCount, quint32 time) override
|
|
|
|
{
|
|
|
|
Q_UNUSED(fingerCount)
|
|
|
|
Q_UNUSED(time)
|
|
|
|
notifyActivity();
|
|
|
|
}
|
|
|
|
void holdGestureEnd(quint32 time) override
|
|
|
|
{
|
|
|
|
Q_UNUSED(time)
|
|
|
|
notifyActivity();
|
|
|
|
}
|
|
|
|
void holdGestureCancelled(quint32 time) override
|
|
|
|
{
|
|
|
|
Q_UNUSED(time)
|
|
|
|
notifyActivity();
|
|
|
|
}
|
|
|
|
|
|
|
|
void tabletToolEvent(TabletEvent *event) override
|
|
|
|
{
|
|
|
|
Q_UNUSED(event)
|
|
|
|
notifyActivity();
|
|
|
|
}
|
|
|
|
void tabletToolButtonEvent(uint button, bool pressed, const TabletToolId &tabletToolId) override
|
|
|
|
{
|
|
|
|
Q_UNUSED(button)
|
|
|
|
Q_UNUSED(pressed)
|
|
|
|
Q_UNUSED(tabletToolId)
|
|
|
|
notifyActivity();
|
|
|
|
}
|
|
|
|
void tabletPadButtonEvent(uint button, bool pressed, const TabletPadId &tabletPadId) override
|
|
|
|
{
|
|
|
|
Q_UNUSED(button)
|
|
|
|
Q_UNUSED(pressed)
|
|
|
|
Q_UNUSED(tabletPadId)
|
|
|
|
notifyActivity();
|
|
|
|
}
|
|
|
|
void tabletPadStripEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId) override
|
|
|
|
{
|
|
|
|
Q_UNUSED(number)
|
|
|
|
Q_UNUSED(position)
|
|
|
|
Q_UNUSED(isFinger)
|
|
|
|
Q_UNUSED(tabletPadId)
|
|
|
|
notifyActivity();
|
|
|
|
}
|
|
|
|
void tabletPadRingEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId) override
|
|
|
|
{
|
|
|
|
Q_UNUSED(number)
|
|
|
|
Q_UNUSED(position)
|
|
|
|
Q_UNUSED(isFinger)
|
|
|
|
Q_UNUSED(tabletPadId)
|
|
|
|
notifyActivity();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void notifyActivity()
|
|
|
|
{
|
|
|
|
waylandServer()->simulateUserActivity();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-02-03 14:17:49 +00:00
|
|
|
void InputRedirection::setupInputFilters()
|
|
|
|
{
|
2021-12-01 08:47:03 +00:00
|
|
|
const bool hasGlobalShortcutSupport = waylandServer()->hasGlobalShortcutSupport();
|
2021-01-30 13:22:07 +00:00
|
|
|
if ((kwinApp()->platform()->session()->capabilities() & Session::Capability::SwitchTerminal)
|
|
|
|
&& hasGlobalShortcutSupport) {
|
2016-02-03 14:17:49 +00:00
|
|
|
installInputEventFilter(new VirtualTerminalFilter);
|
|
|
|
}
|
2021-12-01 08:47:03 +00:00
|
|
|
installInputEventSpy(new HideCursorSpy);
|
|
|
|
installInputEventSpy(new UserActivitySpy);
|
|
|
|
if (hasGlobalShortcutSupport) {
|
|
|
|
installInputEventFilter(new TerminateServerFilter);
|
2016-02-03 14:17:49 +00:00
|
|
|
}
|
2021-12-01 08:47:03 +00:00
|
|
|
installInputEventFilter(new DragAndDropInputFilter);
|
|
|
|
installInputEventFilter(new LockScreenFilter);
|
|
|
|
m_windowSelector = new WindowSelectorFilter;
|
|
|
|
installInputEventFilter(m_windowSelector);
|
2018-12-02 12:09:37 +00:00
|
|
|
if (hasGlobalShortcutSupport) {
|
|
|
|
installInputEventFilter(new ScreenEdgeInputFilter);
|
|
|
|
}
|
2016-02-03 14:17:49 +00:00
|
|
|
installInputEventFilter(new EffectsFilter);
|
|
|
|
installInputEventFilter(new MoveResizeFilter);
|
|
|
|
#ifdef KWIN_BUILD_TABBOX
|
|
|
|
installInputEventFilter(new TabBoxInputFilter);
|
|
|
|
#endif
|
2018-12-02 12:09:37 +00:00
|
|
|
if (hasGlobalShortcutSupport) {
|
|
|
|
installInputEventFilter(new GlobalShortcutFilter);
|
|
|
|
}
|
2021-12-01 08:47:03 +00:00
|
|
|
installInputEventFilter(new PopupInputFilter);
|
2016-02-03 14:17:49 +00:00
|
|
|
installInputEventFilter(new DecorationEventFilter);
|
2021-12-01 08:47:03 +00:00
|
|
|
installInputEventFilter(new WindowActionInputFilter);
|
|
|
|
installInputEventFilter(new InternalWindowEventFilter);
|
2021-12-13 17:23:23 +00:00
|
|
|
installInputEventFilter(new InputKeyboardFilter);
|
2021-12-01 08:47:03 +00:00
|
|
|
installInputEventFilter(new ForwardInputFilter);
|
|
|
|
installInputEventFilter(new TabletInputFilter);
|
2015-06-13 15:54:08 +00:00
|
|
|
}
|
|
|
|
|
2020-12-10 21:18:31 +00:00
|
|
|
void InputRedirection::handleInputConfigChanged(const KConfigGroup &group)
|
|
|
|
{
|
|
|
|
if (group.name() == QLatin1String("Keyboard")) {
|
2021-10-02 08:26:51 +00:00
|
|
|
m_keyboard->reconfigure();
|
2020-12-10 21:18:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
void InputRedirection::updateLeds(LEDs leds)
|
2015-09-15 08:29:06 +00:00
|
|
|
{
|
2021-10-02 08:26:51 +00:00
|
|
|
if (m_leds != leds) {
|
|
|
|
m_leds = leds;
|
2015-09-15 08:29:06 +00:00
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
for (InputDevice *device : qAsConst(m_inputDevices)) {
|
|
|
|
device->setLeds(leds);
|
|
|
|
}
|
2015-09-15 08:29:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
void InputRedirection::handleInputDeviceAdded(InputDevice *device)
|
2014-08-15 07:30:08 +00:00
|
|
|
{
|
2021-10-02 08:26:51 +00:00
|
|
|
connect(device, &InputDevice::keyChanged, m_keyboard, &KeyboardInputRedirection::processKey);
|
|
|
|
|
|
|
|
connect(device, &InputDevice::pointerMotionAbsolute,
|
|
|
|
m_pointer, &PointerInputRedirection::processMotionAbsolute);
|
|
|
|
connect(device, &InputDevice::pointerMotion,
|
|
|
|
m_pointer, &PointerInputRedirection::processMotion);
|
|
|
|
connect(device, &InputDevice::pointerButtonChanged,
|
|
|
|
m_pointer, &PointerInputRedirection::processButton);
|
|
|
|
connect(device, &InputDevice::pointerAxisChanged,
|
|
|
|
m_pointer, &PointerInputRedirection::processAxis);
|
|
|
|
connect(device, &InputDevice::pinchGestureBegin,
|
|
|
|
m_pointer, &PointerInputRedirection::processPinchGestureBegin);
|
|
|
|
connect(device, &InputDevice::pinchGestureUpdate,
|
|
|
|
m_pointer, &PointerInputRedirection::processPinchGestureUpdate);
|
|
|
|
connect(device, &InputDevice::pinchGestureEnd,
|
|
|
|
m_pointer, &PointerInputRedirection::processPinchGestureEnd);
|
|
|
|
connect(device, &InputDevice::pinchGestureCancelled,
|
|
|
|
m_pointer, &PointerInputRedirection::processPinchGestureCancelled);
|
|
|
|
connect(device, &InputDevice::swipeGestureBegin,
|
|
|
|
m_pointer, &PointerInputRedirection::processSwipeGestureBegin);
|
|
|
|
connect(device, &InputDevice::swipeGestureUpdate,
|
|
|
|
m_pointer, &PointerInputRedirection::processSwipeGestureUpdate);
|
|
|
|
connect(device, &InputDevice::swipeGestureEnd,
|
|
|
|
m_pointer, &PointerInputRedirection::processSwipeGestureEnd);
|
|
|
|
connect(device, &InputDevice::swipeGestureCancelled,
|
|
|
|
m_pointer, &PointerInputRedirection::processSwipeGestureCancelled);
|
|
|
|
connect(device, &InputDevice::holdGestureBegin,
|
|
|
|
m_pointer, &PointerInputRedirection::processHoldGestureBegin);
|
|
|
|
connect(device, &InputDevice::holdGestureEnd,
|
|
|
|
m_pointer, &PointerInputRedirection::processHoldGestureEnd);
|
|
|
|
connect(device, &InputDevice::holdGestureCancelled,
|
|
|
|
m_pointer, &PointerInputRedirection::processHoldGestureCancelled);
|
|
|
|
|
|
|
|
connect(device, &InputDevice::touchDown, m_touch, &TouchInputRedirection::processDown);
|
|
|
|
connect(device, &InputDevice::touchUp, m_touch, &TouchInputRedirection::processUp);
|
|
|
|
connect(device, &InputDevice::touchMotion, m_touch, &TouchInputRedirection::processMotion);
|
|
|
|
connect(device, &InputDevice::touchCanceled, m_touch, &TouchInputRedirection::cancel);
|
|
|
|
connect(device, &InputDevice::touchFrame, m_touch, &TouchInputRedirection::frame);
|
|
|
|
|
|
|
|
auto handleSwitchEvent = [this] (SwitchEvent::State state, quint32 time, quint64 timeMicroseconds, InputDevice *device) {
|
|
|
|
SwitchEvent event(state, time, timeMicroseconds, device);
|
|
|
|
processSpies(std::bind(&InputEventSpy::switchEvent, std::placeholders::_1, &event));
|
|
|
|
processFilters(std::bind(&InputEventFilter::switchEvent, std::placeholders::_1, &event));
|
|
|
|
};
|
|
|
|
connect(device, &InputDevice::switchToggledOn, this,
|
|
|
|
std::bind(handleSwitchEvent, SwitchEvent::State::On, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
|
|
|
connect(device, &InputDevice::switchToggledOff, this,
|
|
|
|
std::bind(handleSwitchEvent, SwitchEvent::State::Off, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
|
|
|
|
|
|
|
connect(device, &InputDevice::tabletToolEvent,
|
|
|
|
m_tablet, &TabletInputRedirection::tabletToolEvent);
|
|
|
|
connect(device, &InputDevice::tabletToolButtonEvent,
|
|
|
|
m_tablet, &TabletInputRedirection::tabletToolButtonEvent);
|
|
|
|
connect(device, &InputDevice::tabletPadButtonEvent,
|
|
|
|
m_tablet, &TabletInputRedirection::tabletPadButtonEvent);
|
|
|
|
connect(device, &InputDevice::tabletPadRingEvent,
|
|
|
|
m_tablet, &TabletInputRedirection::tabletPadRingEvent);
|
|
|
|
connect(device, &InputDevice::tabletPadStripEvent,
|
|
|
|
m_tablet, &TabletInputRedirection::tabletPadStripEvent);
|
|
|
|
|
|
|
|
device->setLeds(m_leds);
|
|
|
|
|
|
|
|
m_inputDevices.append(device);
|
|
|
|
Q_EMIT deviceAdded(device);
|
|
|
|
|
|
|
|
updateAvailableInputDevices();
|
|
|
|
}
|
|
|
|
|
|
|
|
void InputRedirection::handleInputDeviceRemoved(InputDevice *device)
|
|
|
|
{
|
|
|
|
m_inputDevices.removeOne(device);
|
|
|
|
Q_EMIT deviceRemoved(device);
|
|
|
|
|
|
|
|
updateAvailableInputDevices();
|
|
|
|
}
|
|
|
|
|
|
|
|
void InputRedirection::updateAvailableInputDevices()
|
|
|
|
{
|
|
|
|
const bool hasKeyboard = std::any_of(m_inputDevices.constBegin(), m_inputDevices.constEnd(), [](InputDevice *device) {
|
|
|
|
return device->isKeyboard();
|
|
|
|
});
|
|
|
|
if (m_hasKeyboard != hasKeyboard) {
|
|
|
|
m_hasKeyboard = hasKeyboard;
|
|
|
|
Q_EMIT hasKeyboardChanged(hasKeyboard);
|
2014-08-15 12:03:52 +00:00
|
|
|
}
|
2021-10-02 08:26:51 +00:00
|
|
|
|
|
|
|
const bool hasAlphaNumericKeyboard = std::any_of(m_inputDevices.constBegin(), m_inputDevices.constEnd(), [](InputDevice *device) {
|
|
|
|
return device->isAlphaNumericKeyboard();
|
|
|
|
});
|
|
|
|
if (m_hasAlphaNumericKeyboard != hasAlphaNumericKeyboard) {
|
|
|
|
m_hasAlphaNumericKeyboard = hasAlphaNumericKeyboard;
|
|
|
|
Q_EMIT hasAlphaNumericKeyboardChanged(hasAlphaNumericKeyboard);
|
|
|
|
}
|
|
|
|
|
|
|
|
const bool hasPointer = std::any_of(m_inputDevices.constBegin(), m_inputDevices.constEnd(), [](InputDevice *device) {
|
|
|
|
return device->isPointer();
|
|
|
|
});
|
|
|
|
if (m_hasPointer != hasPointer) {
|
|
|
|
m_hasPointer = hasPointer;
|
|
|
|
Q_EMIT hasPointerChanged(hasPointer);
|
|
|
|
}
|
|
|
|
|
|
|
|
const bool hasTouch = std::any_of(m_inputDevices.constBegin(), m_inputDevices.constEnd(), [](InputDevice *device) {
|
|
|
|
return device->isTouch();
|
|
|
|
});
|
|
|
|
if (m_hasTouch != hasTouch) {
|
|
|
|
m_hasTouch = hasTouch;
|
|
|
|
Q_EMIT hasTouchChanged(hasTouch);
|
|
|
|
}
|
|
|
|
|
|
|
|
const bool hasTabletModeSwitch = std::any_of(m_inputDevices.constBegin(), m_inputDevices.constEnd(), [](InputDevice *device) {
|
|
|
|
return device->isTabletModeSwitch();
|
|
|
|
});
|
|
|
|
if (m_hasTabletModeSwitch != hasTabletModeSwitch) {
|
|
|
|
m_hasTabletModeSwitch = hasTabletModeSwitch;
|
|
|
|
Q_EMIT hasTabletModeSwitchChanged(hasTabletModeSwitch);
|
2015-03-30 06:55:41 +00:00
|
|
|
}
|
2021-10-02 08:26:51 +00:00
|
|
|
}
|
2021-09-27 10:10:37 +00:00
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
void InputRedirection::toggleTouchpads()
|
|
|
|
{
|
|
|
|
bool changed = false;
|
|
|
|
m_touchpadsEnabled = !m_touchpadsEnabled;
|
|
|
|
for (InputDevice *device : qAsConst(m_inputDevices)) {
|
|
|
|
if (!device->isTouchpad()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const bool old = device->isEnabled();
|
|
|
|
device->setEnabled(m_touchpadsEnabled);
|
|
|
|
if (old != device->isEnabled()) {
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (changed) {
|
|
|
|
// send OSD message
|
|
|
|
QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.plasmashell"),
|
|
|
|
QStringLiteral("/org/kde/osdService"),
|
|
|
|
QStringLiteral("org.kde.osdService"),
|
|
|
|
QStringLiteral("touchpadEnabledChanged")
|
2015-09-02 09:32:26 +00:00
|
|
|
);
|
2021-10-02 08:26:51 +00:00
|
|
|
msg.setArguments({m_touchpadsEnabled});
|
|
|
|
QDBusConnection::sessionBus().asyncCall(msg);
|
|
|
|
}
|
|
|
|
}
|
2020-12-10 21:18:31 +00:00
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
void InputRedirection::enableTouchpads()
|
|
|
|
{
|
|
|
|
if (!m_touchpadsEnabled) {
|
|
|
|
toggleTouchpads();
|
2015-02-26 14:32:44 +00:00
|
|
|
}
|
2017-05-06 23:40:48 +00:00
|
|
|
}
|
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
void InputRedirection::disableTouchpads()
|
2017-05-06 23:40:48 +00:00
|
|
|
{
|
2021-10-02 08:26:51 +00:00
|
|
|
if (m_touchpadsEnabled) {
|
|
|
|
toggleTouchpads();
|
2017-05-06 23:40:48 +00:00
|
|
|
}
|
2021-10-02 08:26:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void InputRedirection::addInputBackend(InputBackend *inputBackend)
|
|
|
|
{
|
|
|
|
Q_ASSERT(!m_inputBackends.contains(inputBackend));
|
|
|
|
m_inputBackends.append(inputBackend);
|
|
|
|
|
|
|
|
connect(inputBackend, &InputBackend::deviceAdded, this, &InputRedirection::handleInputDeviceAdded);
|
|
|
|
connect(inputBackend, &InputBackend::deviceRemoved, this, &InputRedirection::handleInputDeviceRemoved);
|
|
|
|
|
|
|
|
inputBackend->setConfig(InputConfig::self()->inputConfig());
|
|
|
|
inputBackend->initialize();
|
|
|
|
}
|
|
|
|
|
|
|
|
void InputRedirection::setupInputBackends()
|
|
|
|
{
|
|
|
|
InputBackend *inputBackend = kwinApp()->platform()->createInputBackend();
|
|
|
|
if (inputBackend) {
|
|
|
|
addInputBackend(inputBackend);
|
|
|
|
}
|
2021-10-27 08:38:19 +00:00
|
|
|
if (waylandServer()) {
|
|
|
|
addInputBackend(new FakeInputBackend());
|
|
|
|
}
|
2021-10-02 08:26:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void InputRedirection::setupTouchpadShortcuts()
|
|
|
|
{
|
2017-05-06 23:40:48 +00:00
|
|
|
QAction *touchpadToggleAction = new QAction(this);
|
|
|
|
QAction *touchpadOnAction = new QAction(this);
|
|
|
|
QAction *touchpadOffAction = new QAction(this);
|
|
|
|
|
2021-07-26 08:37:48 +00:00
|
|
|
const QString touchpadDisplayName = i18n("Touchpad");
|
|
|
|
|
2017-05-06 23:40:48 +00:00
|
|
|
touchpadToggleAction->setObjectName(QStringLiteral("Toggle Touchpad"));
|
|
|
|
touchpadToggleAction->setProperty("componentName", s_touchpadComponent);
|
2021-07-26 08:37:48 +00:00
|
|
|
touchpadToggleAction->setProperty("componentDisplayName", touchpadDisplayName);
|
2017-05-06 23:40:48 +00:00
|
|
|
touchpadOnAction->setObjectName(QStringLiteral("Enable Touchpad"));
|
|
|
|
touchpadOnAction->setProperty("componentName", s_touchpadComponent);
|
2021-07-26 08:37:48 +00:00
|
|
|
touchpadOnAction->setProperty("componentDisplayName", touchpadDisplayName);
|
2017-05-06 23:40:48 +00:00
|
|
|
touchpadOffAction->setObjectName(QStringLiteral("Disable Touchpad"));
|
|
|
|
touchpadOffAction->setProperty("componentName", s_touchpadComponent);
|
2021-07-26 08:37:48 +00:00
|
|
|
touchpadOffAction->setProperty("componentDisplayName", touchpadDisplayName);
|
2017-05-06 23:40:48 +00:00
|
|
|
KGlobalAccel::self()->setDefaultShortcut(touchpadToggleAction, QList<QKeySequence>{Qt::Key_TouchpadToggle});
|
|
|
|
KGlobalAccel::self()->setShortcut(touchpadToggleAction, QList<QKeySequence>{Qt::Key_TouchpadToggle});
|
|
|
|
KGlobalAccel::self()->setDefaultShortcut(touchpadOnAction, QList<QKeySequence>{Qt::Key_TouchpadOn});
|
|
|
|
KGlobalAccel::self()->setShortcut(touchpadOnAction, QList<QKeySequence>{Qt::Key_TouchpadOn});
|
|
|
|
KGlobalAccel::self()->setDefaultShortcut(touchpadOffAction, QList<QKeySequence>{Qt::Key_TouchpadOff});
|
|
|
|
KGlobalAccel::self()->setShortcut(touchpadOffAction, QList<QKeySequence>{Qt::Key_TouchpadOff});
|
|
|
|
#ifndef KWIN_BUILD_TESTING
|
|
|
|
registerShortcut(Qt::Key_TouchpadToggle, touchpadToggleAction);
|
|
|
|
registerShortcut(Qt::Key_TouchpadOn, touchpadOnAction);
|
|
|
|
registerShortcut(Qt::Key_TouchpadOff, touchpadOffAction);
|
|
|
|
#endif
|
2021-10-02 08:26:51 +00:00
|
|
|
connect(touchpadToggleAction, &QAction::triggered, this, &InputRedirection::toggleTouchpads);
|
|
|
|
connect(touchpadOnAction, &QAction::triggered, this, &InputRedirection::enableTouchpads);
|
|
|
|
connect(touchpadOffAction, &QAction::triggered, this, &InputRedirection::disableTouchpads);
|
2015-03-26 15:19:26 +00:00
|
|
|
}
|
2015-02-26 14:32:44 +00:00
|
|
|
|
2016-08-03 06:31:58 +00:00
|
|
|
bool InputRedirection::hasAlphaNumericKeyboard()
|
|
|
|
{
|
2021-10-02 08:26:51 +00:00
|
|
|
return m_hasAlphaNumericKeyboard;
|
2016-08-03 06:31:58 +00:00
|
|
|
}
|
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
bool InputRedirection::hasPointer() const
|
Expose if the tablet mode switch is available
Summary:
expose in the libinput wrapper a property that tells whether
a tablet mode switch input device is present on the machine,
expose it trough dbus
Test Plan:
still not complete, I need a way to either access the connection
from TabletModeManager or setting to TabletModeManager from input.cpp
Reviewers: #kwin, #plasma, graesslin
Reviewed By: #kwin, #plasma, graesslin
Subscribers: graesslin, ngraham, davidedmundson, plasma-devel, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D9944
2018-01-24 10:36:56 +00:00
|
|
|
{
|
2021-10-02 08:26:51 +00:00
|
|
|
return m_hasPointer;
|
Expose if the tablet mode switch is available
Summary:
expose in the libinput wrapper a property that tells whether
a tablet mode switch input device is present on the machine,
expose it trough dbus
Test Plan:
still not complete, I need a way to either access the connection
from TabletModeManager or setting to TabletModeManager from input.cpp
Reviewers: #kwin, #plasma, graesslin
Reviewed By: #kwin, #plasma, graesslin
Subscribers: graesslin, ngraham, davidedmundson, plasma-devel, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D9944
2018-01-24 10:36:56 +00:00
|
|
|
}
|
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
bool InputRedirection::hasTouch() const
|
2015-03-30 06:55:41 +00:00
|
|
|
{
|
2021-10-02 08:26:51 +00:00
|
|
|
return m_hasTouch;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InputRedirection::hasTabletModeSwitch()
|
|
|
|
{
|
|
|
|
return m_hasTabletModeSwitch;
|
2015-03-30 06:55:41 +00:00
|
|
|
}
|
|
|
|
|
2013-06-26 09:40:30 +00:00
|
|
|
Qt::MouseButtons InputRedirection::qtButtonStates() const
|
|
|
|
{
|
2016-02-12 12:30:00 +00:00
|
|
|
return m_pointer->buttons();
|
2013-06-26 09:40:30 +00:00
|
|
|
}
|
|
|
|
|
2013-07-01 06:37:59 +00:00
|
|
|
Toplevel *InputRedirection::findToplevel(const QPoint &pos)
|
|
|
|
{
|
2015-03-20 13:43:47 +00:00
|
|
|
if (!Workspace::self()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2015-11-16 10:46:20 +00:00
|
|
|
const bool isScreenLocked = waylandServer() && waylandServer()->isScreenLocked();
|
2013-07-01 06:37:59 +00:00
|
|
|
// TODO: check whether the unmanaged wants input events at all
|
2015-11-16 10:46:20 +00:00
|
|
|
if (!isScreenLocked) {
|
2016-02-25 08:15:41 +00:00
|
|
|
// if an effect overrides the cursor we don't have a window to focus
|
|
|
|
if (effects && static_cast<EffectsHandlerImpl*>(effects)->isMouseInterception()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
Drop some custom list typedefs
Summary:
Qt has its own thing where a type might also have corresponding list
alias, e.g. QObject and QObjectList, QWidget and QWidgetList. I don't
know why Qt does that, maybe for some historical reasons, but what
matters is that we copy this pattern here in KWin. While this pattern
might be useful with some long list types, for example
QList<QWeakPointer<TabBoxClient>> TabBoxClientList
in general, it causes more harm than good. For example, we've got two
new client types, do we need corresponding list typedefs for them? If
no, why do we have ClientList and so on?
Another problem with these typedefs is that you need to include utils.h
header in order to use them. A better way to handle such things is to
just forward declare a client class (if that's possible) and use it
directly with QList or QVector. This way translation units don't get
"bloated" with utils.h stuff for no apparent reason.
So, in order to make code more consistent and easier to follow, this
change drops some of our custom typedefs. Namely ConstClientList,
ClientList, DeletedList, UnmanagedList, ToplevelList, and GroupList.
Test Plan: Compiles.
Reviewers: #kwin
Subscribers: kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D24950
2019-10-16 09:11:04 +00:00
|
|
|
const QList<Unmanaged *> &unmanaged = Workspace::self()->unmanagedList();
|
2021-11-03 11:55:31 +00:00
|
|
|
for (Unmanaged *u : unmanaged) {
|
Adapt to input region changes in kwayland-server
SurfaceInterface::inputIsInfinite() has been dropped. If the surface has
no any input region specified, SurfaceInterface::input() will return a
region that corresponds to the rect of the surface (0, 0, width, height).
While the new design is more robust, for example it's no longer possible
to forget to check SurfaceInterface::inputIsInfinite(), it has shown some
issues in the input stack of kwin.
Currently, acceptsInput() will return false if you attempt to click the
server-side decoration for a surface whose input region is not empty.
Therefore, it's possible for an application to set an input region with
a width and a height of 1. If user doesn't know about KSysGuard or the
possibility of closing apps via the task manager, they won't be able to
close such an application.
Another issue is that if an application has specified an empty input
region on purpose, user will be still able click it. With the new
behavior of SurfaceInterface::input(), this is no longer an issue and it
is handled properly by kwin.
2020-10-17 12:47:25 +00:00
|
|
|
if (u->hitTest(pos)) {
|
2015-11-16 10:46:20 +00:00
|
|
|
return u;
|
|
|
|
}
|
2013-07-01 06:37:59 +00:00
|
|
|
}
|
|
|
|
}
|
2018-08-22 12:56:48 +00:00
|
|
|
return findManagedToplevel(pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
Toplevel *InputRedirection::findManagedToplevel(const QPoint &pos)
|
|
|
|
{
|
|
|
|
if (!Workspace::self()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
const bool isScreenLocked = waylandServer() && waylandServer()->isScreenLocked();
|
Drop some custom list typedefs
Summary:
Qt has its own thing where a type might also have corresponding list
alias, e.g. QObject and QObjectList, QWidget and QWidgetList. I don't
know why Qt does that, maybe for some historical reasons, but what
matters is that we copy this pattern here in KWin. While this pattern
might be useful with some long list types, for example
QList<QWeakPointer<TabBoxClient>> TabBoxClientList
in general, it causes more harm than good. For example, we've got two
new client types, do we need corresponding list typedefs for them? If
no, why do we have ClientList and so on?
Another problem with these typedefs is that you need to include utils.h
header in order to use them. A better way to handle such things is to
just forward declare a client class (if that's possible) and use it
directly with QList or QVector. This way translation units don't get
"bloated" with utils.h stuff for no apparent reason.
So, in order to make code more consistent and easier to follow, this
change drops some of our custom typedefs. Namely ConstClientList,
ClientList, DeletedList, UnmanagedList, ToplevelList, and GroupList.
Test Plan: Compiles.
Reviewers: #kwin
Subscribers: kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D24950
2019-10-16 09:11:04 +00:00
|
|
|
const QList<Toplevel *> &stacking = Workspace::self()->stackingOrder();
|
2013-07-01 06:37:59 +00:00
|
|
|
if (stacking.isEmpty()) {
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
return nullptr;
|
2013-07-01 06:37:59 +00:00
|
|
|
}
|
|
|
|
auto it = stacking.end();
|
|
|
|
do {
|
|
|
|
--it;
|
|
|
|
Toplevel *t = (*it);
|
|
|
|
if (t->isDeleted()) {
|
|
|
|
// a deleted window doesn't get mouse events
|
|
|
|
continue;
|
|
|
|
}
|
2015-09-14 11:53:46 +00:00
|
|
|
if (AbstractClient *c = dynamic_cast<AbstractClient*>(t)) {
|
2019-09-14 08:58:12 +00:00
|
|
|
if (!c->isOnCurrentActivity() || !c->isOnCurrentDesktop() || c->isMinimized() || c->isHiddenInternal()) {
|
2013-07-01 06:37:59 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2015-05-18 13:30:10 +00:00
|
|
|
if (!t->readyForPainting()) {
|
|
|
|
continue;
|
|
|
|
}
|
2015-11-16 10:46:20 +00:00
|
|
|
if (isScreenLocked) {
|
|
|
|
if (!t->isLockScreen() && !t->isInputMethod()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
Adapt to input region changes in kwayland-server
SurfaceInterface::inputIsInfinite() has been dropped. If the surface has
no any input region specified, SurfaceInterface::input() will return a
region that corresponds to the rect of the surface (0, 0, width, height).
While the new design is more robust, for example it's no longer possible
to forget to check SurfaceInterface::inputIsInfinite(), it has shown some
issues in the input stack of kwin.
Currently, acceptsInput() will return false if you attempt to click the
server-side decoration for a surface whose input region is not empty.
Therefore, it's possible for an application to set an input region with
a width and a height of 1. If user doesn't know about KSysGuard or the
possibility of closing apps via the task manager, they won't be able to
close such an application.
Another issue is that if an application has specified an empty input
region on purpose, user will be still able click it. With the new
behavior of SurfaceInterface::input(), this is no longer an issue and it
is handled properly by kwin.
2020-10-17 12:47:25 +00:00
|
|
|
if (t->hitTest(pos)) {
|
2013-07-01 06:37:59 +00:00
|
|
|
return t;
|
|
|
|
}
|
|
|
|
} while (it != stacking.begin());
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
return nullptr;
|
2013-07-01 06:37:59 +00:00
|
|
|
}
|
|
|
|
|
2013-07-02 09:44:18 +00:00
|
|
|
Qt::KeyboardModifiers InputRedirection::keyboardModifiers() const
|
|
|
|
{
|
2016-02-15 12:42:48 +00:00
|
|
|
return m_keyboard->modifiers();
|
2013-07-02 09:44:18 +00:00
|
|
|
}
|
|
|
|
|
2016-12-26 13:19:56 +00:00
|
|
|
Qt::KeyboardModifiers InputRedirection::modifiersRelevantForGlobalShortcuts() const
|
|
|
|
{
|
|
|
|
return m_keyboard->modifiersRelevantForGlobalShortcuts();
|
|
|
|
}
|
|
|
|
|
2013-07-10 09:41:16 +00:00
|
|
|
void InputRedirection::registerShortcut(const QKeySequence &shortcut, QAction *action)
|
|
|
|
{
|
2017-07-28 19:31:09 +00:00
|
|
|
Q_UNUSED(shortcut)
|
2017-01-17 06:12:44 +00:00
|
|
|
kwinApp()->platform()->setupActionForGlobalAccel(action);
|
2013-07-10 09:41:16 +00:00
|
|
|
}
|
2013-07-02 09:44:18 +00:00
|
|
|
|
2013-07-14 20:52:58 +00:00
|
|
|
void InputRedirection::registerPointerShortcut(Qt::KeyboardModifiers modifiers, Qt::MouseButton pointerButtons, QAction *action)
|
|
|
|
{
|
|
|
|
m_shortcuts->registerPointerShortcut(action, modifiers, pointerButtons);
|
|
|
|
}
|
|
|
|
|
2013-07-15 09:26:56 +00:00
|
|
|
void InputRedirection::registerAxisShortcut(Qt::KeyboardModifiers modifiers, PointerAxisDirection axis, QAction *action)
|
|
|
|
{
|
|
|
|
m_shortcuts->registerAxisShortcut(action, modifiers, axis);
|
|
|
|
}
|
|
|
|
|
2021-04-19 04:42:46 +00:00
|
|
|
void InputRedirection::registerRealtimeTouchpadSwipeShortcut(SwipeDirection direction, QAction *action, std::function<void(qreal)> cb)
|
|
|
|
{
|
|
|
|
m_shortcuts->registerRealtimeTouchpadSwipe(action, cb, direction);
|
|
|
|
}
|
|
|
|
|
2017-03-18 10:00:30 +00:00
|
|
|
void InputRedirection::registerTouchpadSwipeShortcut(SwipeDirection direction, QAction *action)
|
|
|
|
{
|
|
|
|
m_shortcuts->registerTouchpadSwipe(action, direction);
|
|
|
|
}
|
|
|
|
|
2015-06-26 11:47:08 +00:00
|
|
|
void InputRedirection::registerGlobalAccel(KGlobalAccelInterface *interface)
|
|
|
|
{
|
|
|
|
m_shortcuts->setKGlobalAccelInterface(interface);
|
|
|
|
}
|
|
|
|
|
2016-02-12 12:30:00 +00:00
|
|
|
void InputRedirection::warpPointer(const QPointF &pos)
|
2014-10-17 13:44:12 +00:00
|
|
|
{
|
2016-02-12 12:30:00 +00:00
|
|
|
m_pointer->warp(pos);
|
2014-10-17 13:44:12 +00:00
|
|
|
}
|
|
|
|
|
2016-02-12 12:30:00 +00:00
|
|
|
bool InputRedirection::supportsPointerWarping() const
|
2014-10-17 13:44:12 +00:00
|
|
|
{
|
2016-02-12 12:30:00 +00:00
|
|
|
return m_pointer->supportsWarping();
|
2014-10-17 13:44:12 +00:00
|
|
|
}
|
|
|
|
|
2016-02-12 12:30:00 +00:00
|
|
|
QPointF InputRedirection::globalPointer() const
|
2015-06-05 17:34:03 +00:00
|
|
|
{
|
2016-02-12 12:30:00 +00:00
|
|
|
return m_pointer->pos();
|
2015-06-05 17:34:03 +00:00
|
|
|
}
|
|
|
|
|
2016-11-15 13:23:51 +00:00
|
|
|
void InputRedirection::startInteractiveWindowSelection(std::function<void(KWin::Toplevel*)> callback, const QByteArray &cursorName)
|
|
|
|
{
|
|
|
|
if (!m_windowSelector || m_windowSelector->isActive()) {
|
|
|
|
callback(nullptr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
m_windowSelector->start(callback);
|
|
|
|
m_pointer->setWindowSelectionCursor(cursorName);
|
|
|
|
}
|
|
|
|
|
2016-11-23 14:53:17 +00:00
|
|
|
void InputRedirection::startInteractivePositionSelection(std::function<void(const QPoint &)> callback)
|
|
|
|
{
|
|
|
|
if (!m_windowSelector || m_windowSelector->isActive()) {
|
|
|
|
callback(QPoint(-1, -1));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
m_windowSelector->start(callback);
|
|
|
|
m_pointer->setWindowSelectionCursor(QByteArray());
|
|
|
|
}
|
|
|
|
|
2016-11-15 13:23:51 +00:00
|
|
|
bool InputRedirection::isSelectingWindow() const
|
|
|
|
{
|
|
|
|
return m_windowSelector ? m_windowSelector->isActive() : false;
|
|
|
|
}
|
|
|
|
|
2016-05-12 14:33:03 +00:00
|
|
|
InputDeviceHandler::InputDeviceHandler(InputRedirection *input)
|
|
|
|
: QObject(input)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
InputDeviceHandler::~InputDeviceHandler() = default;
|
|
|
|
|
2018-09-15 00:00:24 +00:00
|
|
|
void InputDeviceHandler::init()
|
2016-05-12 14:33:03 +00:00
|
|
|
{
|
2018-09-15 00:00:24 +00:00
|
|
|
connect(workspace(), &Workspace::stackingOrderChanged, this, &InputDeviceHandler::update);
|
|
|
|
connect(workspace(), &Workspace::clientMinimizedChanged, this, &InputDeviceHandler::update);
|
|
|
|
connect(VirtualDesktopManager::self(), &VirtualDesktopManager::currentChanged, this, &InputDeviceHandler::update);
|
|
|
|
}
|
|
|
|
|
2021-11-29 16:33:08 +00:00
|
|
|
bool InputDeviceHandler::setHover(Toplevel *toplevel)
|
2018-09-15 00:00:24 +00:00
|
|
|
{
|
2021-11-29 16:33:08 +00:00
|
|
|
if (m_hover.window == toplevel) {
|
2018-09-15 00:00:24 +00:00
|
|
|
return false;
|
|
|
|
}
|
2021-11-29 16:33:08 +00:00
|
|
|
auto old = m_hover.window;
|
|
|
|
disconnect(m_hover.surfaceCreatedConnection);
|
|
|
|
m_hover.surfaceCreatedConnection = QMetaObject::Connection();
|
2019-06-06 21:17:01 +00:00
|
|
|
|
2021-11-29 16:33:08 +00:00
|
|
|
m_hover.window = toplevel;
|
2018-09-15 00:00:24 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void InputDeviceHandler::setFocus(Toplevel *toplevel)
|
|
|
|
{
|
2021-12-15 16:00:24 +00:00
|
|
|
if (m_focus.window != toplevel) {
|
|
|
|
Toplevel *oldFocus = m_focus.window;
|
|
|
|
m_focus.window = toplevel;
|
|
|
|
focusUpdate(oldFocus, m_focus.window);
|
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
}
|
|
|
|
|
2021-01-28 14:45:57 +00:00
|
|
|
void InputDeviceHandler::setDecoration(Decoration::DecoratedClientImpl *decoration)
|
2018-09-15 00:00:24 +00:00
|
|
|
{
|
2021-12-15 16:00:24 +00:00
|
|
|
if (m_focus.decoration != decoration) {
|
|
|
|
auto oldDeco = m_focus.decoration;
|
|
|
|
m_focus.decoration = decoration;
|
|
|
|
cleanupDecoration(oldDeco.data(), m_focus.decoration.data());
|
|
|
|
Q_EMIT decorationChanged();
|
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void InputDeviceHandler::updateFocus()
|
|
|
|
{
|
2021-11-29 16:46:17 +00:00
|
|
|
Toplevel *focus = m_hover.window;
|
2019-06-06 21:17:01 +00:00
|
|
|
|
2021-12-15 16:00:24 +00:00
|
|
|
if (m_focus.decoration) {
|
|
|
|
focus = nullptr;
|
|
|
|
} else if (m_hover.window && !m_hover.window->surface() && !m_hover.window->isInternal()) {
|
2019-06-06 21:17:01 +00:00
|
|
|
// The surface has not yet been created (special XWayland case).
|
|
|
|
// Therefore listen for its creation.
|
2021-11-29 16:33:08 +00:00
|
|
|
if (!m_hover.surfaceCreatedConnection) {
|
|
|
|
m_hover.surfaceCreatedConnection = connect(m_hover.window, &Toplevel::surfaceChanged,
|
2019-06-06 21:17:01 +00:00
|
|
|
this, &InputDeviceHandler::update);
|
|
|
|
}
|
2021-11-29 16:46:17 +00:00
|
|
|
focus = nullptr;
|
2019-06-06 21:17:01 +00:00
|
|
|
}
|
|
|
|
|
2021-11-29 16:46:17 +00:00
|
|
|
setFocus(focus);
|
2018-09-15 00:00:24 +00:00
|
|
|
}
|
|
|
|
|
2021-12-15 16:00:24 +00:00
|
|
|
void InputDeviceHandler::updateDecoration()
|
2018-09-15 00:00:24 +00:00
|
|
|
{
|
2021-12-15 16:00:24 +00:00
|
|
|
Decoration::DecoratedClientImpl *decoration = nullptr;
|
2021-11-29 16:33:08 +00:00
|
|
|
auto *ac = qobject_cast<AbstractClient*>(m_hover.window);
|
2018-09-15 00:00:24 +00:00
|
|
|
if (ac && ac->decoratedClient()) {
|
2021-01-28 15:39:32 +00:00
|
|
|
if (!ac->clientGeometry().contains(position().toPoint())) {
|
2018-09-15 00:00:24 +00:00
|
|
|
// input device above decoration
|
2021-12-15 16:00:24 +00:00
|
|
|
decoration = ac->decoratedClient();
|
2016-05-12 14:33:03 +00:00
|
|
|
}
|
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
|
2021-12-15 16:00:24 +00:00
|
|
|
setDecoration(decoration);
|
2018-09-15 00:00:24 +00:00
|
|
|
}
|
2016-05-12 14:33:03 +00:00
|
|
|
|
2018-09-15 00:00:24 +00:00
|
|
|
void InputDeviceHandler::update()
|
|
|
|
{
|
|
|
|
if (!m_inited) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-12-13 09:46:21 +00:00
|
|
|
Toplevel *toplevel = nullptr;
|
2021-01-28 15:10:14 +00:00
|
|
|
if (positionValid()) {
|
2021-01-30 20:29:55 +00:00
|
|
|
toplevel = input()->findToplevel(position().toPoint());
|
2018-12-13 09:46:21 +00:00
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
// Always set the toplevel at the position of the input device.
|
2021-11-29 16:33:08 +00:00
|
|
|
setHover(toplevel);
|
2018-09-15 00:00:24 +00:00
|
|
|
|
|
|
|
if (focusUpdatesBlocked()) {
|
2021-05-03 08:24:22 +00:00
|
|
|
workspace()->updateFocusMousePosition(position().toPoint());
|
2018-09-15 00:00:24 +00:00
|
|
|
return;
|
2016-05-12 14:33:03 +00:00
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
|
2021-12-15 16:00:24 +00:00
|
|
|
updateDecoration();
|
|
|
|
updateFocus();
|
2021-05-03 08:24:22 +00:00
|
|
|
|
|
|
|
workspace()->updateFocusMousePosition(position().toPoint());
|
2016-05-12 14:33:03 +00:00
|
|
|
}
|
|
|
|
|
2021-11-29 16:33:08 +00:00
|
|
|
Toplevel *InputDeviceHandler::hover() const
|
2020-09-07 08:11:07 +00:00
|
|
|
{
|
2021-11-29 16:33:08 +00:00
|
|
|
return m_hover.window.data();
|
2020-09-07 08:11:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Toplevel *InputDeviceHandler::focus() const
|
|
|
|
{
|
2021-11-29 16:33:08 +00:00
|
|
|
return m_focus.window.data();
|
2020-09-07 08:11:07 +00:00
|
|
|
}
|
|
|
|
|
2021-01-28 14:45:57 +00:00
|
|
|
Decoration::DecoratedClientImpl *InputDeviceHandler::decoration() const
|
|
|
|
{
|
|
|
|
return m_focus.decoration;
|
|
|
|
}
|
|
|
|
|
2021-10-13 11:14:21 +00:00
|
|
|
uint32_t InputDeviceHandler::lastEventTime() const
|
|
|
|
{
|
|
|
|
return m_lastEventTime;
|
|
|
|
}
|
|
|
|
|
2013-06-26 08:15:20 +00:00
|
|
|
} // namespace
|
2021-02-19 11:32:35 +00:00
|
|
|
|
|
|
|
#include "input.moc"
|