diff --git a/extension.js b/extension.js index fd0a759..24bb1eb 100644 --- a/extension.js +++ b/extension.js @@ -31,9 +31,7 @@ export default class CursorOverlayExtension extends Extension { this._timerId = null; this._monitorChangedId = null; this._connectorMap = new Map(); - this._disabledSet = new Set(this._settings.get_strv('disabled-monitors')); - this._lastMonitorIdx = -1; - this._lastMonitorDisabled = false; + this._updateMonitorPolicy(); this._buildMonitorMap(); this._setupOverlay(); @@ -42,9 +40,7 @@ export default class CursorOverlayExtension extends Extension { this._settingsChangedId = this._settings.connect('changed', () => { this._stopTracking(); this._teardownOverlay(); - this._disabledSet = new Set(this._settings.get_strv('disabled-monitors')); - this._lastMonitorIdx = -1; - this._lastMonitorDisabled = false; + this._updateMonitorPolicy(); this._setupOverlay(); this._startTracking(); }); @@ -73,6 +69,7 @@ export default class CursorOverlayExtension extends Extension { this._teardownOverlay(); this._settings = null; this._connectorMap = null; + this._enabledSet = null; this._disabledSet = null; } @@ -99,17 +96,59 @@ export default class CursorOverlayExtension extends Extension { this._lastY = null; } + _updateMonitorPolicy() { + this._enabledSet = new Set(this._settings.get_strv('enabled-monitors')); + this._disabledSet = new Set(this._settings.get_strv('disabled-monitors')); + this._disableNew = this._settings.get_boolean('disable-new-monitors'); + this._enableMeta = this._settings.get_boolean('enable-meta-monitors'); + this._lastMonitorIdx = -1; + this._lastMonitorDisabled = false; + this._needsMonitorCheck = this._disabledSet.size > 0 + || this._enabledSet.size > 0 + || this._disableNew; + if (this._connectorMap?.size > 0) + this._rebuildDisabledCache(); + } + _buildMonitorMap() { this._connectorMap = new Map(); + this._metaSet = new Set(); try { const mm = global.backend.get_monitor_manager(); for (const monitor of mm.get_monitors()) { const connector = monitor.get_connector(); const idx = mm.get_monitor_for_connector(connector); - if (idx >= 0) - this._connectorMap.set(idx, connector); + if (idx < 0) continue; + this._connectorMap.set(idx, connector); + try { + const vendor = monitor.get_vendor(); + const name = monitor.get_display_name(); + if ((vendor && vendor.includes('Meta')) + || (name && name.includes('Meta')) + || (connector && connector.includes('Meta'))) + this._metaSet.add(connector); + } catch { /* no vendor/name API */ } } } catch { /* unavailable */ } + this._rebuildDisabledCache(); + } + + _rebuildDisabledCache() { + this._disabledCache = new Map(); + for (const [idx, connector] of this._connectorMap) { + let disabled; + if (this._enabledSet.has(connector)) + disabled = false; + else if (this._disabledSet.has(connector)) + disabled = true; + else if (!this._disableNew) + disabled = false; + else if (this._enableMeta && this._metaSet.has(connector)) + disabled = false; + else + disabled = true; + this._disabledCache.set(idx, disabled); + } } _startTracking() { @@ -148,12 +187,11 @@ export default class CursorOverlayExtension extends Extension { const [mx, my] = global.get_pointer(); - if (this._disabledSet.size > 0 && this._connectorMap.size > 0) { + if (this._needsMonitorCheck && this._disabledCache?.size > 0) { const monIdx = global.display.get_current_monitor(); if (monIdx !== this._lastMonitorIdx) { this._lastMonitorIdx = monIdx; - const connector = this._connectorMap.get(monIdx); - this._lastMonitorDisabled = connector != null && this._disabledSet.has(connector); + this._lastMonitorDisabled = this._disabledCache.get(monIdx) ?? this._disableNew; } if (this._lastMonitorDisabled) { this._overlay.hide(); diff --git a/prefs.js b/prefs.js index 790b938..dc0e9dc 100644 --- a/prefs.js +++ b/prefs.js @@ -164,19 +164,50 @@ const OverlayPage = GObject.registerClass( const monitorGroup = new Adw.PreferencesGroup({title: 'Per-Monitor'}); this.add(monitorGroup); + const disableNewRow = new Adw.SwitchRow({ + title: 'Do not enable on new monitors', + active: settings.get_boolean('disable-new-monitors'), + }); + disableNewRow.connect('notify::active', w => { + settings.set_boolean('disable-new-monitors', w.active); + metaRow.sensitive = w.active; + }); + monitorGroup.add(disableNewRow); + + const metaRow = new Adw.SwitchRow({ + title: 'Enable on Meta monitors', + subtitle: 'Auto-enable on virtual monitors', + active: settings.get_boolean('enable-meta-monitors'), + sensitive: settings.get_boolean('disable-new-monitors'), + }); + metaRow.connect('notify::active', w => { + settings.set_boolean('enable-meta-monitors', w.active); + }); + monitorGroup.add(metaRow); + const monitorList = Gdk.Display.get_default().get_monitors(); const nMonitors = monitorList.get_n_items(); + const enabledMonitors = settings.get_strv('enabled-monitors'); const disabledMonitors = settings.get_strv('disabled-monitors'); + const disableNew = settings.get_boolean('disable-new-monitors'); + const enableMeta = settings.get_boolean('enable-meta-monitors'); for (let i = 0; i < nMonitors; i++) { const monitor = monitorList.get_item(i); const connector = monitor.get_connector(); const geom = monitor.get_geometry(); - const toggle = new Gtk.Switch({ - active: !disabledMonitors.includes(connector), - valign: Gtk.Align.CENTER, - }); + let active; + if (enabledMonitors.includes(connector)) + active = true; + else if (disabledMonitors.includes(connector)) + active = false; + else if (!disableNew) + active = true; + else + active = enableMeta; + + const toggle = new Gtk.Switch({active, valign: Gtk.Align.CENTER}); const row = new Adw.ActionRow({ title: connector || `Monitor ${i + 1}`, @@ -186,13 +217,18 @@ const OverlayPage = GObject.registerClass( row.set_activatable_widget(toggle); toggle.connect('notify::active', widget => { - const current = settings.get_strv('disabled-monitors'); + const en = settings.get_strv('enabled-monitors'); + const dis = settings.get_strv('disabled-monitors'); if (widget.active) { + settings.set_strv('enabled-monitors', + en.includes(connector) ? en : [...en, connector]); settings.set_strv('disabled-monitors', - current.filter(c => c !== connector)); + dis.filter(c => c !== connector)); } else { - if (!current.includes(connector)) - settings.set_strv('disabled-monitors', [...current, connector]); + settings.set_strv('disabled-monitors', + dis.includes(connector) ? dis : [...dis, connector]); + settings.set_strv('enabled-monitors', + en.filter(c => c !== connector)); } }); diff --git a/schemas/org.gnome.shell.extensions.cursor-overlay.gschema.xml b/schemas/org.gnome.shell.extensions.cursor-overlay.gschema.xml index 81ac49d..997091f 100644 --- a/schemas/org.gnome.shell.extensions.cursor-overlay.gschema.xml +++ b/schemas/org.gnome.shell.extensions.cursor-overlay.gschema.xml @@ -10,95 +10,29 @@ - - 'circle' - Overlay mode - Circle ring, tinted cursor, or custom image overlay. - + 16 + 2 + '#000000' + 85 - - 16 - Circle radius - Radius of the circle overlay in pixels. - + 48 + '#ff0000' + 80 - - 2 - Circle stroke width - Width of the circle outline in pixels. - + '' + 48 + 80 - - '#000000' - Circle color - Color of the circle overlay. - + true + true + [] + [] - - 85 - Circle opacity - Opacity of the circle overlay (0-100). - - - - - - 48 - Cursor overlay size - Size of the cursor overlay in pixels. - - - - '#ff0000' - Cursor overlay color - Tint color for the cursor overlay. - - - - 80 - Cursor overlay opacity - Opacity of the cursor overlay (0-100). - - - - - - '' - Custom image path - Path to a custom image file (PNG, BMP, SVG, etc.) to use as overlay. - - - - 48 - Custom image size - Display size of the custom image overlay in pixels. - - - - 80 - Custom image opacity - Opacity of the custom image overlay (0-100). - - - - - - [] - Disabled monitors - List of monitor connector names where the overlay is hidden. - - - - - - 120 - Poll rate - Fallback cursor position polling rate in Hz. - + 120