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_zoomOnFocusEnabled = false;
int m_lookingAtScreenIndex = -1; // -1 means disabled/unknown
int m_lookingAtScreenIndex = -1;
bool m_imuResetState;
QList<QQuaternion> m_imuRotations;
quint32 m_imuTimeElapsedMs;

View File

@ -463,7 +463,8 @@ void BreezyDesktopEffectConfig::updateUiFromConfig()
ui.kcfg_RemoveVirtualDisplaysOnDisable->setChecked(BreezyDesktopConfig::self()->removeVirtualDisplaysOnDisable());
ui.kcfg_AllDisplaysFollowMode->setChecked(BreezyDesktopConfig::self()->allDisplaysFollowMode());
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());
}

View File

@ -29,9 +29,7 @@ Node {
const rotations = smoothFollowEnabled ? effect.smoothFollowOrigin : effect.imuRotations;
if (rotations && rotations.length > 0) {
let focusedIndex = -1;
let lookingAtIndex = -1;
lookingAtIndex = displays.findFocusedMonitor(
const lookingAtIndex = displays.findFocusedMonitor(
displays.eusToNwuQuat(rotations[0]),
breezyDesktop.monitorPlacements.map(monitorVectors => monitorVectors.centerLook),
breezyDesktop.focusedMonitorIndex,
@ -57,26 +55,31 @@ Node {
let targetProgress;
if (smoothFollowEnabled && focusedIndex !== -1) {
focusedDisplay = breezyDesktop.displayAtIndex(focusedIndex);
targetDisplay = focusedDisplay;
targetProgress = 1.0;
startSmoothFollowFocusAnimation = true;
if (focusedDisplay !== null) {
targetDisplay = focusedDisplay;
targetProgress = 1.0;
startSmoothFollowFocusAnimation = true;
}
} else if (!smoothFollowEnabled && breezyDesktop.focusedMonitorIndex !== -1) {
unfocusedDisplay = breezyDesktop.displayAtIndex(breezyDesktop.focusedMonitorIndex);
targetDisplay = unfocusedDisplay;
targetProgress = 0.0;
if (unfocusedDisplay !== null) {
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) {
const unfocusedIndex = breezyDesktop.focusedMonitorIndex;
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 (!unfocusedDisplay) unfocusedDisplay = breezyDesktop.displayAtIndex(unfocusedIndex);
zoomOutAnimation.target = unfocusedDisplay;
@ -105,16 +108,12 @@ Node {
}
}
// monitorPlacement assumed to be present
function displayEusVector(display) {
function displayRotationVector(display) {
const displayNwu =
display.monitorPlacement.centerNoRotate
.times(display.monitorDistance / effect.allDisplaysDistance);
return displays.nwuToEusVector(displayNwu);
}
function displayRotationVector(display, eusVector) {
const eusVector = displays.nwuToEusVector(displayNwu)
return display.rotationMatrix.times(eusVector);
}
@ -125,23 +124,27 @@ Node {
return effect.smoothFollowOrigin[0].times(effect.imuRotations[0].conjugated());
}
function displaySmoothFollowVector(display, eusVector) {
return smoothFollowQuat().times(eusVector);
function displaySmoothFollowVector(display, smoothFollowRotation) {
// 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
// used for smooth follow
function displayPosition(display, smoothFollowRotation) {
const displayEus = displayEusVector(display);
// short circuit to avoid slerping if not needed
if (display.smoothFollowTransitionProgress === 1.0) {
return displaySmoothFollowVector(display, displayEus, smoothFollowRotation);
return displaySmoothFollowVector(display, smoothFollowRotation);
}
const finalPosition = displays.slerpVector(
displayRotationVector(display, displayEus),
displaySmoothFollowVector(display, displayEus, smoothFollowRotation),
displayRotationVector(display),
displaySmoothFollowVector(display, smoothFollowRotation),
display.smoothFollowTransitionProgress
);
@ -173,7 +176,7 @@ Node {
position: {
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);
} else {
focusedDisplay.rotation = Qt.quaternion(1, 0, 0, 0);
focusedDisplay.eulerRotation.x = focusedDisplay.screenRotationX;
focusedDisplay.eulerRotation.y = focusedDisplay.screenRotationY;
focusedDisplay.eulerRotation.x = Qt.binding(function() { return focusedDisplay.screenRotationX; });
focusedDisplay.eulerRotation.y = Qt.binding(function() { return focusedDisplay.screenRotationY; });
focusedDisplay.eulerRotation.z = 0.0;
// 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
// 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.
focusedDisplay.position = Qt.binding(function() {
return displayRotationVector(this, displayEusVector(this));
}.bind(focusedDisplay) );
focusedDisplay.position = Qt.binding(function() {
return displayRotationVector(this);
}.bind(focusedDisplay));
}
}

View File

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