Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Network
KDE Connect
Commits
cf8ada2b
Commit
cf8ada2b
authored
Jul 23, 2021
by
Aleix Pol Gonzalez
🐧
Committed by
Aleix Pol Gonzalez
Jul 30, 2021
Browse files
app: make it possible to lock the mouse when using the remote input
It makes it possible to use normal touchpads and mice
parent
55b946f8
Pipeline
#72696
passed with stage
in 3 minutes and 57 seconds
Changes
8
Pipelines
2
Hide whitespace changes
Inline
Side-by-side
CMakeLists.txt
View file @
cf8ada2b
...
...
@@ -61,7 +61,7 @@ else()
set
(
Qca_LIBRARY qca-qt5
)
set
(
KF5_REQUIRED_COMPONENTS I18n ConfigWidgets DBusAddons IconThemes Notifications KIO KCMUtils Service Solid Kirigami2 People WindowSystem
)
set
(
KF5_OPTIONAL_COMPONENTS DocTools
)
set
(
KF5_OPTIONAL_COMPONENTS DocTools
Wayland
)
set_package_properties
(
KF5Kirigami2 PROPERTIES
DESCRIPTION
"QtQuick plugins to build user interfaces based on KDE UX guidelines"
...
...
app/qml/mousepad.qml
View file @
cf8ada2b
...
...
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
import
QtQuick
2.
2
import
QtQuick
2.
15
import
QtQuick
.
Controls
2.2
import
QtQuick
.
Layouts
1.1
import
org
.
kde
.
kirigami
2.7
as
Kirigami
...
...
@@ -20,6 +20,10 @@ Kirigami.Page
// Otherwise swiping on the MouseArea might trigger changing the page
Kirigami.ColumnView.preventStealing
:
true
Component.onCompleted
:
{
PointerLocker
.
window
=
applicationWindow
()
}
ColumnLayout
{
anchors.fill
:
parent
...
...
@@ -34,6 +38,37 @@ Kirigami.Page
acceptedButtons
:
Qt
.
LeftButton
|
Qt
.
RightButton
|
Qt
.
MiddleButton
Button
{
id
:
lockButton
anchors.centerIn
:
parent
text
:
i18n
(
"
Lock
"
)
visible
:
!
Kirigami
.
Settings
.
tabletMode
&&
!
PointerLocker
.
isLocked
onClicked
:
{
PointerLocker
.
isLocked
=
true
area
.
pressedPos
=
Qt
.
point
(
-
1
,
-
1
);
}
}
Label
{
anchors.centerIn
:
parent
visible
:
PointerLocker
.
isLocked
text
:
i18n
(
"
Press the right 'x' key or the left and right mouse buttons at the same time to unlock
"
)
}
Connections
{
target
:
PointerLocker
onPointerMoved
:
{
if
(
!
PointerLocker
.
isLocked
)
return
;
mousepad
.
pluginInterface
.
moveCursor
(
Qt
.
point
(
delta
.
x
,
delta
.
y
));
}
}
// We don't want to see the lock button when using a touchscreen
TapHandler
{
acceptedDevices
:
PointerDevice
.
TouchScreen
onTapped
:
lockButton
.
visible
=
false
}
onClicked
:
{
var
clickType
=
""
;
var
packet
=
{};
...
...
@@ -90,8 +125,20 @@ Kirigami.Page
lastPos
=
Qt
.
point
(
mouse
.
x
,
mouse
.
y
);
}
Keys.onPressed
:
{
if
(
event
.
key
==
Qt
.
Key_X
)
{
PointerLocker
.
isLocked
=
false
event
.
accepted
=
true
;
}
}
onPressed
:
{
pressedPos
=
Qt
.
point
(
mouse
.
x
,
mouse
.
y
);
if
(
PointerLocker
.
isLocked
)
{
if
(
pressedButtons
===
(
Qt
.
LeftButton
|
Qt
.
RightButton
))
{
PointerLocker
.
isLocked
=
false
}
}
else
{
pressedPos
=
Qt
.
point
(
mouse
.
x
,
mouse
.
y
);
}
}
onWheel
:
{
...
...
declarativeplugin/CMakeLists.txt
View file @
cf8ada2b
...
...
@@ -2,6 +2,7 @@ add_library(kdeconnectdeclarativeplugin SHARED
kdeconnectdeclarativeplugin.cpp
responsewaiter.cpp
objectfactory.cpp
pointerlocker.cpp
resources.qrc
)
target_link_libraries
(
kdeconnectdeclarativeplugin
...
...
@@ -10,6 +11,14 @@ target_link_libraries(kdeconnectdeclarativeplugin
kdeconnectcore
)
if
(
TARGET KF5::WaylandClient
)
target_sources
(
kdeconnectdeclarativeplugin PRIVATE pointerlockerwayland.cpp
)
target_link_libraries
(
kdeconnectdeclarativeplugin KF5::WaylandClient
)
target_compile_definitions
(
kdeconnectdeclarativeplugin PRIVATE -DWITH_WAYLAND=1
)
else
()
target_compile_definitions
(
kdeconnectdeclarativeplugin PRIVATE -DWITH_WAYLAND=0
)
endif
()
install
(
TARGETS kdeconnectdeclarativeplugin DESTINATION
${
QML_INSTALL_DIR
}
/org/kde/kdeconnect
)
install
(
FILES qmldir DESTINATION
${
QML_INSTALL_DIR
}
/org/kde/kdeconnect
)
...
...
declarativeplugin/kdeconnectdeclarativeplugin.cpp
View file @
cf8ada2b
...
...
@@ -10,6 +10,7 @@
#include <QQmlContext>
#include <QDBusPendingCall>
#include <QDBusPendingReply>
#include <QGuiApplication>
#include "objectfactory.h"
#include "responsewaiter.h"
...
...
@@ -22,6 +23,10 @@
#include <pluginmodel.h>
#include "core/kdeconnectpluginconfig.h"
#include "interfaces/commandsmodel.h"
#include "pointerlocker.h"
#if WITH_WAYLAND == 1
#include "pointerlockerwayland.h"
#endif
QObject
*
createDBusResponse
()
{
...
...
@@ -63,6 +68,16 @@ void KdeConnectDeclarativePlugin::registerTypes(const char* uri)
return
new
DaemonDbusInterface
;
}
);
qmlRegisterSingletonType
<
AbstractPointerLocker
>
(
"org.kde.kdeconnect"
,
1
,
0
,
"PointerLocker"
,
[]
(
QQmlEngine
*
,
QJSEngine
*
)
->
QObject
*
{
AbstractPointerLocker
*
ret
;
#if WITH_WAYLAND == 1
if
(
qGuiApp
->
platformName
()
==
QLatin1String
(
"wayland"
))
ret
=
new
PointerLockerWayland
;
else
#endif
ret
=
new
PointerLockerQt
;
return
ret
;
});
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
qmlRegisterAnonymousType
<
QAbstractItemModel
>
(
uri
,
1
);
...
...
declarativeplugin/pointerlocker.cpp
0 → 100644
View file @
cf8ada2b
/*
SPDX-FileCopyrightText: 2018 Roman Gilg <subdiff@gmail.com>
SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "pointerlocker.h"
#include <QGuiApplication>
#include <QQmlContext>
#include <QQmlEngine>
#include <QCursor>
#include <QDebug>
#include <QScopedPointer>
void
AbstractPointerLocker
::
setWindow
(
QWindow
*
window
)
{
if
(
m_window
==
window
)
{
return
;
}
m_window
=
window
;
Q_EMIT
windowChanged
();
}
PointerLockerQt
::
PointerLockerQt
(
QObject
*
parent
)
:
AbstractPointerLocker
(
parent
)
{
}
PointerLockerQt
::~
PointerLockerQt
()
=
default
;
void
PointerLockerQt
::
setLocked
(
bool
lock
)
{
if
(
isLocked
()
==
lock
)
{
return
;
}
if
(
lock
)
{
/* Cursor needs to be hidden such that Xwayland emulates warps. */
QGuiApplication
::
setOverrideCursor
(
QCursor
(
Qt
::
BlankCursor
));
m_originalPosition
=
QCursor
::
pos
();
m_window
->
installEventFilter
(
this
);
Q_EMIT
lockedChanged
(
true
);
Q_EMIT
lockEffectiveChanged
(
true
);
}
else
{
m_window
->
removeEventFilter
(
this
);
QGuiApplication
::
restoreOverrideCursor
();
Q_EMIT
lockedChanged
(
false
);
Q_EMIT
lockEffectiveChanged
(
false
);
}
}
bool
PointerLockerQt
::
isLocked
()
const
{
return
!
m_originalPosition
.
isNull
();
}
bool
PointerLockerQt
::
eventFilter
(
QObject
*
watched
,
QEvent
*
event
)
{
if
(
watched
!=
m_window
||
event
->
type
()
!=
QEvent
::
MouseMove
||
!
isLocked
())
{
return
false
;
}
const
auto
newPos
=
QCursor
::
pos
();
const
QPointF
dist
=
newPos
-
m_originalPosition
;
Q_EMIT
pointerMoved
({
dist
.
x
(),
dist
.
y
()
});
QCursor
::
setPos
(
m_originalPosition
);
return
true
;
}
declarativeplugin/pointerlocker.h
0 → 100644
View file @
cf8ada2b
/*
SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#ifndef POINTERLOCKER_H
#define POINTERLOCKER_H
#include <QObject>
#include <QWindow>
class
AbstractPointerLocker
:
public
QObject
{
Q_OBJECT
Q_PROPERTY
(
bool
isSupported
READ
isSupported
NOTIFY
supportedChanged
)
Q_PROPERTY
(
bool
isLocked
READ
isLocked
WRITE
setLocked
NOTIFY
lockedChanged
)
Q_PROPERTY
(
bool
isLockEffective
READ
isLockEffective
NOTIFY
lockEffectiveChanged
)
Q_PROPERTY
(
QWindow
*
window
READ
window
WRITE
setWindow
NOTIFY
windowChanged
)
public:
AbstractPointerLocker
(
QObject
*
parent
=
nullptr
)
:
QObject
(
parent
)
{}
virtual
void
setLocked
(
bool
locked
)
=
0
;
virtual
bool
isLocked
()
const
=
0
;
virtual
bool
isLockEffective
()
const
=
0
;
virtual
bool
isSupported
()
const
=
0
;
virtual
void
setWindow
(
QWindow
*
window
);
QWindow
*
window
()
const
{
return
m_window
;
}
Q_SIGNALS:
void
supportedChanged
(
bool
isSupported
);
void
lockedChanged
(
bool
isLocked
);
void
lockEffectiveChanged
(
bool
isLockEffective
);
void
windowChanged
();
void
pointerMoved
(
const
QPointF
&
delta
);
protected:
QWindow
*
m_window
=
nullptr
;
};
class
PointerLockerQt
:
public
AbstractPointerLocker
{
Q_OBJECT
public:
PointerLockerQt
(
QObject
*
parent
=
nullptr
);
~
PointerLockerQt
()
override
;
void
setLocked
(
bool
locked
)
override
;
bool
isLocked
()
const
override
;
bool
isSupported
()
const
override
{
return
true
;
}
bool
isLockEffective
()
const
override
{
return
isLocked
();
}
private:
bool
eventFilter
(
QObject
*
watched
,
QEvent
*
event
)
override
;
QPoint
m_originalPosition
;
bool
m_moving
=
false
;
};
#endif
declarativeplugin/pointerlockerwayland.cpp
0 → 100644
View file @
cf8ada2b
/*
SPDX-FileCopyrightText: 2018 Roman Gilg <subdiff@gmail.com>
SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "pointerlockerwayland.h"
#include <QDebug>
#include <KWayland/Client/compositor.h>
#include <KWayland/Client/connection_thread.h>
#include <KWayland/Client/pointer.h>
#include <KWayland/Client/pointerconstraints.h>
#include <KWayland/Client/registry.h>
#include <KWayland/Client/region.h>
#include <KWayland/Client/relativepointer.h>
#include <KWayland/Client/seat.h>
#include <KWayland/Client/surface.h>
using
namespace
KWayland
::
Client
;
PointerLockerWayland
::
PointerLockerWayland
(
QObject
*
parent
)
:
AbstractPointerLocker
(
parent
)
,
m_connectionThreadObject
(
ConnectionThread
::
fromApplication
(
this
))
{
setupRegistry
();
}
void
PointerLockerWayland
::
setupRegistry
()
{
Registry
*
registry
=
new
Registry
(
this
);
connect
(
registry
,
&
Registry
::
compositorAnnounced
,
this
,
[
this
,
registry
](
quint32
name
,
quint32
version
)
{
m_compositor
=
registry
->
createCompositor
(
name
,
version
,
this
);
}
);
connect
(
registry
,
&
Registry
::
relativePointerManagerUnstableV1Announced
,
this
,
[
this
,
registry
](
quint32
name
,
quint32
version
)
{
Q_ASSERT
(
!
m_relativePointerManager
);
m_relativePointerManager
=
registry
->
createRelativePointerManager
(
name
,
version
,
this
);
}
);
connect
(
registry
,
&
Registry
::
seatAnnounced
,
this
,
[
this
,
registry
](
quint32
name
,
quint32
version
)
{
m_seat
=
registry
->
createSeat
(
name
,
version
,
this
);
if
(
m_seat
->
hasPointer
())
{
m_pointer
=
m_seat
->
createPointer
(
this
);
}
connect
(
m_seat
,
&
Seat
::
hasPointerChanged
,
this
,
[
this
]
(
bool
hasPointer
)
{
delete
m_pointer
;
if
(
!
hasPointer
)
return
;
m_pointer
=
m_seat
->
createPointer
(
this
);
delete
m_relativePointer
;
m_relativePointer
=
m_relativePointerManager
->
createRelativePointer
(
m_pointer
,
this
);
connect
(
m_relativePointer
,
&
RelativePointer
::
relativeMotion
,
this
,
[
this
]
(
const
QSizeF
&
delta
)
{
Q_EMIT
pointerMoved
({
delta
.
width
(),
delta
.
height
()});
});
}
);
}
);
connect
(
registry
,
&
Registry
::
pointerConstraintsUnstableV1Announced
,
this
,
[
this
,
registry
](
quint32
name
,
quint32
version
)
{
m_pointerConstraints
=
registry
->
createPointerConstraints
(
name
,
version
,
this
);
}
);
connect
(
registry
,
&
Registry
::
interfacesAnnounced
,
this
,
[
this
]
{
Q_ASSERT
(
m_compositor
);
Q_ASSERT
(
m_seat
);
Q_ASSERT
(
m_pointerConstraints
);
}
);
registry
->
create
(
m_connectionThreadObject
);
registry
->
setup
();
}
bool
PointerLockerWayland
::
isLockEffective
()
const
{
return
m_lockedPointer
&&
m_lockedPointer
->
isValid
();
}
void
PointerLockerWayland
::
enforceLock
()
{
if
(
!
m_isLocked
)
{
return
;
}
QScopedPointer
<
Surface
>
winSurface
(
Surface
::
fromWindow
(
m_window
));
if
(
!
winSurface
)
{
qWarning
()
<<
"Locking a window that is not mapped"
;
return
;
}
auto
*
lockedPointer
=
m_pointerConstraints
->
lockPointer
(
winSurface
.
data
(),
m_pointer
,
nullptr
,
PointerConstraints
::
LifeTime
::
Persistent
,
this
);
if
(
!
lockedPointer
)
{
qDebug
()
<<
"ERROR when receiving locked pointer!"
;
return
;
}
m_lockedPointer
=
lockedPointer
;
connect
(
lockedPointer
,
&
LockedPointer
::
locked
,
this
,
[
this
]
{
Q_EMIT
lockEffectiveChanged
(
true
);
});
connect
(
lockedPointer
,
&
LockedPointer
::
unlocked
,
this
,
[
this
]
{
Q_EMIT
lockEffectiveChanged
(
false
);
});
}
void
PointerLockerWayland
::
setLocked
(
bool
lock
)
{
if
(
m_isLocked
==
lock
)
{
return
;
}
if
(
!
isSupported
())
{
qWarning
()
<<
"Locking before having our interfaces announced"
;
return
;
}
m_isLocked
=
lock
;
if
(
lock
)
{
enforceLock
();
}
else
{
cleanupLock
();
}
Q_EMIT
lockedChanged
(
lock
);
}
void
PointerLockerWayland
::
cleanupLock
()
{
if
(
!
m_lockedPointer
)
{
return
;
}
m_lockedPointer
->
release
();
m_lockedPointer
->
deleteLater
();
m_lockedPointer
=
nullptr
;
Q_EMIT
lockEffectiveChanged
(
false
);
}
void
PointerLockerWayland
::
setWindow
(
QWindow
*
window
)
{
if
(
m_window
==
window
)
{
return
;
}
cleanupLock
();
if
(
m_window
)
{
disconnect
(
m_window
,
&
QWindow
::
visibleChanged
,
this
,
&
PointerLockerWayland
::
enforceLock
);
}
AbstractPointerLocker
::
setWindow
(
window
);
connect
(
m_window
,
&
QWindow
::
visibleChanged
,
this
,
&
PointerLockerWayland
::
enforceLock
);
if
(
m_isLocked
)
{
enforceLock
();
}
}
declarativeplugin/pointerlockerwayland.h
0 → 100644
View file @
cf8ada2b
/*
SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#ifndef POINTERLOCKERWAYLAND_H
#define POINTERLOCKERWAYLAND_H
#include "pointerlocker.h"
namespace
KWayland
{
namespace
Client
{
class
ConnectionThread
;
class
Registry
;
class
Compositor
;
class
Seat
;
class
Pointer
;
class
PointerConstraints
;
class
LockedPointer
;
class
ConfinedPointer
;
class
RelativePointer
;
class
RelativePointerManager
;
}
}
class
PointerLockerWayland
:
public
AbstractPointerLocker
{
Q_OBJECT
public:
PointerLockerWayland
(
QObject
*
parent
=
nullptr
);
void
setLocked
(
bool
locked
)
override
;
bool
isLocked
()
const
override
{
return
m_isLocked
;
}
bool
isLockEffective
()
const
override
;
bool
isSupported
()
const
override
{
return
m_pointerConstraints
&&
m_relativePointerManager
;
}
void
setWindow
(
QWindow
*
window
)
override
;
private:
void
setupRegistry
();
void
enforceLock
();
void
cleanupLock
();
bool
m_isLocked
=
false
;
KWayland
::
Client
::
ConnectionThread
*
m_connectionThreadObject
;
KWayland
::
Client
::
Compositor
*
m_compositor
=
nullptr
;
KWayland
::
Client
::
Seat
*
m_seat
=
nullptr
;
KWayland
::
Client
::
Pointer
*
m_pointer
=
nullptr
;
KWayland
::
Client
::
PointerConstraints
*
m_pointerConstraints
=
nullptr
;
KWayland
::
Client
::
RelativePointer
*
m_relativePointer
=
nullptr
;
KWayland
::
Client
::
RelativePointerManager
*
m_relativePointerManager
=
nullptr
;
KWayland
::
Client
::
LockedPointer
*
m_lockedPointer
=
nullptr
;
};
#endif
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment