Fix a couple smooth follow issues

* Always fully center the focused screen
* Properly rebind the rotation of the screen so it doesn't get frozen to
  an old one when changing wrap scheme
This commit is contained in:
wheaney 2025-09-21 23:10:14 -07:00
parent 19bacf2b92
commit 2bcc2e07e3
4 changed files with 43 additions and 36 deletions

View File

@ -149,7 +149,7 @@ namespace KWin
bool m_enabled = false; bool m_enabled = false;
bool m_zoomOnFocusEnabled = false; bool m_zoomOnFocusEnabled = false;
int m_lookingAtScreenIndex = -1; // -1 means disabled/unknown int m_lookingAtScreenIndex = -1;
bool m_imuResetState; bool m_imuResetState;
QList<QQuaternion> m_imuRotations; QList<QQuaternion> m_imuRotations;
quint32 m_imuTimeElapsedMs; quint32 m_imuTimeElapsedMs;

View File

@ -463,7 +463,8 @@ void BreezyDesktopEffectConfig::updateUiFromConfig()
ui.kcfg_RemoveVirtualDisplaysOnDisable->setChecked(BreezyDesktopConfig::self()->removeVirtualDisplaysOnDisable()); ui.kcfg_RemoveVirtualDisplaysOnDisable->setChecked(BreezyDesktopConfig::self()->removeVirtualDisplaysOnDisable());
ui.kcfg_AllDisplaysFollowMode->setChecked(BreezyDesktopConfig::self()->allDisplaysFollowMode()); ui.kcfg_AllDisplaysFollowMode->setChecked(BreezyDesktopConfig::self()->allDisplaysFollowMode());
ui.kcfg_ZoomOnFocusEnabled->setChecked(BreezyDesktopConfig::self()->zoomOnFocusEnabled()); ui.kcfg_ZoomOnFocusEnabled->setChecked(BreezyDesktopConfig::self()->zoomOnFocusEnabled());
ui.kcfg_FocusedDisplayDistance->setEnabled(ui.kcfg_ZoomOnFocusEnabled->isChecked()); ui.kcfg_FocusedDisplayDistance->setEnabled(
ui.kcfg_ZoomOnFocusEnabled->isChecked() || ui.SmoothFollowEnabled->isChecked());
ui.kcfg_SmoothFollowThreshold->setValue(BreezyDesktopConfig::self()->smoothFollowThreshold()); ui.kcfg_SmoothFollowThreshold->setValue(BreezyDesktopConfig::self()->smoothFollowThreshold());
} }

View File

@ -29,9 +29,7 @@ Node {
const rotations = smoothFollowEnabled ? effect.smoothFollowOrigin : effect.imuRotations; const rotations = smoothFollowEnabled ? effect.smoothFollowOrigin : effect.imuRotations;
if (rotations && rotations.length > 0) { if (rotations && rotations.length > 0) {
let focusedIndex = -1; let focusedIndex = -1;
let lookingAtIndex = -1; const lookingAtIndex = displays.findFocusedMonitor(
lookingAtIndex = displays.findFocusedMonitor(
displays.eusToNwuQuat(rotations[0]), displays.eusToNwuQuat(rotations[0]),
breezyDesktop.monitorPlacements.map(monitorVectors => monitorVectors.centerLook), breezyDesktop.monitorPlacements.map(monitorVectors => monitorVectors.centerLook),
breezyDesktop.focusedMonitorIndex, breezyDesktop.focusedMonitorIndex,
@ -57,26 +55,31 @@ Node {
let targetProgress; let targetProgress;
if (smoothFollowEnabled && focusedIndex !== -1) { if (smoothFollowEnabled && focusedIndex !== -1) {
focusedDisplay = breezyDesktop.displayAtIndex(focusedIndex); focusedDisplay = breezyDesktop.displayAtIndex(focusedIndex);
targetDisplay = focusedDisplay; if (focusedDisplay !== null) {
targetProgress = 1.0; targetDisplay = focusedDisplay;
startSmoothFollowFocusAnimation = true; targetProgress = 1.0;
startSmoothFollowFocusAnimation = true;
}
} else if (!smoothFollowEnabled && breezyDesktop.focusedMonitorIndex !== -1) { } else if (!smoothFollowEnabled && breezyDesktop.focusedMonitorIndex !== -1) {
unfocusedDisplay = breezyDesktop.displayAtIndex(breezyDesktop.focusedMonitorIndex); unfocusedDisplay = breezyDesktop.displayAtIndex(breezyDesktop.focusedMonitorIndex);
targetDisplay = unfocusedDisplay; if (unfocusedDisplay !== null) {
targetProgress = 0.0; targetDisplay = unfocusedDisplay;
targetProgress = 0.0;
}
}
if (targetDisplay !== null) {
smoothFollowTransitionAnimation.stop();
smoothFollowTransitionAnimation.target = targetDisplay;
smoothFollowTransitionAnimation.from = targetDisplay.smoothFollowTransitionProgress;
smoothFollowTransitionAnimation.to = targetProgress;
smoothFollowTransitionAnimation.start();
} }
smoothFollowTransitionAnimation.stop();
smoothFollowTransitionAnimation.target = targetDisplay;
smoothFollowTransitionAnimation.from = targetDisplay.smoothFollowTransitionProgress;
smoothFollowTransitionAnimation.to = targetProgress;
smoothFollowTransitionAnimation.start();
} }
if (focusedIndex !== breezyDesktop.focusedMonitorIndex) { if (focusedIndex !== breezyDesktop.focusedMonitorIndex) {
const unfocusedIndex = breezyDesktop.focusedMonitorIndex; const unfocusedIndex = breezyDesktop.focusedMonitorIndex;
if (!focusedDisplay) focusedDisplay = focusedIndex !== -1 ? breezyDesktop.displayAtIndex(focusedIndex) : null; if (!focusedDisplay) focusedDisplay = focusedIndex !== -1 ? breezyDesktop.displayAtIndex(focusedIndex) : null;
const allDisplaysDistanceBinding = Qt.binding(function() { return effect.allDisplaysDistance; });
const focusedDisplayDistanceBinding = Qt.binding(function() { return effect.focusedDisplayDistance; });
if (focusedDisplay === null) { if (focusedDisplay === null) {
if (!unfocusedDisplay) unfocusedDisplay = breezyDesktop.displayAtIndex(unfocusedIndex); if (!unfocusedDisplay) unfocusedDisplay = breezyDesktop.displayAtIndex(unfocusedIndex);
zoomOutAnimation.target = unfocusedDisplay; zoomOutAnimation.target = unfocusedDisplay;
@ -105,16 +108,12 @@ Node {
} }
} }
// monitorPlacement assumed to be present function displayRotationVector(display) {
function displayEusVector(display) {
const displayNwu = const displayNwu =
display.monitorPlacement.centerNoRotate display.monitorPlacement.centerNoRotate
.times(display.monitorDistance / effect.allDisplaysDistance); .times(display.monitorDistance / effect.allDisplaysDistance);
return displays.nwuToEusVector(displayNwu); const eusVector = displays.nwuToEusVector(displayNwu)
}
function displayRotationVector(display, eusVector) {
return display.rotationMatrix.times(eusVector); return display.rotationMatrix.times(eusVector);
} }
@ -125,23 +124,27 @@ Node {
return effect.smoothFollowOrigin[0].times(effect.imuRotations[0].conjugated()); return effect.smoothFollowOrigin[0].times(effect.imuRotations[0].conjugated());
} }
function displaySmoothFollowVector(display, eusVector) { function displaySmoothFollowVector(display, smoothFollowRotation) {
return smoothFollowQuat().times(eusVector); // for smooth follow, place the display centered directly in front of the camera
const displayDistanceNorth =
display.monitorPlacement.monitorCenterNorth *
display.monitorDistance / effect.allDisplaysDistance;
const eusVector = Qt.vector3d(0, 0, -displayDistanceNorth);
return smoothFollowRotation.times(eusVector);
} }
// don't call this from the delegate to avoid binding the position property to the effect properties // don't call this from the delegate to avoid binding the position property to the effect properties
// used for smooth follow // used for smooth follow
function displayPosition(display, smoothFollowRotation) { function displayPosition(display, smoothFollowRotation) {
const displayEus = displayEusVector(display);
// short circuit to avoid slerping if not needed // short circuit to avoid slerping if not needed
if (display.smoothFollowTransitionProgress === 1.0) { if (display.smoothFollowTransitionProgress === 1.0) {
return displaySmoothFollowVector(display, displayEus, smoothFollowRotation); return displaySmoothFollowVector(display, smoothFollowRotation);
} }
const finalPosition = displays.slerpVector( const finalPosition = displays.slerpVector(
displayRotationVector(display, displayEus), displayRotationVector(display),
displaySmoothFollowVector(display, displayEus, smoothFollowRotation), displaySmoothFollowVector(display, smoothFollowRotation),
display.smoothFollowTransitionProgress display.smoothFollowTransitionProgress
); );
@ -173,7 +176,7 @@ Node {
position: { position: {
if (!monitorPlacement) return Qt.vector3d(0, 0, 0); if (!monitorPlacement) return Qt.vector3d(0, 0, 0);
return displayRotationVector(this, displayEusVector(this)); return displayRotationVector(this);
} }
} }
} }
@ -208,17 +211,17 @@ Node {
focusedDisplay.position = displayPosition(focusedDisplay, smoothFollowRotation); focusedDisplay.position = displayPosition(focusedDisplay, smoothFollowRotation);
} else { } else {
focusedDisplay.rotation = Qt.quaternion(1, 0, 0, 0); focusedDisplay.rotation = Qt.quaternion(1, 0, 0, 0);
focusedDisplay.eulerRotation.x = focusedDisplay.screenRotationX; focusedDisplay.eulerRotation.x = Qt.binding(function() { return focusedDisplay.screenRotationX; });
focusedDisplay.eulerRotation.y = focusedDisplay.screenRotationY; focusedDisplay.eulerRotation.y = Qt.binding(function() { return focusedDisplay.screenRotationY; });
focusedDisplay.eulerRotation.z = 0.0; focusedDisplay.eulerRotation.z = 0.0;
// When smooth follow is done, this frame animation will no longer run so we need to // When smooth follow is done, this frame animation will no longer run so we need to
// rebind a safe function to the position property that will automatically update the // rebind a safe function to the position property that will automatically update the
// position when delegate properties change. display properties don't often change, // position when delegate properties change. display properties don't often change,
// but zoomOnFocus does change monitorDistance, so we need the binding to pick that up. // but zoomOnFocus does change monitorDistance, so we need the binding to pick that up.
focusedDisplay.position = Qt.binding(function() { focusedDisplay.position = Qt.binding(function() {
return displayRotationVector(this, displayEusVector(this)); return displayRotationVector(this);
}.bind(focusedDisplay) ); }.bind(focusedDisplay));
} }
} }

View File

@ -219,6 +219,7 @@ QtObject {
monitorPlacements.push({ monitorPlacements.push({
originalIndex: originalIndex, originalIndex: originalIndex,
monitorCenterNorth: monitorCenterRadius,
centerNoRotate: Qt.vector3d( centerNoRotate: Qt.vector3d(
monitorCenterRadius, monitorCenterRadius,
0, 0,
@ -259,6 +260,7 @@ QtObject {
monitorPlacements.push({ monitorPlacements.push({
originalIndex: originalIndex, originalIndex: originalIndex,
monitorCenterNorth: monitorCenterRadius,
centerNoRotate: Qt.vector3d( centerNoRotate: Qt.vector3d(
monitorCenterRadius, monitorCenterRadius,
westCenterPixels, westCenterPixels,
@ -287,6 +289,7 @@ QtObject {
monitorPlacements.push({ monitorPlacements.push({
originalIndex: index, originalIndex: index,
monitorCenterNorth: fovDetails.completeScreenDistancePixels,
centerNoRotate: Qt.vector3d( centerNoRotate: Qt.vector3d(
fovDetails.completeScreenDistancePixels, fovDetails.completeScreenDistancePixels,
westCenterPixels, westCenterPixels,