Improve cursor interaction so that physical displays use native cursor rendering
This commit is contained in:
parent
4a756d63a5
commit
fc6858d535
|
|
@ -291,8 +291,6 @@ void BreezyDesktopEffect::activate()
|
||||||
// and doesn't allow for interaction with anything on the desktop. These two calls fix that.
|
// and doesn't allow for interaction with anything on the desktop. These two calls fix that.
|
||||||
effects->ungrabKeyboard();
|
effects->ungrabKeyboard();
|
||||||
effects->stopMouseInterception(this);
|
effects->stopMouseInterception(this);
|
||||||
|
|
||||||
hideCursor();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BreezyDesktopEffect::deactivate()
|
void BreezyDesktopEffect::deactivate()
|
||||||
|
|
@ -392,6 +390,14 @@ bool BreezyDesktopEffect::isEnabled() const {
|
||||||
return m_enabled;
|
return m_enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BreezyDesktopEffect::setEffectTargetScreenIndex(int index) {
|
||||||
|
if (m_effectTargetScreenIndex != index) {
|
||||||
|
m_effectTargetScreenIndex = index;
|
||||||
|
invalidateEffectOnScreenGeometryCache();
|
||||||
|
evaluateCursorOnScreenState(m_cursorPos, m_cursorPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool BreezyDesktopEffect::isZoomOnFocusEnabled() const {
|
bool BreezyDesktopEffect::isZoomOnFocusEnabled() const {
|
||||||
return m_zoomOnFocusEnabled;
|
return m_zoomOnFocusEnabled;
|
||||||
}
|
}
|
||||||
|
|
@ -572,7 +578,6 @@ bool BreezyDesktopEffect::checkParityByte(const char* data) {
|
||||||
return parityByte == parity;
|
return parityByte == parity;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO - can this be something callable from the camera qml code, so it's pulled only when needed?
|
|
||||||
static qint64 lastConfigUpdate = 0;
|
static qint64 lastConfigUpdate = 0;
|
||||||
static qint64 activatedAt = 0;
|
static qint64 activatedAt = 0;
|
||||||
void BreezyDesktopEffect::updateImuRotation() {
|
void BreezyDesktopEffect::updateImuRotation() {
|
||||||
|
|
@ -786,13 +791,19 @@ QPointF BreezyDesktopEffect::cursorPos() const
|
||||||
|
|
||||||
void BreezyDesktopEffect::showCursor()
|
void BreezyDesktopEffect::showCursor()
|
||||||
{
|
{
|
||||||
|
if (!m_cursorHidden) return;
|
||||||
|
|
||||||
effects->showCursor();
|
effects->showCursor();
|
||||||
|
m_cursorHidden = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BreezyDesktopEffect::hideCursor()
|
void BreezyDesktopEffect::hideCursor()
|
||||||
{
|
{
|
||||||
|
if (m_cursorHidden) return;
|
||||||
|
|
||||||
updateCursorImage();
|
updateCursorImage();
|
||||||
effects->hideCursor();
|
effects->hideCursor();
|
||||||
|
m_cursorHidden = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BreezyDesktopEffect::updateCursorImage()
|
void BreezyDesktopEffect::updateCursorImage()
|
||||||
|
|
@ -810,6 +821,8 @@ void BreezyDesktopEffect::updateCursorImage()
|
||||||
m_cursorImageSource = QString();
|
m_cursorImageSource = QString();
|
||||||
m_cursorImageSize = QSize();
|
m_cursorImageSize = QSize();
|
||||||
}
|
}
|
||||||
|
// Cursor size affects the expanded geometry margin; invalidate cache.
|
||||||
|
invalidateEffectOnScreenGeometryCache();
|
||||||
Q_EMIT cursorImageSourceChanged();
|
Q_EMIT cursorImageSourceChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -819,11 +832,56 @@ void BreezyDesktopEffect::updateCursorPos()
|
||||||
const auto cursor = effects->cursorImage();
|
const auto cursor = effects->cursorImage();
|
||||||
QPointF newPos = effects->cursorPos() - cursor.hotSpot();
|
QPointF newPos = effects->cursorPos() - cursor.hotSpot();
|
||||||
if (m_cursorPos != newPos) {
|
if (m_cursorPos != newPos) {
|
||||||
|
const QPointF prevPos = m_cursorPos;
|
||||||
m_cursorPos = newPos;
|
m_cursorPos = newPos;
|
||||||
Q_EMIT cursorPosChanged();
|
Q_EMIT cursorPosChanged();
|
||||||
|
|
||||||
|
evaluateCursorOnScreenState(prevPos, m_cursorPos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BreezyDesktopEffect::evaluateCursorOnScreenState(const QPointF &prevPos, const QPointF &newPos)
|
||||||
|
{
|
||||||
|
if (!updateEffectOnScreenGeometryCache()) return;
|
||||||
|
|
||||||
|
const QPointF velocity = newPos - prevPos;
|
||||||
|
const QPointF predicted = newPos + velocity;
|
||||||
|
|
||||||
|
const bool onScreen =
|
||||||
|
m_effectOnScreenExpandedGeometry.contains(newPos.toPoint()) ||
|
||||||
|
m_effectOnScreenExpandedGeometry.contains(predicted.toPoint());
|
||||||
|
if (!m_cursorHidden && onScreen) {
|
||||||
|
hideCursor();
|
||||||
|
} else if (m_enabled && !m_imuResetState && m_cursorHidden && !onScreen) {
|
||||||
|
showCursor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BreezyDesktopEffect::invalidateEffectOnScreenGeometryCache()
|
||||||
|
{
|
||||||
|
m_effectOnScreenGeometryValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BreezyDesktopEffect::updateEffectOnScreenGeometryCache()
|
||||||
|
{
|
||||||
|
if (m_effectOnScreenGeometryValid)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (m_effectTargetScreenIndex == -1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Output *effectOnScreen = effects->screens().at(m_effectTargetScreenIndex);
|
||||||
|
if (!effectOnScreen)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const QRect geometry = effectOnScreen->geometry();
|
||||||
|
const int marginX = (m_cursorImageSize.width() > 0) ? m_cursorImageSize.width() : 10;
|
||||||
|
const int marginY = (m_cursorImageSize.height() > 0) ? m_cursorImageSize.height() : 10;
|
||||||
|
m_effectOnScreenExpandedGeometry = geometry.adjusted(-marginX, -marginY, marginX, marginY);
|
||||||
|
m_effectOnScreenGeometryValid = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void BreezyDesktopEffect::warpPointerToOutputCenter(Output *output)
|
void BreezyDesktopEffect::warpPointerToOutputCenter(Output *output)
|
||||||
{
|
{
|
||||||
if (!output) {
|
if (!output) {
|
||||||
|
|
@ -832,6 +890,9 @@ void BreezyDesktopEffect::warpPointerToOutputCenter(Output *output)
|
||||||
const QRect geometry = output->geometry();
|
const QRect geometry = output->geometry();
|
||||||
const QPointF center = geometry.center();
|
const QPointF center = geometry.center();
|
||||||
Cursors::self()->mouse()->setPos(center);
|
Cursors::self()->mouse()->setPos(center);
|
||||||
|
|
||||||
|
// When warping, we don't have a meaningful previous position; use center for both.
|
||||||
|
evaluateCursorOnScreenState(center, center);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BreezyDesktopEffect::moveCursorToFocusedDisplay()
|
void BreezyDesktopEffect::moveCursorToFocusedDisplay()
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QVariantList>
|
#include <QVariantList>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
#include <QRect>
|
||||||
|
|
||||||
namespace KWin
|
namespace KWin
|
||||||
{
|
{
|
||||||
|
|
@ -18,6 +19,7 @@ namespace KWin
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(bool isEnabled READ isEnabled NOTIFY enabledStateChanged)
|
Q_PROPERTY(bool isEnabled READ isEnabled NOTIFY enabledStateChanged)
|
||||||
|
Q_PROPERTY(int effectTargetScreenIndex READ effectTargetScreenIndex WRITE setEffectTargetScreenIndex)
|
||||||
Q_PROPERTY(bool zoomOnFocusEnabled READ isZoomOnFocusEnabled WRITE setZoomOnFocusEnabled NOTIFY zoomOnFocusChanged)
|
Q_PROPERTY(bool zoomOnFocusEnabled READ isZoomOnFocusEnabled WRITE setZoomOnFocusEnabled NOTIFY zoomOnFocusChanged)
|
||||||
Q_PROPERTY(int lookingAtScreenIndex READ lookingAtScreenIndex WRITE setLookingAtScreenIndex)
|
Q_PROPERTY(int lookingAtScreenIndex READ lookingAtScreenIndex WRITE setLookingAtScreenIndex)
|
||||||
Q_PROPERTY(bool imuResetState READ imuResetState NOTIFY imuResetStateChanged)
|
Q_PROPERTY(bool imuResetState READ imuResetState NOTIFY imuResetStateChanged)
|
||||||
|
|
@ -48,6 +50,7 @@ namespace KWin
|
||||||
Q_PROPERTY(bool curvedDisplay READ curvedDisplay NOTIFY curvedDisplayChanged)
|
Q_PROPERTY(bool curvedDisplay READ curvedDisplay NOTIFY curvedDisplayChanged)
|
||||||
Q_PROPERTY(bool curvedDisplaySupported READ curvedDisplaySupported WRITE setCurvedDisplaySupported NOTIFY curvedDisplaySupportedChanged)
|
Q_PROPERTY(bool curvedDisplaySupported READ curvedDisplaySupported WRITE setCurvedDisplaySupported NOTIFY curvedDisplaySupportedChanged)
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
BreezyDesktopEffect();
|
BreezyDesktopEffect();
|
||||||
|
|
@ -62,6 +65,8 @@ namespace KWin
|
||||||
QPointF cursorPos() const;
|
QPointF cursorPos() const;
|
||||||
|
|
||||||
bool isEnabled() const;
|
bool isEnabled() const;
|
||||||
|
int effectTargetScreenIndex() const { return m_effectTargetScreenIndex; }
|
||||||
|
void setEffectTargetScreenIndex(int index);
|
||||||
bool isZoomOnFocusEnabled() const;
|
bool isZoomOnFocusEnabled() const;
|
||||||
void setZoomOnFocusEnabled(bool enabled);
|
void setZoomOnFocusEnabled(bool enabled);
|
||||||
int lookingAtScreenIndex() const { return m_lookingAtScreenIndex; }
|
int lookingAtScreenIndex() const { return m_lookingAtScreenIndex; }
|
||||||
|
|
@ -147,6 +152,9 @@ namespace KWin
|
||||||
void setSmoothFollowThreshold(float threshold);
|
void setSmoothFollowThreshold(float threshold);
|
||||||
void updateDriverSmoothFollowSettings();
|
void updateDriverSmoothFollowSettings();
|
||||||
void warpPointerToOutputCenter(Output *output);
|
void warpPointerToOutputCenter(Output *output);
|
||||||
|
void evaluateCursorOnScreenState(const QPointF &prevPos, const QPointF &newPos);
|
||||||
|
void invalidateEffectOnScreenGeometryCache();
|
||||||
|
bool updateEffectOnScreenGeometryCache();
|
||||||
|
|
||||||
QString m_cursorImageSource;
|
QString m_cursorImageSource;
|
||||||
QSize m_cursorImageSize;
|
QSize m_cursorImageSize;
|
||||||
|
|
@ -154,6 +162,7 @@ namespace KWin
|
||||||
bool m_enabled = false;
|
bool m_enabled = false;
|
||||||
bool m_zoomOnFocusEnabled = false;
|
bool m_zoomOnFocusEnabled = false;
|
||||||
int m_lookingAtScreenIndex = -1;
|
int m_lookingAtScreenIndex = -1;
|
||||||
|
int m_effectTargetScreenIndex = -1;
|
||||||
bool m_imuResetState;
|
bool m_imuResetState;
|
||||||
QList<QQuaternion> m_imuRotations;
|
QList<QQuaternion> m_imuRotations;
|
||||||
quint32 m_imuTimeElapsedMs;
|
quint32 m_imuTimeElapsedMs;
|
||||||
|
|
@ -169,6 +178,7 @@ namespace KWin
|
||||||
bool m_customBannerEnabled;
|
bool m_customBannerEnabled;
|
||||||
QFileSystemWatcher *m_shmFileWatcher = nullptr;
|
QFileSystemWatcher *m_shmFileWatcher = nullptr;
|
||||||
QFileSystemWatcher *m_shmDirectoryWatcher = nullptr;
|
QFileSystemWatcher *m_shmDirectoryWatcher = nullptr;
|
||||||
|
bool m_cursorHidden = false;
|
||||||
QPointF m_cursorPos;
|
QPointF m_cursorPos;
|
||||||
QTimer *m_cursorUpdateTimer = nullptr;
|
QTimer *m_cursorUpdateTimer = nullptr;
|
||||||
qreal m_focusedDisplayDistance = 0.85;
|
qreal m_focusedDisplayDistance = 0.85;
|
||||||
|
|
@ -186,6 +196,10 @@ namespace KWin
|
||||||
bool m_allDisplaysFollowMode = false;
|
bool m_allDisplaysFollowMode = false;
|
||||||
bool m_focusedSmoothFollowEnabled = false;
|
bool m_focusedSmoothFollowEnabled = false;
|
||||||
|
|
||||||
|
// Cached geometry for on-screen cursor evaluation
|
||||||
|
QRect m_effectOnScreenExpandedGeometry;
|
||||||
|
bool m_effectOnScreenGeometryValid = false;
|
||||||
|
|
||||||
struct VirtualOutputInfo {
|
struct VirtualOutputInfo {
|
||||||
Output *output = nullptr;
|
Output *output = nullptr;
|
||||||
QString id;
|
QString id;
|
||||||
|
|
|
||||||
|
|
@ -2,33 +2,15 @@ import QtQuick
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: singleDesktopView
|
id: singleDesktopView
|
||||||
property point cursorPos: effect.cursorPos
|
|
||||||
property bool supportsXR: false
|
property bool supportsXR: false
|
||||||
property bool showCalibratingBanner: false
|
property bool showCalibratingBanner: false
|
||||||
|
|
||||||
function cursorInBounds() {
|
|
||||||
const x = cursorPos.x
|
|
||||||
const y = cursorPos.y
|
|
||||||
const screenGeom = targetScreen.geometry
|
|
||||||
return x >= screenGeom.x &&
|
|
||||||
x < screenGeom.x + screenGeom.width &&
|
|
||||||
y >= screenGeom.y &&
|
|
||||||
y < screenGeom.y + screenGeom.height
|
|
||||||
}
|
|
||||||
|
|
||||||
DesktopView {
|
DesktopView {
|
||||||
id: desktopViewComponent
|
id: desktopViewComponent
|
||||||
screen: targetScreen
|
screen: targetScreen
|
||||||
width: targetScreen.geometry.width
|
width: targetScreen.geometry.width
|
||||||
height: targetScreen.geometry.height
|
height: targetScreen.geometry.height
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
|
||||||
id: cursorImg
|
|
||||||
x: 0
|
|
||||||
y: 0
|
|
||||||
z: 9999 // ensure on top
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
source: effect.customBannerEnabled ? "custom_banner.png" : "calibrating.png"
|
source: effect.customBannerEnabled ? "custom_banner.png" : "calibrating.png"
|
||||||
|
|
@ -36,18 +18,4 @@ Item {
|
||||||
anchors.horizontalCenter: desktopViewComponent.horizontalCenter
|
anchors.horizontalCenter: desktopViewComponent.horizontalCenter
|
||||||
anchors.bottom: desktopViewComponent.bottom
|
anchors.bottom: desktopViewComponent.bottom
|
||||||
}
|
}
|
||||||
|
|
||||||
onCursorPosChanged: {
|
|
||||||
if (singleDesktopView.cursorInBounds()) {
|
|
||||||
const newX = effect.cursorPos.x - targetScreen.geometry.x
|
|
||||||
const newY = effect.cursorPos.y - targetScreen.geometry.y
|
|
||||||
const newSrc = effect.cursorImageSource
|
|
||||||
if (cursorImg.x !== newX) cursorImg.x = newX
|
|
||||||
if (cursorImg.y !== newY) cursorImg.y = newY
|
|
||||||
if (cursorImg.source !== newSrc) cursorImg.source = newSrc
|
|
||||||
if (!cursorImg.visible) cursorImg.visible = true
|
|
||||||
} else if (cursorImg.visible) {
|
|
||||||
cursorImg.visible = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -140,6 +140,7 @@ Item {
|
||||||
console.log(`Breezy - checking screen ${targetScreen.model}: ${targetScreenSupported} ${targetScreenIsVirtual} ${isEnabled} ${imuResetState}`);
|
console.log(`Breezy - checking screen ${targetScreen.model}: ${targetScreenSupported} ${targetScreenIsVirtual} ${isEnabled} ${imuResetState}`);
|
||||||
const show3DView = targetScreenSupported && isEnabled && !imuResetState;
|
const show3DView = targetScreenSupported && isEnabled && !imuResetState;
|
||||||
if (!targetScreenIsVirtual) viewLoader.sourceComponent = show3DView ? view3DComponent : desktopViewComponent;
|
if (!targetScreenIsVirtual) viewLoader.sourceComponent = show3DView ? view3DComponent : desktopViewComponent;
|
||||||
|
if (targetScreenSupported) effect.effectTargetScreenIndex = KWinComponents.Workspace.screens.indexOf(targetScreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
onImuResetStateChanged: {
|
onImuResetStateChanged: {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue