Merge multimonitor with cursormanager changes

This commit is contained in:
wheaney 2025-02-04 10:16:54 -08:00
parent 7bd026c9c3
commit f6869fcaff
5 changed files with 53 additions and 167 deletions

View File

@ -4,11 +4,11 @@ import * as PointerWatcher from 'resource:///org/gnome/shell/ui/pointerWatcher.j
import { MouseSpriteContent } from './cursor.js';
import Globals from './globals.js';
// Taken from https://github.com/jkitching/soft-brightness-plus
export class CursorManager {
constructor(overlay, refreshRate) {
this._overlay = overlay;
constructor(mainActor, targetMonitors, refreshRate) {
this._mainActor = mainActor;
this._targetMonitors = targetMonitors;
this._refreshRate = refreshRate;
// Set/destroyed by _enableCloningMouse/_disableCloningMouse
@ -44,6 +44,10 @@ export class CursorManager {
this._stopCloningMouse();
}
moveAboveSiblings() {
if (this._cursorRoot) this._mainActor.set_child_above_sibling(this._cursorRoot, null);
}
// After this:
// * real cursor is disabled
// * cloning is "on"
@ -111,7 +115,7 @@ export class CursorManager {
// prereqs: setup in _enableCloningMouse
_startCloningMouse() {
Globals.logger.log_debug('CursorManager _startCloningMouse');
this._overlay.mainActor().add_child(this._cursorRoot);
this._mainActor.add_child(this._cursorRoot);
this._updateMouseSprite();
this._cursorTracker.connectObject('cursor-changed', this._updateMouseSprite.bind(this), this);
@ -124,7 +128,8 @@ export class CursorManager {
this._updateMousePosition();
const [xMouse, yMouse] = global.get_pointer();
if (this._overlay.isWithinBounds(xMouse, yMouse)) this._hideSystemCursor();
if (this._targetMonitors.some(monitor => this._isWithinMonitorBounds(xMouse, yMouse, monitor))) this._hideSystemCursor();
}
// After this:
@ -144,7 +149,7 @@ export class CursorManager {
if (this._mouseSprite?.content?.texture) this._mouseSprite.content.texture = null;
Meta.enable_unredirect_for_display(global.display);
if (this._cursorRoot) this._overlay.mainActor().remove_child(this._cursorRoot);
if (this._cursorRoot) this._mainActor.remove_child(this._cursorRoot);
if (!this._systemCursorShown) this._showSystemCursor();
}
@ -168,21 +173,20 @@ export class CursorManager {
_updateMousePosition(...args) {
const [xMouse, yMouse] = args.length ? args : global.get_pointer();
const inBounds = this._overlay.isWithinBounds(xMouse, yMouse);
const [xRel, yRel] = this._overlay.getRelativePosition(xMouse, yMouse);
const inBounds = this._targetMonitors.some(monitor => this._isWithinMonitorBounds(xMouse, yMouse, monitor));
if (xRel === this.xMouse && yRel === this.yMouse)
if (xMouse === this.xMouse && yMouse === this.yMouse)
return;
if (inBounds) {
if (this._systemCursorShown) this._hideSystemCursor();
this._cursorRoot.set_position(xRel, yRel);
this._cursorRoot.set_position(xMouse, yMouse);
} else if (!this._systemCursorShown && !inBounds) {
this._showSystemCursor();
}
this.xMouse = xRel;
this.yMouse = yRel;
this.xMouse = xMouse;
this.yMouse = yMouse;
const seat = Clutter.get_default_backend().get_default_seat();
if (this._cursorUnfocusInhibited && !seat.is_unfocus_inhibited()) {
@ -210,4 +214,9 @@ export class CursorManager {
this._mouseSprite.hide();
}
}
_isWithinMonitorBounds(x, y, monitor) {
return x >= monitor.x && x < monitor.x + monitor.width &&
y >= monitor.y && y < monitor.y + monitor.height;
}
}

View File

@ -1,71 +0,0 @@
const { Clutter, GLib, GObject } = imports.gi;
export const CustomEffect = GObject.registerClass({
Properties: {
'fov-degrees': GObject.ParamSpec.double(
'fov-degrees',
'FOV Degrees',
'Diagonal field-of-view in degrees',
GObject.ParamFlags.READWRITE,
1.0,
179.0,
60.0
)
}
}, class Customffect extends Clutter.ShaderEffect {
_init(params = {}) {
super._init(params);
this.fov_degrees = params['fov-degrees'] || 60.0;
this.connect('notify::fov-degrees', this._updateMatrices.bind(this));
// Set up the vertex shader
this.set_shader_source(Clutter.ShaderType.VERTEX, `
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
uniform vec4 quaternion;
vec3 applyQuaternionToVector(vec3 v, vec4 q) {
return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v);
}
void main() {
// First apply the view matrix to position the vertex in camera space
vec4 viewPosition = viewMatrix * vec4(gl_Vertex.xyz, 1.0);
// Then apply the quaternion rotation
vec3 transformedPosition = applyQuaternionToVector(viewPosition.xyz, quaternion);
// Finally apply the projection matrix
gl_Position = projectionMatrix * vec4(transformedPosition, 1.0);
gl_TexCoord[0] = gl_MultiTexCoord0;
}
`);
// Initialize with the current matrices
this._updateMatrices();
}
_updateMatrices() {
let aspect = this.get_parent().width / this.get_parent().height;
let fov = this.fov_degrees * Math.PI / 180.0;
let near = 0.1;
let far = 100.0;
let top = Math.tan(fov / 2.0) * near;
let bottom = -top;
let right = top * aspect;
let left = -right;
let projectionMatrix = GLib.Matrix.init_frustum(left, right, bottom, top, near, far);
let viewMatrix = GLib.Matrix.init_identity();
// Calculate the appropriate Z-distance based on FOV
let distance = -1.0 / Math.tan(fov / 2.0);
viewMatrix = viewMatrix.translate(0, 0, distance);
this.set_shader_uniform_value('projectionMatrix', new Clutter.ShaderValue({matrix: projectionMatrix}));
this.set_shader_uniform_value('viewMatrix', new Clutter.ShaderValue({matrix: viewMatrix}));
}
set_quaternion(quat) {
this.set_shader_uniform_value('quaternion', new Clutter.ShaderValue({vector4: quat}));
}
});

View File

@ -1,16 +1,15 @@
import Clutter from 'gi://Clutter'
import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import GObject from 'gi://GObject';
import Meta from 'gi://Meta';
import Shell from 'gi://Shell';
import St from 'gi://St';
import { CursorManager } from './cursormanager.js';
import { DeviceDataStream } from './devicedatastream.js';
import Globals from './globals.js';
import { Logger } from './logger.js';
import { MonitorManager } from './monitormanager.js';
import { Overlay } from './overlay.js';
import { VirtualMonitorsActor } from './virtualmonitorsactor.js';
import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
@ -278,12 +277,34 @@ export default class BreezyDesktopExtension extends Extension {
try {
const targetMonitor = this._target_monitor.monitor;
const virtualMonitors = this._find_virtual_monitors();
const refreshRate = targetMonitor.refreshRate ?? 60;
this._overlay = new Overlay(targetMonitor);
this._cursor_manager = new CursorManager(this._overlay, refreshRate);
this._cursor_manager = new CursorManager(Main.layoutManager.uiGroup, [targetMonitor, ...virtualMonitors], refreshRate);
this._cursor_manager.enable();
this._overlay = new St.Bin({ style: 'background-color: rgba(0, 0, 0, 1);', reactive: false, clip_to_allocation: true });
this._overlay.opacity = 255;
this._overlay.set_position(targetMonitor.x, targetMonitor.y);
this._overlay.set_size(targetMonitor.width, targetMonitor.height);
// const textureSourceActor = Main.layoutManager.uiGroup;
Globals.data_stream.refresh_data();
this._overlay_content = new VirtualMonitorsActor({
monitors: virtualMonitors,
fov_degrees: 46.0,
target_monitor: targetMonitor,
display_distance: this.settings.get_double('display-distance'),
toggle_display_distance_start: this.settings.get_double('toggle-display-distance-start'),
toggle_display_distance_end: this.settings.get_double('toggle-display-distance-end'),
imu_snapshots: Globals.data_stream.imu_snapshots
});
this._overlay.set_child(this._overlay_content);
Shell.util_set_hidden_from_pick(this._overlay, true);
global.stage.add_child(this._overlay);
// In GS 45, use of "actor" was renamed to "child".
const clutterContainer = Clutter.Container !== undefined;
this._actor_added_connection = global.stage.connect(
@ -318,7 +339,6 @@ export default class BreezyDesktopExtension extends Extension {
// this._look_ahead_override_binding = this.settings.bind('look-ahead-override', this._xr_effect, 'look-ahead-override', Gio.SettingsBindFlags.DEFAULT);
// this._disable_anti_aliasing_binding = this.settings.bind('disable-anti-aliasing', this._xr_effect, 'disable-anti-aliasing', Gio.SettingsBindFlags.DEFAULT);
this._overlay.mainActor().add_effect_with_name('xr-desktop', this._xr_effect);
Meta.disable_unredirect_for_display(global.display);
this._add_settings_keybinding('toggle-xr-effect-shortcut', this._toggle_xr_effect.bind(this));
@ -335,7 +355,8 @@ export default class BreezyDesktopExtension extends Extension {
_handle_sibling_update() {
Globals.logger.log_debug('BreezyDesktopExtension _handle_sibling_update()');
global.stage.set_child_above_sibling(this._overlay.mainActor(), null);
this._cursor_manager.moveAboveSiblings();
global.stage.set_child_above_sibling(this._overlay, null);
}
_add_settings_keybinding(settings_key, bind_to_function) {
@ -628,26 +649,10 @@ export default class BreezyDesktopExtension extends Extension {
this._overlay = null;
}
if (this._xr_effect) {
if (this._widescreen_mode_effect_state_connection) {
this._xr_effect.disconnect(this._widescreen_mode_effect_state_connection);
this._widescreen_mode_effect_state_connection = null;
}
if (this._supported_device_detected_connected) {
this._xr_effect.disconnect(this._supported_device_detected_connected);
this._supported_device_detected_connected = null;
}
this._xr_effect.cleanup();
this._xr_effect = null;
}
if (this._cursor_manager) {
this._cursor_manager.disable();
this._cursor_manager = null;
}
if (this._overlay) {
this._overlay.mainActor().remove_effect_by_name('xr-desktop');
this._overlay.destroy();
}
// this should always be done at the end of this function after the widescreen settings binding is removed,
// so it doesn't reset the setting to false

View File

@ -1,49 +0,0 @@
import Clutter from 'gi://Clutter'
import Shell from 'gi://Shell';
import St from 'gi://St';
import { SystemBackground } from './systembackground.js';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
export class Overlay {
constructor(targetMonitor) {
this._overlayContent = new Clutter.Actor({clip_to_allocation: true});
this._overlay = new St.Bin({
child: this._overlayContent
});
this._overlay.set_position(targetMonitor.x, targetMonitor.y);
this._overlay.set_size(targetMonitor.width, targetMonitor.height);
global.stage.add_child(this._overlay);
Shell.util_set_hidden_from_pick(this._overlay, true);
this._background = new SystemBackground();
this._overlayContent.add_child(this._background);
this._uiClone = new Clutter.Clone({ source: Main.layoutManager.uiGroup, clip_to_allocation: true });
this._uiClone.x = -targetMonitor.x;
this._uiClone.y = -targetMonitor.y;
this._overlayContent.add_child(this._uiClone);
this._targetMonitor = targetMonitor;
}
isWithinBounds(x, y) {
return x >= this._targetMonitor.x && x < this._targetMonitor.x + this._targetMonitor.width &&
y >= this._targetMonitor.y && y < this._targetMonitor.y + this._targetMonitor.height;
}
getRelativePosition(x, y) {
return [x - this._targetMonitor.x, y - this._targetMonitor.y];
}
mainActor() {
return this._overlayContent;
}
destroy() {
global.stage.remove_child(this._overlay);
this._overlay.destroy();
this._overlay = null;
}
}

View File

@ -657,30 +657,22 @@ export const VirtualMonitorsActor = GObject.registerClass({
const length = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1] + vector[2] * vector[2]);
return [vector[0] / length, vector[1] / length, vector[2] / length];
});
const monitors = this._all_monitors;
const minMonitorX = Math.min(...monitors.map(monitor => monitor.x));
const maxMonitorX = Math.max(...monitors.map(monitor => monitor.x + monitor.width));
const minMonitorY = Math.min(...monitors.map(monitor => monitor.y));
const maxMonitorY = Math.max(...monitors.map(monitor => monitor.y + monitor.height));
const displayWidth = global.stage.width;
const displayHeight = global.stage.height;
const actorToDisplayRatios = [
displayWidth / this.width,
displayHeight / this.height
global.stage.width / this.width,
global.stage.height / this.height
];
// how far this viewport actor's center is from the center of the whole stage
const actorMidX = this.target_monitor.x + this.width / 2;
const actorMidY = this.target_monitor.y + this.height / 2;
const actorToDisplayOffsets = [
(displayWidth / 2 - (actorMidX - global.stage.x)) * 2 / this.width,
(displayHeight / 2 - (actorMidY - global.stage.y)) * 2 / this.height
(global.stage.width / 2 - (actorMidX - global.stage.x)) * 2 / this.width,
(global.stage.height / 2 - (actorMidY - global.stage.y)) * 2 / this.height
];
Globals.logger.log_debug(`\t\t\tActor to display ratios: ${actorToDisplayRatios}, offsets: ${actorToDisplayOffsets}`);
monitors.forEach(((monitor, index) => {
this._all_monitors.forEach(((monitor, index) => {
// if (index === 0) return;
Globals.logger.log(`\t\t\tMonitor ${index}: ${monitor.x}, ${monitor.y}, ${monitor.width}, ${monitor.height}`);