From 0e02c593402d681c98b17bca9f50e6248ae71820 Mon Sep 17 00:00:00 2001 From: wheaney <42350981+wheaney@users.noreply.github.com> Date: Thu, 10 Oct 2024 12:38:08 -0700 Subject: [PATCH 1/2] Fix caching of backend object, copy SystemBackground from GNOME magnifier implemenation --- gnome-44-max.patch | 41 +++++++++++++++++++++++++++++------ gnome/src/extension.js | 22 ++++++++++--------- gnome/src/monitormanager.js | 5 +---- gnome/src/systembackground.js | 31 ++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 21 deletions(-) create mode 100644 gnome/src/systembackground.js diff --git a/gnome-44-max.patch b/gnome-44-max.patch index 2df4770..e633a59 100644 --- a/gnome-44-max.patch +++ b/gnome-44-max.patch @@ -70,10 +70,10 @@ index 44b3f5f..fa65a4a 100644 } diff --git a/gnome-44-max/src/extension.js b/gnome-44-max/src/extension.js -index 5f62bfd..d06f91c 100644 +index 29a38f1..0a7e9ae 100644 --- a/gnome-44-max/src/extension.js +++ b/gnome-44-max/src/extension.js -@@ -1,19 +1,21 @@ +@@ -1,20 +1,22 @@ -import Clutter from 'gi://Clutter' -import Gio from 'gi://Gio'; -import GLib from 'gi://GLib'; @@ -85,6 +85,7 @@ index 5f62bfd..d06f91c 100644 -import Globals from './globals.js'; -import { Logger } from './logger.js'; -import { MonitorManager } from './monitormanager.js'; +-import { SystemBackground } from './systembackground.js'; -import { isValidKeepAlive } from './time.js'; -import { IPC_FILE_PATH, XREffect } from './xrEffect.js'; - @@ -106,12 +107,13 @@ index 5f62bfd..d06f91c 100644 +const { CursorManager } = Me.imports.cursormanager; +const { Logger } = Me.imports.logger; +const { MonitorManager } = Me.imports.monitormanager; ++const { SystemBackground } = Me.imports.systembackground; +const { isValidKeepAlive } = Me.imports.time; +const { IPC_FILE_PATH, XREffect } = Me.imports.xrEffect; const NESTED_MONITOR_PRODUCT = 'MetaMonitor'; const SUPPORTED_MONITOR_PRODUCTS = [ -@@ -29,11 +31,10 @@ const SUPPORTED_MONITOR_PRODUCTS = [ +@@ -30,11 +32,10 @@ const SUPPORTED_MONITOR_PRODUCTS = [ NESTED_MONITOR_PRODUCT ]; @@ -127,7 +129,7 @@ index 5f62bfd..d06f91c 100644 // Set/destroyed by enable/disable this._cursor_manager = null; -@@ -619,6 +620,6 @@ export default class BreezyDesktopExtension extends Extension { +@@ -621,6 +622,6 @@ export default class BreezyDesktopExtension extends Extension { } } @@ -270,7 +272,7 @@ index 125954e..c888f94 100644 } \ No newline at end of file diff --git a/gnome-44-max/src/monitormanager.js b/gnome-44-max/src/monitormanager.js -index ca8a6a5..075ba63 100644 +index 6cf5532..580925c 100644 --- a/gnome-44-max/src/monitormanager.js +++ b/gnome-44-max/src/monitormanager.js @@ -16,12 +16,15 @@ @@ -324,6 +326,31 @@ index f70c96d..352be40 100644 const file = Gio.file_new_for_path(path); const data = file.load_contents(null); +diff --git a/gnome-44-max/src/systembackground.js b/gnome-44-max/src/systembackground.js +index 23039b9..350f32d 100644 +--- a/gnome-44-max/src/systembackground.js ++++ b/gnome-44-max/src/systembackground.js +@@ -1,13 +1,14 @@ +-import Cogl from 'gi://Cogl'; +-import GLib from 'gi://GLib'; +-import GObject from 'gi://GObject'; +-import Meta from 'gi://Meta'; ++const Clutter = imports.gi.Clutter; ++const Cogl = imports.gi.Cogl; ++const GLib = imports.gi.GLib; ++const GObject = imports.gi.GObject; ++const Meta = imports.gi.Meta; + +-const DEFAULT_BACKGROUND_COLOR = new Cogl.Color({red: 40, green: 40, blue: 40, alpha: 255}); ++const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff); + + let _systemBackground; + +-export const SystemBackground = GObject.registerClass({ ++var SystemBackground = GObject.registerClass({ + Signals: {'loaded': {}}, + }, class SystemBackground extends Meta.BackgroundActor { + _init() { diff --git a/gnome-44-max/src/time.js b/gnome-44-max/src/time.js index 7883b9b..5478d2a 100644 --- a/gnome-44-max/src/time.js @@ -345,7 +372,7 @@ index 7883b9b..5478d2a 100644 } \ No newline at end of file diff --git a/gnome-44-max/src/xrEffect.js b/gnome-44-max/src/xrEffect.js -index 6b1421b..7d36c46 100644 +index 8590e0f..4394f43 100644 --- a/gnome-44-max/src/xrEffect.js +++ b/gnome-44-max/src/xrEffect.js @@ -1,13 +1,15 @@ @@ -401,7 +428,7 @@ index 6b1421b..7d36c46 100644 Properties: { 'supported-device-detected': GObject.ParamSpec.boolean( 'supported-device-detected', -@@ -372,8 +374,13 @@ export const XREffect = GObject.registerClass({ +@@ -380,8 +382,13 @@ export const XREffect = GObject.registerClass({ if (!this._initialized) { this.set_uniform_float(this.get_uniform_location('screenTexture'), 1, [0]); diff --git a/gnome/src/extension.js b/gnome/src/extension.js index 5f62bfd..29a38f1 100644 --- a/gnome/src/extension.js +++ b/gnome/src/extension.js @@ -9,6 +9,7 @@ import { CursorManager } from './cursormanager.js'; import Globals from './globals.js'; import { Logger } from './logger.js'; import { MonitorManager } from './monitormanager.js'; +import { SystemBackground } from './systembackground.js'; import { isValidKeepAlive } from './time.js'; import { IPC_FILE_PATH, XREffect } from './xrEffect.js'; @@ -250,24 +251,25 @@ export default class BreezyDesktopExtension extends Extension { this._cursor_manager = new CursorManager(Main.layoutManager.uiGroup, refreshRate); this._cursor_manager.enable(); - this._overlay = new St.Bin(); - this._overlay.opacity = 255; + const overlayContent = new Clutter.Actor({clip_to_allocation: true}); + + this._overlay = new St.Bin({ + child: overlayContent + }); this._overlay.set_position(targetMonitor.x, targetMonitor.y); this._overlay.set_size(targetMonitor.width, targetMonitor.height); - Globals.logger.log_debug(`BreezyDesktopExtension _effect_enable overlay size: \ - ${targetMonitor.width}x${targetMonitor.height} at ${targetMonitor.x},${targetMonitor.y}`); - const overlayContent = new Clutter.Actor({clip_to_allocation: true}); + global.stage.add_child(this._overlay); + Shell.util_set_hidden_from_pick(this._overlay, true); + + this._background = new SystemBackground(); + overlayContent.add_child(this._background); + const uiClone = new Clutter.Clone({ source: Main.layoutManager.uiGroup, clip_to_allocation: true }); uiClone.x = -targetMonitor.x; uiClone.y = -targetMonitor.y; overlayContent.add_child(uiClone); - this._overlay.set_child(overlayContent); - - 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( diff --git a/gnome/src/monitormanager.js b/gnome/src/monitormanager.js index ca8a6a5..6cf5532 100644 --- a/gnome/src/monitormanager.js +++ b/gnome/src/monitormanager.js @@ -280,7 +280,6 @@ export const MonitorManager = GObject.registerClass({ this._monitorsChangedConnection = null; this._displayConfigProxy = null; - this._backendManager = null; this._monitorProperties = null; this._changeHookFn = null; this._needsConfigCheck = this.use_optimal_monitor_config; @@ -292,7 +291,6 @@ export const MonitorManager = GObject.registerClass({ enable() { Globals.logger.log_debug('MonitorManager enable'); - this._backendManager = global.backend.get_monitor_manager(); newDisplayConfig(this.extension_path, ((proxy, error) => { if (error) { return; @@ -310,7 +308,6 @@ export const MonitorManager = GObject.registerClass({ this._monitorsChangedConnection = null; this._displayConfigProxy = null; - this._backendManager = null; this._monitorProperties = null; this._changeHookFn = null; } @@ -402,7 +399,7 @@ export const MonitorManager = GObject.registerClass({ const monitorProperties = []; for (let i = 0; i < result.length; i++) { const [monitorName, connectorName, vendor, product, serial, refreshRate] = result[i]; - const monitorIndex = this._backendManager.get_monitor_for_connector(connectorName); + const monitorIndex = global.backend.get_monitor_manager().get_monitor_for_connector(connectorName); Globals.logger.log_debug(`Found monitor ${monitorName}, vendor ${vendor}, product ${product}, serial ${serial}, connector ${connectorName}, index ${monitorIndex}`); if (monitorIndex >= 0) { monitorProperties[monitorIndex] = { diff --git a/gnome/src/systembackground.js b/gnome/src/systembackground.js new file mode 100644 index 0000000..23039b9 --- /dev/null +++ b/gnome/src/systembackground.js @@ -0,0 +1,31 @@ +import Cogl from 'gi://Cogl'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import Meta from 'gi://Meta'; + +const DEFAULT_BACKGROUND_COLOR = new Cogl.Color({red: 40, green: 40, blue: 40, alpha: 255}); + +let _systemBackground; + +export const SystemBackground = GObject.registerClass({ + Signals: {'loaded': {}}, +}, class SystemBackground extends Meta.BackgroundActor { + _init() { + if (_systemBackground == null) { + _systemBackground = new Meta.Background({meta_display: global.display}); + _systemBackground.set_color(DEFAULT_BACKGROUND_COLOR); + } + + super._init({ + meta_display: global.display, + monitor: 0, + }); + this.content.background = _systemBackground; + + let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => { + this.emit('loaded'); + return GLib.SOURCE_REMOVE; + }); + GLib.Source.set_name_by_id(id, '[gnome-shell] SystemBackground.loaded'); + } +}); From 646290363629d59506f743e808bdde4e439548f2 Mon Sep 17 00:00:00 2001 From: wheaney <42350981+wheaney@users.noreply.github.com> Date: Thu, 10 Oct 2024 15:27:33 -0700 Subject: [PATCH 2/2] Bring cursor mirroring back in line with how gnome-shell does it for the zoom/magnifier logic, reduce CPU consumption --- gnome/src/cursormanager.js | 238 +++++++++++++--------------------- gnome/src/systembackground.js | 4 +- 2 files changed, 93 insertions(+), 149 deletions(-) diff --git a/gnome/src/cursormanager.js b/gnome/src/cursormanager.js index 44b3f5f..018b942 100644 --- a/gnome/src/cursormanager.js +++ b/gnome/src/cursormanager.js @@ -11,20 +11,14 @@ export class CursorManager { this._refreshRate = refreshRate; // Set/destroyed by _enableCloningMouse/_disableCloningMouse - this._cursorWantedVisible = null; this._cursorTracker = null; - this._cursorTrackerSetPointerVisible = null; - this._cursorTrackerSetPointerVisibleBound = null; - this._cursorSprite = null; - this._cursorActor = null; - this._cursorWatcher = null; - this._cursorSeat = null; + this._mouseSprite = null; + this._cursorRoot = null; this._cursorUnfocusInhibited = false; // Set/destroyed by _startCloningMouse / _stopCloningMouse this._cursorWatch = null; this._cursorChangedConnection = null; - this._cursorVisibilityChangedConnection = null; this._redraw_timeline = null; } @@ -59,22 +53,32 @@ export class CursorManager { _enableCloningMouse() { Globals.logger.log_debug('CursorManager _enableCloningMouse'); this._cursorTracker = Meta.CursorTracker.get_for_display(global.display); - this._cursorWantedVisible = this._cursorTracker.get_pointer_visible(); - this._cursorTrackerSetPointerVisible = Meta.CursorTracker.prototype.set_pointer_visible; - this._cursorTrackerSetPointerVisibleBound = this._cursorTrackerSetPointerVisible.bind(this._cursorTracker); - Meta.CursorTracker.prototype.set_pointer_visible = this._cursorTrackerSetPointerVisibleReplacement.bind(this); - this._cursorTrackerSetPointerVisibleBound(false); + this._mouseSprite = new Clutter.Actor({ request_mode: Clutter.RequestMode.CONTENT_SIZE }); + this._mouseSprite.content = new MouseSpriteContent(); - this._cursorSprite = new Clutter.Actor({ request_mode: Clutter.RequestMode.CONTENT_SIZE }); - this._cursorSprite.content = new MouseSpriteContent(); - - this._cursorActor = new Clutter.Actor(); - this._cursorActor.add_child(this._cursorSprite); - this._cursorWatcher = PointerWatcher.getPointerWatcher(); - this._cursorSeat = Clutter.get_default_backend().get_default_seat(); + this._cursorRoot = new Clutter.Actor(); + this._cursorRoot.add_child(this._mouseSprite); } + _hideSystemCursor() { + const seat = Clutter.get_default_backend().get_default_seat(); + + if (!this._cursorUnfocusInhibited) { + seat.inhibit_unfocus(); + this._cursorUnfocusInhibited = true; + } + + if (!this._cursorVisibilityChangedId) { + this._cursorTracker.set_pointer_visible(false); + this._cursorVisibilityChangedId = this._cursorTracker.connect('visibility-changed', (() => { + if (this._cursorTracker.get_pointer_visible()) + this._cursorTracker.set_pointer_visible(false); + }).bind(this)); + } + } + + // After this: // * real cursor enabled, manages its own visibility // * cloning is "off" @@ -84,34 +88,15 @@ export class CursorManager { _disableCloningMouse() { Globals.logger.log_debug('CursorManager _disableCloningMouse'); this._stopCloningMouse(); - Meta.CursorTracker.prototype.set_pointer_visible = this._cursorTrackerSetPointerVisible; - this._cursorTracker.set_pointer_visible(this._cursorWantedVisible); - if (this._cursorSprite) { - this._cursorSprite.content = null; - if (this._cursorActor) this._cursorActor.remove_child(this._cursorSprite); + if (this._mouseSprite) { + this._mouseSprite.content = null; + if (this._cursorRoot) this._cursorRoot.remove_child(this._mouseSprite); } - this._cursorWantedVisible = null; this._cursorTracker = null; - this._cursorTrackerSetPointerVisible = null; - this._cursorTrackerSetPointerVisibleBound = null; - this._cursorSprite = null; - this._cursorActor = null; - this._cursorWatcher = null; - this._cursorSeat = null; - } - - // bound to Meta.CursorTracker.prototype.set_pointer_visible when cloning is "on" - // original function available in this._cursorTrackerSetPointerVisibleBound - _cursorTrackerSetPointerVisibleReplacement(visible) { - Globals.logger.log_debug('CursorManager _cursorTrackerSetPointerVisibleReplacement'); - this._cursorWantedVisible = visible; - if (visible) { - this._startCloningMouse(); - } else { - this._stopCloningMouse(); - } + this._mouseSprite = null; + this._cursorRoot = null; } // After this: @@ -120,45 +105,20 @@ export class CursorManager { // * clone cursor is visible // // add the clone cursor actor, watch for pointer movement and cursor changes, reflect them in the cloned cursor - // prereqs: setup in _enableCloningMouse, _cursorWantedVisible is true + // prereqs: setup in _enableCloningMouse _startCloningMouse() { Globals.logger.log_debug('CursorManager _startCloningMouse'); - this._mainActor.add_child(this._cursorActor); - this._cursorChangedConnection = this._cursorTracker.connect('cursor-changed', this._queueSpriteUpdate.bind(this)); - this._cursorVisibilityChangedConnection = this._cursorTracker.connect('visibility-changed', this._queueVisibilityUpdate.bind(this)); + this._mainActor.add_child(this._cursorRoot); - const interval = 1000.0 / this._refreshRate; - this._redraw_timeline = Clutter.Timeline.new_for_actor(this._mainActor, interval * 10); - this._redraw_timeline.connect('new-frame', (() => { - this.handleNewFrame(); - }).bind(this)); + this._updateMouseSprite(); + this._cursorTracker.connectObject('cursor-changed', this._updateMouseSprite.bind(this), this); + Meta.disable_unredirect_for_display(global.display); - // Some elements will occasionally appear above the cursor, so we periodically reset the actor stacking. - // This could theoretically be fixed "better" by attaching to all events that might affect actor ordering, - // but finding a comprehensive list is difficult and not future proof. So this ugly solution helps us - // catch everything. - this._redraw_timeline.connect('completed', (() => { - this._periodicReset(); - }).bind(this)); + const interval = 1000.0 / 60; + this._cursorWatch = PointerWatcher.getPointerWatcher().addWatch(interval, this._updateMousePosition.bind(this)); + this._updateMousePosition(); - this._redraw_timeline.set_repeat_count(-1); - this._redraw_timeline.start(); - - this._cursorWatch = this._cursorWatcher.addWatch(interval, this._queuePositionUpdate.bind(this)); - - const [x, y] = global.get_pointer(); - this._queuePositionUpdate(x, y); - this._queueSpriteUpdate(); - - if (this._cursorTracker.set_keep_focus_while_hidden) { - this._cursorTracker.set_keep_focus_while_hidden(true); - } - - if (!this._cursorUnfocusInhibited) { - Globals.logger.log_debug('inhibit_unfocus'); - this._cursorSeat.inhibit_unfocus(); - this._cursorUnfocusInhibited = true; - } + this._hideSystemCursor(); } // After this: @@ -174,95 +134,79 @@ export class CursorManager { this._cursorWatch = null; } + this._cursorTracker.disconnectObject(this); + this._mouseSprite.content.texture = null; + Meta.enable_unredirect_for_display(global.display); + if (this._cursorChangedConnection) { this._cursorTracker.disconnect(this._cursorChangedConnection); this._cursorChangedConnection = null; } - if (this._cursorVisibilityChangedConnection) { - this._cursorTracker.disconnect(this._cursorVisibilityChangedConnection); - this._cursorVisibilityChangedConnection = null; - } - if (this._redraw_timeline) { this._redraw_timeline.stop(); this._redraw_timeline = null; } - if (this._cursorActor) this._mainActor.remove_child(this._cursorActor); + if (this._cursorRoot) this._mainActor.remove_child(this._cursorRoot); + + this._showSystemCursor(); + } + + _showSystemCursor() { + const seat = Clutter.get_default_backend().get_default_seat(); if (this._cursorUnfocusInhibited) { - Globals.logger.log_debug('uninhibit_unfocus'); - this._cursorSeat.uninhibit_unfocus(); + seat.uninhibit_unfocus(); this._cursorUnfocusInhibited = false; } - } - _queuePositionUpdate(x, y) { - this._queued_cursor_position = [x, y]; - } + if (this._cursorVisibilityChangedId) { + this._cursorTracker.disconnect(this._cursorVisibilityChangedId); + delete this._cursorVisibilityChangedId; - _queueSpriteUpdate() { - this._queued_sprite_update = true; - } - - _queueVisibilityUpdate() { - this._queued_visibility_update = true; - this._cursorTrackerSetPointerVisibleBound(false); - this._queueSpriteUpdate(); - } - - // updates the stacking and other attributes that are hard to track and may periodically get out of sync - _periodicReset() { - this._queue_reset = true; - this._queueVisibilityUpdate(); - } - - handleNewFrame() { - let redraw = false; - if (this._queued_cursor_position) { - const [x, y] = this._queued_cursor_position; - this._cursorActor.set_position(x, y); - this._queued_cursor_position = null; - redraw = true; + this._cursorTracker.set_pointer_visible(true); } + } - if (this._queued_sprite_update) { - const sprite = this._cursorTracker.get_sprite(); - if (sprite) { - this._cursorSprite.content.texture = sprite; - this._cursorSprite.show(); - } else { - this._cursorSprite.hide(); - } - - const [xHot, yHot] = this._cursorTracker.get_hot(); - this._cursorSprite.set({ - translation_x: -xHot, - translation_y: -yHot, - }); - this._queued_sprite_update = false; - redraw = true; + _updateMousePosition(...args) { + const [xMouse, yMouse] = args.length ? args : global.get_pointer(); + + if (xMouse === this.xMouse && yMouse === this.yMouse) + return; + + this.xMouse = xMouse; + this.yMouse = yMouse; + + this._cursorRoot.set_position(xMouse, yMouse); + + if (this._mainActor.get_last_child() !== this._cursorRoot) + this._mainActor.set_child_above_sibling(this._cursorRoot, null); + + const seat = Clutter.get_default_backend().get_default_seat(); + if (!seat.is_unfocus_inhibited() && this._cursorUnfocusInhibited) { + Globals.logger.log_debug('reinhibiting'); + seat.inhibit_unfocus(); } + } - if (this._queued_visibility_update) { - this._queued_visibility_update = false; - redraw = true; + _updateMouseSprite() { + this._updateSpriteTexture(); + let [xHot, yHot] = this._cursorTracker.get_hot(); + this._mouseSprite.set({ + translation_x: -xHot, + translation_y: -yHot, + }); + } + + _updateSpriteTexture() { + let sprite = this._cursorTracker.get_sprite(); + + if (sprite) { + this._mouseSprite.content.texture = sprite; + this._mouseSprite.show(); + } else { + this._mouseSprite.hide(); } - - if (this._queue_reset) { - if (this._mainActor.get_last_child() !== this._cursorActor) - this._mainActor.set_child_above_sibling(this._cursorActor, null); - - // some other processes are uninhibiting when they shouldn't, so we need to re-inhibit here - if (!this._cursorSeat.is_unfocus_inhibited() && this._cursorUnfocusInhibited) { - Globals.logger.log_debug('reinhibiting'); - this._cursorSeat.inhibit_unfocus(); - } - this._queue_reset = false; - redraw = true; - } - - return redraw; } } \ No newline at end of file diff --git a/gnome/src/systembackground.js b/gnome/src/systembackground.js index 23039b9..32b501e 100644 --- a/gnome/src/systembackground.js +++ b/gnome/src/systembackground.js @@ -1,9 +1,9 @@ -import Cogl from 'gi://Cogl'; +import Clutter from 'gi://Clutter'; import GLib from 'gi://GLib'; import GObject from 'gi://GObject'; import Meta from 'gi://Meta'; -const DEFAULT_BACKGROUND_COLOR = new Cogl.Color({red: 40, green: 40, blue: 40, alpha: 255}); +const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff); let _systemBackground;