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.
|
||||
effects->ungrabKeyboard();
|
||||
effects->stopMouseInterception(this);
|
||||
|
||||
hideCursor();
|
||||
}
|
||||
|
||||
void BreezyDesktopEffect::deactivate()
|
||||
|
|
@ -392,6 +390,14 @@ bool BreezyDesktopEffect::isEnabled() const {
|
|||
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 {
|
||||
return m_zoomOnFocusEnabled;
|
||||
}
|
||||
|
|
@ -572,7 +578,6 @@ bool BreezyDesktopEffect::checkParityByte(const char* data) {
|
|||
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 activatedAt = 0;
|
||||
void BreezyDesktopEffect::updateImuRotation() {
|
||||
|
|
@ -786,13 +791,19 @@ QPointF BreezyDesktopEffect::cursorPos() const
|
|||
|
||||
void BreezyDesktopEffect::showCursor()
|
||||
{
|
||||
if (!m_cursorHidden) return;
|
||||
|
||||
effects->showCursor();
|
||||
m_cursorHidden = false;
|
||||
}
|
||||
|
||||
void BreezyDesktopEffect::hideCursor()
|
||||
{
|
||||
if (m_cursorHidden) return;
|
||||
|
||||
updateCursorImage();
|
||||
effects->hideCursor();
|
||||
m_cursorHidden = true;
|
||||
}
|
||||
|
||||
void BreezyDesktopEffect::updateCursorImage()
|
||||
|
|
@ -810,6 +821,8 @@ void BreezyDesktopEffect::updateCursorImage()
|
|||
m_cursorImageSource = QString();
|
||||
m_cursorImageSize = QSize();
|
||||
}
|
||||
// Cursor size affects the expanded geometry margin; invalidate cache.
|
||||
invalidateEffectOnScreenGeometryCache();
|
||||
Q_EMIT cursorImageSourceChanged();
|
||||
}
|
||||
|
||||
|
|
@ -819,11 +832,56 @@ void BreezyDesktopEffect::updateCursorPos()
|
|||
const auto cursor = effects->cursorImage();
|
||||
QPointF newPos = effects->cursorPos() - cursor.hotSpot();
|
||||
if (m_cursorPos != newPos) {
|
||||
const QPointF prevPos = m_cursorPos;
|
||||
m_cursorPos = newPos;
|
||||
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)
|
||||
{
|
||||
if (!output) {
|
||||
|
|
@ -832,6 +890,9 @@ void BreezyDesktopEffect::warpPointerToOutputCenter(Output *output)
|
|||
const QRect geometry = output->geometry();
|
||||
const QPointF center = geometry.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()
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <QVariant>
|
||||
#include <QVariantList>
|
||||
#include <QHash>
|
||||
#include <QRect>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
|
@ -18,6 +19,7 @@ namespace KWin
|
|||
{
|
||||
Q_OBJECT
|
||||
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(int lookingAtScreenIndex READ lookingAtScreenIndex WRITE setLookingAtScreenIndex)
|
||||
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 curvedDisplaySupported READ curvedDisplaySupported WRITE setCurvedDisplaySupported NOTIFY curvedDisplaySupportedChanged)
|
||||
|
||||
|
||||
public:
|
||||
|
||||
BreezyDesktopEffect();
|
||||
|
|
@ -62,6 +65,8 @@ namespace KWin
|
|||
QPointF cursorPos() const;
|
||||
|
||||
bool isEnabled() const;
|
||||
int effectTargetScreenIndex() const { return m_effectTargetScreenIndex; }
|
||||
void setEffectTargetScreenIndex(int index);
|
||||
bool isZoomOnFocusEnabled() const;
|
||||
void setZoomOnFocusEnabled(bool enabled);
|
||||
int lookingAtScreenIndex() const { return m_lookingAtScreenIndex; }
|
||||
|
|
@ -147,6 +152,9 @@ namespace KWin
|
|||
void setSmoothFollowThreshold(float threshold);
|
||||
void updateDriverSmoothFollowSettings();
|
||||
void warpPointerToOutputCenter(Output *output);
|
||||
void evaluateCursorOnScreenState(const QPointF &prevPos, const QPointF &newPos);
|
||||
void invalidateEffectOnScreenGeometryCache();
|
||||
bool updateEffectOnScreenGeometryCache();
|
||||
|
||||
QString m_cursorImageSource;
|
||||
QSize m_cursorImageSize;
|
||||
|
|
@ -154,6 +162,7 @@ namespace KWin
|
|||
bool m_enabled = false;
|
||||
bool m_zoomOnFocusEnabled = false;
|
||||
int m_lookingAtScreenIndex = -1;
|
||||
int m_effectTargetScreenIndex = -1;
|
||||
bool m_imuResetState;
|
||||
QList<QQuaternion> m_imuRotations;
|
||||
quint32 m_imuTimeElapsedMs;
|
||||
|
|
@ -169,6 +178,7 @@ namespace KWin
|
|||
bool m_customBannerEnabled;
|
||||
QFileSystemWatcher *m_shmFileWatcher = nullptr;
|
||||
QFileSystemWatcher *m_shmDirectoryWatcher = nullptr;
|
||||
bool m_cursorHidden = false;
|
||||
QPointF m_cursorPos;
|
||||
QTimer *m_cursorUpdateTimer = nullptr;
|
||||
qreal m_focusedDisplayDistance = 0.85;
|
||||
|
|
@ -186,6 +196,10 @@ namespace KWin
|
|||
bool m_allDisplaysFollowMode = false;
|
||||
bool m_focusedSmoothFollowEnabled = false;
|
||||
|
||||
// Cached geometry for on-screen cursor evaluation
|
||||
QRect m_effectOnScreenExpandedGeometry;
|
||||
bool m_effectOnScreenGeometryValid = false;
|
||||
|
||||
struct VirtualOutputInfo {
|
||||
Output *output = nullptr;
|
||||
QString id;
|
||||
|
|
|
|||
|
|
@ -2,33 +2,15 @@ import QtQuick
|
|||
|
||||
Item {
|
||||
id: singleDesktopView
|
||||
property point cursorPos: effect.cursorPos
|
||||
property bool supportsXR: 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 {
|
||||
id: desktopViewComponent
|
||||
screen: targetScreen
|
||||
width: targetScreen.geometry.width
|
||||
height: targetScreen.geometry.height
|
||||
}
|
||||
|
||||
Image {
|
||||
id: cursorImg
|
||||
x: 0
|
||||
y: 0
|
||||
z: 9999 // ensure on top
|
||||
}
|
||||
|
||||
Image {
|
||||
source: effect.customBannerEnabled ? "custom_banner.png" : "calibrating.png"
|
||||
|
|
@ -36,18 +18,4 @@ Item {
|
|||
anchors.horizontalCenter: desktopViewComponent.horizontalCenter
|
||||
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}`);
|
||||
const show3DView = targetScreenSupported && isEnabled && !imuResetState;
|
||||
if (!targetScreenIsVirtual) viewLoader.sourceComponent = show3DView ? view3DComponent : desktopViewComponent;
|
||||
if (targetScreenSupported) effect.effectTargetScreenIndex = KWinComponents.Workspace.screens.indexOf(targetScreen);
|
||||
}
|
||||
|
||||
onImuResetStateChanged: {
|
||||
|
|
|
|||
Loading…
Reference in New Issue