diff --git a/schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml b/schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml index 092fd85..0b58223 100644 --- a/schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml +++ b/schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml @@ -609,14 +609,10 @@ Use favorite icons as application launchers When the applications are ungrouped, this defines if running applications stay separate from the favorite icons. - - -1 + + '' Primary monitor index - Specifies the index of the primary monitor. -1 if same as gnome-shell primary monitor. - - - 0 - Primary gnome-shell monitor index + Specifies the id of the primary monitor. true @@ -1308,6 +1304,10 @@ '' The preferences page name to display + + false + Track if the preferences window is opened + '' Unix time when the donate icon was hidden diff --git a/src/extension.js b/src/extension.js index dddd08a..8486f12 100644 --- a/src/extension.js +++ b/src/extension.js @@ -22,6 +22,7 @@ import Gio from 'gi://Gio' import * as Main from 'resource:///org/gnome/shell/ui/main.js' import { EventEmitter } from 'resource:///org/gnome/shell/misc/signals.js' import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js' +import * as PanelSettings from './panelSettings.js' import * as PanelManager from './panelManager.js' import * as AppIcons from './appIcons.js' @@ -49,7 +50,7 @@ export default class DashToPanelExtension extends Extension { PERSISTENTSTORAGE = {} } - enable() { + async enable() { DTP_EXTENSION = this SETTINGS = this.getSettings('org.gnome.shell.extensions.dash-to-panel') DESKTOPSETTINGS = new Gio.Settings({ @@ -63,6 +64,14 @@ export default class DashToPanelExtension extends Extension { //create a global object that can emit signals and conveniently expose functionalities to other extensions global.dashToPanel = new EventEmitter() + // reset to be safe + SETTINGS.set_boolean('prefs-opened', false) + + await PanelSettings.init(SETTINGS) + + // To remove later, try to map settings using monitor indexes to monitor ids + PanelSettings.adjustMonitorSettings(SETTINGS) + let completeEnable = () => { panelManager = new PanelManager.PanelManager() panelManager.enable() @@ -106,6 +115,7 @@ export default class DashToPanelExtension extends Extension { disable() { if (ubuntuDockDelayId) clearTimeout(ubuntuDockDelayId) + PanelSettings.disable(SETTINGS) panelManager.disable() DTP_EXTENSION = null @@ -125,4 +135,10 @@ export default class DashToPanelExtension extends Extension { Main.sessionMode.hasOverview = this._realHasOverview } + + openPreferences() { + if (SETTINGS.get_boolean('prefs-opened')) return + + super.openPreferences() + } } diff --git a/src/panel.js b/src/panel.js index c0fe97e..264efc6 100644 --- a/src/panel.js +++ b/src/panel.js @@ -490,9 +490,10 @@ export const Panel = GObject.registerClass( } updateElementPositions() { - let panelPositions = - this.panelManager.panelsElementPositions[this.monitor.index] || - Pos.defaults + let panelPositions = PanelSettings.getPanelElementPositions( + SETTINGS, + this.monitor.index, + ) this._updateGroupedElements(panelPositions) diff --git a/src/panelManager.js b/src/panelManager.js index f89b472..656d947 100755 --- a/src/panelManager.js +++ b/src/panelManager.js @@ -56,14 +56,12 @@ import { export const PanelManager = class { constructor() { this.overview = new Overview.Overview() - this.panelsElementPositions = {} this._injectionManager = new InjectionManager() - - this._saveMonitors() } enable(reset) { - let dtpPrimaryIndex = SETTINGS.get_int('primary-monitor') + let dtpPrimaryIndex = + PanelSettings.monitorIdToIndex[SETTINGS.get_string('primary-monitor')] this.allPanels = [] this.dtpPrimaryMonitor = @@ -210,10 +208,10 @@ export const PanelManager = class { 'changed::panel-anchors', 'changed::stockgs-keep-top-panel', ], - (settings,settingChanged) => { + (settings, settingChanged) => { PanelSettings.clearCache(settingChanged) this._reset() - } + }, ], [ SETTINGS, @@ -221,7 +219,7 @@ export const PanelManager = class { () => { PanelSettings.clearCache('panel-element-positions') this._updatePanelElementPositions() - } + }, ], [ SETTINGS, @@ -241,9 +239,9 @@ export const PanelManager = class { [ Utils.DisplayWrapper.getMonitorManager(), 'monitors-changed', - () => { + async () => { if (Main.layoutManager.primaryMonitor) { - this._saveMonitors() + await PanelSettings.setMonitorsInfo(SETTINGS) this._reset() } }, @@ -492,25 +490,6 @@ export const PanelManager = class { } } - _saveMonitors() { - //Mutter meta_monitor_manager_get_primary_monitor (global.display.get_primary_monitor()) doesn't return the same - //monitor as GDK gdk_screen_get_primary_monitor (imports.gi.Gdk.Screen.get_default().get_primary_monitor()). - //Since the Mutter function is what's used in gnome-shell and we can't access it from the settings dialog, store - //the monitors information in a setting so we can use the same monitor indexes as the ones in gnome-shell - let keyPrimary = 'primary-monitor' - let primaryIndex = Main.layoutManager.primaryIndex - let dtpPrimaryIndex = SETTINGS.get_int(keyPrimary) - - if ( - !Main.layoutManager.monitors[dtpPrimaryIndex] || - dtpPrimaryIndex == primaryIndex - ) - dtpPrimaryIndex = -1 - - SETTINGS.set_int(keyPrimary, dtpPrimaryIndex) - SETTINGS.set_int('gs-primary-monitor', primaryIndex) - } - checkIfFocusedMonitor(monitor) { return ( Main.overview._overview._controls._workspacesDisplay._primaryIndex == @@ -557,10 +536,6 @@ export const PanelManager = class { } _updatePanelElementPositions() { - this.panelsElementPositions = PanelSettings.getSettingsJson( - SETTINGS, - 'panel-element-positions', - ) this.allPanels.forEach((p) => p.updateElementPositions()) } diff --git a/src/panelSettings.js b/src/panelSettings.js index 88a2e11..406ddf9 100644 --- a/src/panelSettings.js +++ b/src/panelSettings.js @@ -15,12 +15,48 @@ * along with this program. If not, see . */ +import Gio from 'gi://Gio' + import * as Pos from './panelPositions.js' -// cache is a different object in the settings dialog (gjs process) +const displayConfigWrapper = Gio.DBusProxy.makeProxyWrapper( + ` + + + + + + + + + `, +) + +// the module variables here are different in the settings dialog (gjs process) // and in gnome-shell (gnome-shell process) +let displayConfigProxy = null +let prefsOpenedId = null +let useCache = false let cache = {} +export var availableMonitors = [] +export var monitorIdToIndex = {} +export var monitorIndexToId = {} + +export async function init(settings) { + useCache = true + prefsOpenedId = settings.connect( + 'changed::prefs-opened', + () => (useCache = !settings.get_boolean('prefs-opened')), + ) + + await setMonitorsInfo(settings) +} + +export async function disable(settings) { + settings.disconnect(prefsOpenedId) +} + export function clearCache(setting) { if (setting) { cache[setting] = null @@ -33,8 +69,7 @@ export function clearCache(setting) { /** Return object representing a settings value that is stored as JSON. */ export function getSettingsJson(settings, setting) { try { - if (cache[setting]) - return cache[setting] + if (useCache && cache[setting]) return cache[setting] let res = JSON.parse(settings.get_string(setting)) @@ -56,13 +91,38 @@ export function setSettingsJson(settings, setting, value) { } } +// Previously, the monitor index was used as an id to persist per monitor +// settings. Since these indexes are unreliable AF, switch to use the monitor +// serial as its id while keeping it backward compatible. +function getMonitorSetting(settings, settingName, monitorIndex, fallback) { + let monitorId = monitorIndexToId[monitorIndex] + + settings = getSettingsJson(settings, settingName) + + return settings[monitorId] || settings[monitorIndex] || fallback +} + +function setMonitorSetting(settings, settingName, monitorIndex, value) { + let monitorId = monitorIndexToId[monitorIndex] + let usedId = monitorId || monitorIndex + + let currentSettings = getSettingsJson(settings, settingName) + + if (monitorId) delete currentSettings[monitorIndex] + + currentSettings[usedId] = value + setSettingsJson(settings, settingName, currentSettings) +} + /** Returns size of panel on a specific monitor, in pixels. */ export function getPanelSize(settings, monitorIndex) { - const sizes = getSettingsJson(settings, 'panel-sizes') // Pull in deprecated setting if panel-sizes does not have setting for monitor. - const fallbackSize = settings.get_int('panel-size') - const theDefault = 48 - return sizes[monitorIndex] || fallbackSize || theDefault + return getMonitorSetting( + settings, + 'panel-sizes', + monitorIndex, + settings.get_int('panel-size') || 48, + ) } export function setPanelSize(settings, monitorIndex, value) { @@ -70,9 +130,8 @@ export function setPanelSize(settings, monitorIndex, value) { console.log('Not setting invalid panel size: ' + value) return } - let sizes = getSettingsJson(settings, 'panel-sizes') - sizes[monitorIndex] = value - setSettingsJson(settings, 'panel-sizes', sizes) + + setMonitorSetting(settings, 'panel-sizes', monitorIndex, value) } /** @@ -80,9 +139,7 @@ export function setPanelSize(settings, monitorIndex, value) { * from settings. e.g. 100 */ export function getPanelLength(settings, monitorIndex) { - const lengths = getSettingsJson(settings, 'panel-lengths') - const theDefault = 100 - return lengths[monitorIndex] || theDefault + return getMonitorSetting(settings, 'panel-lengths', monitorIndex, 100) } export function setPanelLength(settings, monitorIndex, value) { @@ -90,17 +147,18 @@ export function setPanelLength(settings, monitorIndex, value) { console.log('Not setting invalid panel length: ' + value) return } - let lengths = getSettingsJson(settings, 'panel-lengths') - lengths[monitorIndex] = value - setSettingsJson(settings, 'panel-lengths', lengths) + + setMonitorSetting(settings, 'panel-lengths', monitorIndex, value) } /** Returns position of panel on a specific monitor. */ export function getPanelPosition(settings, monitorIndex) { - const positions = getSettingsJson(settings, 'panel-positions') - const fallbackPosition = settings.get_string('panel-position') - const theDefault = Pos.BOTTOM - return positions[monitorIndex] || fallbackPosition || theDefault + return getMonitorSetting( + settings, + 'panel-positions', + monitorIndex, + settings.get_string('panel-position') || Pos.BOTTOM, + ) } export function setPanelPosition(settings, monitorIndex, value) { @@ -115,16 +173,13 @@ export function setPanelPosition(settings, monitorIndex, value) { console.log('Not setting invalid panel position: ' + value) return } - const positions = getSettingsJson(settings, 'panel-positions') - positions[monitorIndex] = value - setSettingsJson(settings, 'panel-positions', positions) + + setMonitorSetting(settings, 'panel-positions', monitorIndex, value) } /** Returns anchor location of panel on a specific monitor. */ export function getPanelAnchor(settings, monitorIndex) { - const anchors = getSettingsJson(settings, 'panel-anchors') - const theDefault = Pos.MIDDLE - return anchors[monitorIndex] || theDefault + return getMonitorSetting(settings, 'panel-anchors', monitorIndex, Pos.MIDDLE) } export function setPanelAnchor(settings, monitorIndex, value) { @@ -132,7 +187,109 @@ export function setPanelAnchor(settings, monitorIndex, value) { console.log('Not setting invalid panel anchor: ' + value) return } - const anchors = getSettingsJson(settings, 'panel-anchors') - anchors[monitorIndex] = value - setSettingsJson(settings, 'panel-anchors', anchors) + + setMonitorSetting(settings, 'panel-anchors', monitorIndex, value) +} + +export function getPanelElementPositions(settings, monitorIndex) { + return getMonitorSetting( + settings, + 'panel-element-positions', + monitorIndex, + Pos.defaults, + ) +} + +export function setPanelElementPositions(settings, monitorIndex, value) { + setMonitorSetting(settings, 'panel-element-positions', monitorIndex, value) +} + +export async function setMonitorsInfo(settings) { + return new Promise((resolve, reject) => { + try { + let monitorInfos = [] + let saveMonitorState = (proxy) => { + proxy.GetCurrentStateRemote((displayInfo, e) => { + if (e) return reject(`Error getting display state: ${e}`) + + let gsPrimaryIndex = 0 + + //https://gitlab.gnome.org/GNOME/mutter/-/blob/main/data/dbus-interfaces/org.gnome.Mutter.DisplayConfig.xml#L347 + displayInfo[2].forEach((logicalMonitor, i) => { + let id = logicalMonitor[5][0][3] + let primary = logicalMonitor[4] + + if (primary) gsPrimaryIndex = i + + monitorInfos.push({ + id, + name: logicalMonitor[5][0][2], + primary, + }) + + monitorIdToIndex[id] = i + monitorIndexToId[i] = id + }) + + _saveMonitors(settings, monitorInfos, gsPrimaryIndex) + + resolve() + }) + } + + if (!displayConfigProxy) + displayConfigProxy = new displayConfigWrapper( + Gio.DBus.session, + 'org.gnome.Mutter.DisplayConfig', + '/org/gnome/Mutter/DisplayConfig', + (proxy, e) => { + if (e) return reject(`Error creating display proxy: ${e}`) + + saveMonitorState(proxy) + }, + ) + else saveMonitorState(displayConfigProxy) + } catch (e) { + reject(e) + } + }) +} + +function _saveMonitors(settings, monitorInfos, gsPrimaryIndex) { + let keyPrimary = 'primary-monitor' + let dtpPrimaryMonitor = settings.get_string(keyPrimary) + + // convert previously saved index to monitor id + if (dtpPrimaryMonitor.match(/^\d{1,2}$/)) + dtpPrimaryMonitor = monitorInfos[dtpPrimaryMonitor]?.id + + // default to gnome-shell primary monitor + if (!dtpPrimaryMonitor) dtpPrimaryMonitor = monitorInfos[gsPrimaryIndex]?.id + + settings.set_string(keyPrimary, dtpPrimaryMonitor) + availableMonitors = Object.freeze(monitorInfos) +} + +// this is for backward compatibility, to remove in a few versions +export function adjustMonitorSettings(settings) { + let updateSettings = (settingName) => { + let monitorSettings = getSettingsJson(settings, settingName) + let updatedSettings = {} + + Object.keys(monitorSettings).forEach((key) => { + let initialKey = key + + if (key.match(/^\d{1,2}$/)) key = monitorIndexToId[key] || key + + updatedSettings[key] = monitorSettings[initialKey] + }) + + setSettingsJson(settings, settingName, updatedSettings) + } + + updateSettings('panel-sizes') + updateSettings('panel-lengths') + updateSettings('panel-positions') + updateSettings('panel-anchors') + updateSettings('panel-element-positions') } diff --git a/src/prefs.js b/src/prefs.js index cd2e0fa..d479e5e 100644 --- a/src/prefs.js +++ b/src/prefs.js @@ -290,7 +290,7 @@ const Preferences = class { ) let topAvailable = !keepTopPanel || - (!monitorSync && this._currentMonitorIndex != this.monitors[0]) + (!monitorSync && !this.monitors[this._currentMonitorIndex].primary) let topRadio = this._builder.get_object('position_top_button') topRadio.set_sensitive(topAvailable) @@ -310,7 +310,7 @@ const Preferences = class { 'panel-element-positions-monitors-sync', ) const monitorsToSetFor = monitorSync - ? this.monitors + ? Object.keys(this.monitors) : [this._currentMonitorIndex] monitorsToSetFor.forEach((monitorIndex) => { PanelSettings.setPanelPosition(this._settings, monitorIndex, position) @@ -447,18 +447,16 @@ const Preferences = class { let labels = {} let panelPosition = this._getPanelPosition(monitorIndex) let isVertical = panelPosition == Pos.LEFT || panelPosition == Pos.RIGHT - let panelElementPositionsSettings = PanelSettings.getSettingsJson( + let panelElementPositions = PanelSettings.getPanelElementPositions( this._settings, - 'panel-element-positions', + monitorIndex, ) - let panelElementPositions = - panelElementPositionsSettings[monitorIndex] || Pos.defaults let updateElementsSettings = () => { let newPanelElementPositions = [] let monitorSync = this._settings.get_boolean( 'panel-element-positions-monitors-sync', ) - let monitors = monitorSync ? this.monitors : [monitorIndex] + let monitors = monitorSync ? Object.keys(this.monitors) : [monitorIndex] let child = taskbarListBox.get_first_child() while (child != null) { @@ -470,13 +468,12 @@ const Preferences = class { child = child.get_next_sibling() } - monitors.forEach( - (m) => (panelElementPositionsSettings[m] = newPanelElementPositions), - ) - PanelSettings.setSettingsJson( - this._settings, - 'panel-element-positions', - panelElementPositionsSettings, + monitors.forEach((m) => + PanelSettings.setPanelElementPositions( + this._settings, + m, + newPanelElementPositions, + ), ) } @@ -1235,24 +1232,11 @@ const Preferences = class { }) //multi-monitor - this.monitors = [] + this.monitors = PanelSettings.availableMonitors - for ( - let i = 0; - i < Gdk.Display.get_default().get_monitors().get_n_items(); - ++i - ) { - this.monitors.push(i) - } - - let primaryMonitorIndex = this._settings.get_int('gs-primary-monitor') - let dtpPrimaryMonitorIndex = this.monitors.indexOf( - this._settings.get_int('primary-monitor'), - ) - - if (dtpPrimaryMonitorIndex < 0) { - dtpPrimaryMonitorIndex = primaryMonitorIndex - } + let dtpPrimaryMonitorId = this._settings.get_string('primary-monitor') + let dtpPrimaryMonitorIndex = + PanelSettings.monitorIdToIndex[dtpPrimaryMonitorId] this._currentMonitorIndex = dtpPrimaryMonitorIndex @@ -1262,10 +1246,12 @@ const Preferences = class { this._updateVerticalRelatedOptions() for (let i = 0; i < this.monitors.length; ++i) { - let label = - i == primaryMonitorIndex - ? _('Primary monitor') - : _('Monitor ') + (i + 1) + let monitor = this.monitors[i] + let label = monitor.primary + ? _('Primary monitor') + : _('Monitor ') + (i + 1) + + label += monitor.name ? ` (${monitor.name})` : '' this._builder.get_object('multimon_primary_combo').append_text(label) this._builder @@ -1306,16 +1292,16 @@ const Preferences = class { this._builder .get_object('multimon_primary_combo') .connect('changed', (widget) => { - this._settings.set_int( + this._settings.set_string( 'primary-monitor', - this.monitors[widget.get_active()], + this.monitors[widget.get_active()].id, ) }) this._builder .get_object('taskbar_position_monitor_combo') .connect('changed', (widget) => { - this._currentMonitorIndex = this.monitors[widget.get_active()] + this._currentMonitorIndex = widget.get_active() this._updateWidgetSettingsForMonitor(this._currentMonitorIndex) }) @@ -1337,7 +1323,7 @@ const Preferences = class { 'panel-element-positions-monitors-sync', ) const monitorsToSetFor = monitorSync - ? this.monitors + ? Object.keys(this.monitors) : [this._currentMonitorIndex] monitorsToSetFor.forEach((monitorIndex) => { PanelSettings.setPanelLength(this._settings, monitorIndex, value) @@ -1356,7 +1342,7 @@ const Preferences = class { 'panel-element-positions-monitors-sync', ) const monitorsToSetFor = monitorSync - ? this.monitors + ? Object.keys(this.monitors) : [this._currentMonitorIndex] monitorsToSetFor.forEach((monitorIndex) => { PanelSettings.setPanelAnchor(this._settings, monitorIndex, value) @@ -3861,7 +3847,9 @@ const BuilderScope = GObject.registerClass( ) export default class DashToPanelPreferences extends ExtensionPreferences { - fillPreferencesWindow(window) { + async fillPreferencesWindow(window) { + let closeRequestId = null + window._settings = this.getSettings( 'org.gnome.shell.extensions.dash-to-panel', ) @@ -3869,6 +3857,14 @@ export default class DashToPanelPreferences extends ExtensionPreferences { // use default width or window window.set_default_size(0, 740) + window._settings.set_boolean('prefs-opened', true) + closeRequestId = window.connect('close-request', () => { + window._settings.set_boolean('prefs-opened', false) + window.disconnect(closeRequestId) + }) + + await PanelSettings.setMonitorsInfo(window._settings) + new Preferences(window, window._settings, this.path) } } diff --git a/src/taskbar.js b/src/taskbar.js index ed86bdc..a791ee4 100644 --- a/src/taskbar.js +++ b/src/taskbar.js @@ -1732,10 +1732,10 @@ export const TaskbarItemContainer = GObject.registerClass( else return let panelPosition = this._dtpPanel.getPosition() - let panelElementPositions = - this._dtpPanel.panelManager.panelsElementPositions[ - this._dtpPanel.monitor.index - ] || Pos.defaults + let panelElementPositions = PanelSettings.getPanelElementPositions( + SETTINGS, + this._dtpPanel.monitor.index, + ) let taskbarPosition = panelElementPositions.filter( (pos) => pos.element == 'taskbar', )[0].position