Merge multimonitor with cursormanager changes
This commit is contained in:
parent
7bd026c9c3
commit
f6869fcaff
|
|
@ -4,11 +4,11 @@ import * as PointerWatcher from 'resource:///org/gnome/shell/ui/pointerWatcher.j
|
||||||
import { MouseSpriteContent } from './cursor.js';
|
import { MouseSpriteContent } from './cursor.js';
|
||||||
import Globals from './globals.js';
|
import Globals from './globals.js';
|
||||||
|
|
||||||
|
|
||||||
// Taken from https://github.com/jkitching/soft-brightness-plus
|
// Taken from https://github.com/jkitching/soft-brightness-plus
|
||||||
export class CursorManager {
|
export class CursorManager {
|
||||||
constructor(overlay, refreshRate) {
|
constructor(mainActor, targetMonitors, refreshRate) {
|
||||||
this._overlay = overlay;
|
this._mainActor = mainActor;
|
||||||
|
this._targetMonitors = targetMonitors;
|
||||||
this._refreshRate = refreshRate;
|
this._refreshRate = refreshRate;
|
||||||
|
|
||||||
// Set/destroyed by _enableCloningMouse/_disableCloningMouse
|
// Set/destroyed by _enableCloningMouse/_disableCloningMouse
|
||||||
|
|
@ -44,6 +44,10 @@ export class CursorManager {
|
||||||
this._stopCloningMouse();
|
this._stopCloningMouse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
moveAboveSiblings() {
|
||||||
|
if (this._cursorRoot) this._mainActor.set_child_above_sibling(this._cursorRoot, null);
|
||||||
|
}
|
||||||
|
|
||||||
// After this:
|
// After this:
|
||||||
// * real cursor is disabled
|
// * real cursor is disabled
|
||||||
// * cloning is "on"
|
// * cloning is "on"
|
||||||
|
|
@ -111,7 +115,7 @@ export class CursorManager {
|
||||||
// prereqs: setup in _enableCloningMouse
|
// prereqs: setup in _enableCloningMouse
|
||||||
_startCloningMouse() {
|
_startCloningMouse() {
|
||||||
Globals.logger.log_debug('CursorManager _startCloningMouse');
|
Globals.logger.log_debug('CursorManager _startCloningMouse');
|
||||||
this._overlay.mainActor().add_child(this._cursorRoot);
|
this._mainActor.add_child(this._cursorRoot);
|
||||||
|
|
||||||
this._updateMouseSprite();
|
this._updateMouseSprite();
|
||||||
this._cursorTracker.connectObject('cursor-changed', this._updateMouseSprite.bind(this), this);
|
this._cursorTracker.connectObject('cursor-changed', this._updateMouseSprite.bind(this), this);
|
||||||
|
|
@ -124,7 +128,8 @@ export class CursorManager {
|
||||||
this._updateMousePosition();
|
this._updateMousePosition();
|
||||||
|
|
||||||
const [xMouse, yMouse] = global.get_pointer();
|
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:
|
// After this:
|
||||||
|
|
@ -144,7 +149,7 @@ export class CursorManager {
|
||||||
if (this._mouseSprite?.content?.texture) this._mouseSprite.content.texture = null;
|
if (this._mouseSprite?.content?.texture) this._mouseSprite.content.texture = null;
|
||||||
Meta.enable_unredirect_for_display(global.display);
|
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();
|
if (!this._systemCursorShown) this._showSystemCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -168,21 +173,20 @@ export class CursorManager {
|
||||||
|
|
||||||
_updateMousePosition(...args) {
|
_updateMousePosition(...args) {
|
||||||
const [xMouse, yMouse] = args.length ? args : global.get_pointer();
|
const [xMouse, yMouse] = args.length ? args : global.get_pointer();
|
||||||
const inBounds = this._overlay.isWithinBounds(xMouse, yMouse);
|
const inBounds = this._targetMonitors.some(monitor => this._isWithinMonitorBounds(xMouse, yMouse, monitor));
|
||||||
const [xRel, yRel] = this._overlay.getRelativePosition(xMouse, yMouse);
|
|
||||||
|
|
||||||
if (xRel === this.xMouse && yRel === this.yMouse)
|
if (xMouse === this.xMouse && yMouse === this.yMouse)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (inBounds) {
|
if (inBounds) {
|
||||||
if (this._systemCursorShown) this._hideSystemCursor();
|
if (this._systemCursorShown) this._hideSystemCursor();
|
||||||
this._cursorRoot.set_position(xRel, yRel);
|
this._cursorRoot.set_position(xMouse, yMouse);
|
||||||
} else if (!this._systemCursorShown && !inBounds) {
|
} else if (!this._systemCursorShown && !inBounds) {
|
||||||
this._showSystemCursor();
|
this._showSystemCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.xMouse = xRel;
|
this.xMouse = xMouse;
|
||||||
this.yMouse = yRel;
|
this.yMouse = yMouse;
|
||||||
|
|
||||||
const seat = Clutter.get_default_backend().get_default_seat();
|
const seat = Clutter.get_default_backend().get_default_seat();
|
||||||
if (this._cursorUnfocusInhibited && !seat.is_unfocus_inhibited()) {
|
if (this._cursorUnfocusInhibited && !seat.is_unfocus_inhibited()) {
|
||||||
|
|
@ -210,4 +214,9 @@ export class CursorManager {
|
||||||
this._mouseSprite.hide();
|
this._mouseSprite.hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_isWithinMonitorBounds(x, y, monitor) {
|
||||||
|
return x >= monitor.x && x < monitor.x + monitor.width &&
|
||||||
|
y >= monitor.y && y < monitor.y + monitor.height;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
@ -1,16 +1,15 @@
|
||||||
import Clutter from 'gi://Clutter'
|
import Clutter from 'gi://Clutter'
|
||||||
import Gio from 'gi://Gio';
|
import Gio from 'gi://Gio';
|
||||||
import GLib from 'gi://GLib';
|
import GLib from 'gi://GLib';
|
||||||
import GObject from 'gi://GObject';
|
|
||||||
import Meta from 'gi://Meta';
|
import Meta from 'gi://Meta';
|
||||||
import Shell from 'gi://Shell';
|
import Shell from 'gi://Shell';
|
||||||
|
import St from 'gi://St';
|
||||||
|
|
||||||
import { CursorManager } from './cursormanager.js';
|
import { CursorManager } from './cursormanager.js';
|
||||||
import { DeviceDataStream } from './devicedatastream.js';
|
import { DeviceDataStream } from './devicedatastream.js';
|
||||||
import Globals from './globals.js';
|
import Globals from './globals.js';
|
||||||
import { Logger } from './logger.js';
|
import { Logger } from './logger.js';
|
||||||
import { MonitorManager } from './monitormanager.js';
|
import { MonitorManager } from './monitormanager.js';
|
||||||
import { Overlay } from './overlay.js';
|
|
||||||
import { VirtualMonitorsActor } from './virtualmonitorsactor.js';
|
import { VirtualMonitorsActor } from './virtualmonitorsactor.js';
|
||||||
|
|
||||||
import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
|
import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
|
||||||
|
|
@ -278,12 +277,34 @@ export default class BreezyDesktopExtension extends Extension {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const targetMonitor = this._target_monitor.monitor;
|
const targetMonitor = this._target_monitor.monitor;
|
||||||
|
const virtualMonitors = this._find_virtual_monitors();
|
||||||
const refreshRate = targetMonitor.refreshRate ?? 60;
|
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._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".
|
// In GS 45, use of "actor" was renamed to "child".
|
||||||
const clutterContainer = Clutter.Container !== undefined;
|
const clutterContainer = Clutter.Container !== undefined;
|
||||||
this._actor_added_connection = global.stage.connect(
|
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._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._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);
|
Meta.disable_unredirect_for_display(global.display);
|
||||||
|
|
||||||
this._add_settings_keybinding('toggle-xr-effect-shortcut', this._toggle_xr_effect.bind(this));
|
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() {
|
_handle_sibling_update() {
|
||||||
Globals.logger.log_debug('BreezyDesktopExtension _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) {
|
_add_settings_keybinding(settings_key, bind_to_function) {
|
||||||
|
|
@ -628,26 +649,10 @@ export default class BreezyDesktopExtension extends Extension {
|
||||||
this._overlay = null;
|
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) {
|
if (this._cursor_manager) {
|
||||||
this._cursor_manager.disable();
|
this._cursor_manager.disable();
|
||||||
this._cursor_manager = null;
|
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,
|
// 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
|
// so it doesn't reset the setting to false
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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]);
|
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];
|
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 = [
|
const actorToDisplayRatios = [
|
||||||
displayWidth / this.width,
|
global.stage.width / this.width,
|
||||||
displayHeight / this.height
|
global.stage.height / this.height
|
||||||
];
|
];
|
||||||
|
|
||||||
// how far this viewport actor's center is from the center of the whole stage
|
// 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 actorMidX = this.target_monitor.x + this.width / 2;
|
||||||
const actorMidY = this.target_monitor.y + this.height / 2;
|
const actorMidY = this.target_monitor.y + this.height / 2;
|
||||||
const actorToDisplayOffsets = [
|
const actorToDisplayOffsets = [
|
||||||
(displayWidth / 2 - (actorMidX - global.stage.x)) * 2 / this.width,
|
(global.stage.width / 2 - (actorMidX - global.stage.x)) * 2 / this.width,
|
||||||
(displayHeight / 2 - (actorMidY - global.stage.y)) * 2 / this.height
|
(global.stage.height / 2 - (actorMidY - global.stage.y)) * 2 / this.height
|
||||||
];
|
];
|
||||||
|
|
||||||
Globals.logger.log_debug(`\t\t\tActor to display ratios: ${actorToDisplayRatios}, offsets: ${actorToDisplayOffsets}`);
|
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;
|
// if (index === 0) return;
|
||||||
Globals.logger.log(`\t\t\tMonitor ${index}: ${monitor.x}, ${monitor.y}, ${monitor.width}, ${monitor.height}`);
|
Globals.logger.log(`\t\t\tMonitor ${index}: ${monitor.x}, ${monitor.y}, ${monitor.width}, ${monitor.height}`);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue