diff --git a/extension.js b/extension.js index d34bdac..1137148 100644 --- a/extension.js +++ b/extension.js @@ -30,6 +30,7 @@ try { const ExtensionUtils = imports.misc.extensionUtils; const Me = imports.misc.extensionUtils.getCurrentExtension(); +const migrate = Me.imports.src.migrate; const utils = Me.imports.src.utils; const ProfileManager = Me.imports.src.ProfileManager.ProfileManager; const WindowPicker = Me.imports.src.WindowPicker.WindowPicker; @@ -82,6 +83,20 @@ class Extension { // Store a reference to the settings object. this._settings = ExtensionUtils.getSettings(); + // Now we check whether the extension settings need to be migrated from a previous + // version. If this is the case, we defer the profile loading until this is finished. + const lastVersion = this._settings.get_int('last-extension-version'); + if (lastVersion < Me.metadata.version) { + if (lastVersion <= 26) { + migrate.fromVersion26().then(() => { + this._loadProfiles(); + this._settings.set_int('last-extension-version', Me.metadata.version); + }); + } + } else { + this._loadProfiles(); + } + // We reload all effect profiles whenever the currently edited profile in the // preferences dialog changes. This is most likely a bit too often, but it will also // happen whenever a new profile is created and whenever an old profile is deleted. @@ -89,8 +104,6 @@ class Extension { this._loadProfiles(); }); - this._loadProfiles(); - // This is used to get the desktop's color scheme. this._shellSettings = new Gio.Settings({schema: 'org.gnome.desktop.interface'}); diff --git a/prefs.js b/prefs.js index d87f323..5b079f9 100644 --- a/prefs.js +++ b/prefs.js @@ -28,7 +28,6 @@ const _ = imports.gettext.domain('burn-my-windows').gettext; const ExtensionUtils = imports.misc.extensionUtils; const Me = imports.misc.extensionUtils.getCurrentExtension(); -const migrate = Me.imports.src.migrate; const utils = Me.imports.src.utils; const ProfileManager = Me.imports.src.ProfileManager.ProfileManager; @@ -68,8 +67,6 @@ var PreferencesDialog = class PreferencesDialog { new Me.imports.src.effects.Wisps.Wisps(), ]; - migrate.migrate(); - // Load all of our resources. this._resources = Gio.Resource.load(Me.path + '/resources/burn-my-windows.gresource'); Gio.resources_register(this._resources); @@ -358,7 +355,7 @@ var PreferencesDialog = class PreferencesDialog { // Show an Adw.Toast or a Gtk.InfoBar whenever Burn-My-Windows was updated. We use a // small timeout so that it is not shown instantaneously. - const lastVersion = this._settings.get_int('last-version'); + const lastVersion = this._settings.get_int('last-prefs-version'); if (lastVersion < Me.metadata.version) { this._showUpdateInfoTimeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 2000, () => { @@ -372,13 +369,13 @@ var PreferencesDialog = class PreferencesDialog { toast.connect( 'dismissed', - () => this._settings.set_int('last-version', Me.metadata.version)); + () => this._settings.set_int('last-prefs-version', Me.metadata.version)); window.add_toast(toast); } else { const infoBar = this._builder.get_object('update-info'); infoBar.connect('response', i => { - this._settings.set_int('last-version', Me.metadata.version); + this._settings.set_int('last-prefs-version', Me.metadata.version); i.set_revealed(false); }); infoBar.set_revealed(true); @@ -459,7 +456,7 @@ var PreferencesDialog = class PreferencesDialog { null, 'https://github.com/Schneegans/Burn-My-Windows/blob/main/docs/changelog.md', Gdk.CURRENT_TIME); - this._settings.set_int('last-version', Me.metadata.version) + this._settings.set_int('last-prefs-version', Me.metadata.version) }); group.add_action(changelogAction); diff --git a/schemas/org.gnome.shell.extensions.burn-my-windows.gschema.xml b/schemas/org.gnome.shell.extensions.burn-my-windows.gschema.xml index 8739d9c..50fd50e 100644 --- a/schemas/org.gnome.shell.extensions.burn-my-windows.gschema.xml +++ b/schemas/org.gnome.shell.extensions.burn-my-windows.gschema.xml @@ -31,10 +31,16 @@ SPDX-License-Identifier: CC0-1.0 If set to true, all animations will show only one still frame to allow for continuous integration tests. - + 0 - Last Version - Used to check whether the extension got updated. + Last Prefs Version + Used to check whether the extension got updated from the preferences dialog. + + + + 0 + Last Extension Version + Used to check whether the extension got updated from the extension side. diff --git a/src/ProfileManager.js b/src/ProfileManager.js index c655e27..4d1c81e 100644 --- a/src/ProfileManager.js +++ b/src/ProfileManager.js @@ -56,15 +56,17 @@ var ProfileManager = class { return this._profiles; } - // This will create a new profile with all values initialized to their defaults. The - // method will return an effect profile object (as described above). We will use the - // current system time in microseconds as profile name. This ensures that they are - // always sorted according to their creation date. - createProfile() { + // This will create a new profile. If no content is given, all values will be + // initialized to their defaults. The method will return an effect profile object (as + // described above). We will use the current system time in microseconds as profile + // name. This ensures that they are always sorted according to their creation date. + createProfile(content = '') { const path = `${GLib.get_user_config_dir()}/burn-my-windows/profiles/${ GLib.get_real_time()}.conf`; const file = Gio.File.new_for_path(path); file.create(Gio.FileCreateFlags.NONE, null); + file.replace_contents(content, null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, + null); const profile = {'path': path, 'settings': this._getProfileSettings(path)}; diff --git a/src/migrate.js b/src/migrate.js index 382b535..7ae8788 100644 --- a/src/migrate.js +++ b/src/migrate.js @@ -16,27 +16,35 @@ const ExtensionUtils = imports.misc.extensionUtils; const Me = imports.misc.extensionUtils.getCurrentExtension(); +const ProfileManager = Me.imports.src.ProfileManager.ProfileManager; const utils = Me.imports.src.utils; -function migrate() { - utils.executeCommand(['dconf', 'dump', '/org/gnome/shell/extensions/burn-my-windows/']) +// Migrating from pre-27 versions is pretty involved. Before, all settings were stored in +// the standard Gio.Settings backend. Now, there are effect profiles. Depending on the +// settings one or two profiles may be required. +async function fromVersion26() { + return utils + .executeCommand(['dconf', 'dump', '/org/gnome/shell/extensions/burn-my-windows/']) .catch(r => utils.debug('Failed to get old settings for effect migration: ' + r)) .then(r => { - utils.debug('Starting Burn-My-Windows profile migration!'); + utils.debug('Starting Burn-My-Windows profile migration (old version <= 26)!'); - // The default value of this is false, so not-present is equal to false. + // We use this to write the new profiles. + const profileManager = new ProfileManager(); + + // The default value of this was false, so not-present is equal to false. const destroyDialogs = r.includes('destroy-dialogs=true'); if (!destroyDialogs) { utils.debug('Only normal windows will be burned by the new profile(s).'); } - // The default value of this is false, so not-present is equal to false. + // The default value of this was false, so not-present is equal to false. const disableOnBattery = r.includes('disable-on-battery=true'); if (disableOnBattery) { utils.debug('The new profile(s) will not be active on battery.'); } - // The default value of this is false, so not-present is equal to false. + // The default value of this was false, so not-present is equal to false. const disableOnPowerSave = r.includes('disable-on-power-save=true'); if (disableOnPowerSave) { utils.debug('The new profile(s) will not be active in power-save mode.'); @@ -45,7 +53,8 @@ function migrate() { // Remove some unnecessary lines. r = r.replace(/^active-profile=.*\n?/gm, ''); r = r.replace(/^test-mode=.*\n?/gm, ''); - r = r.replace(/^last-version=.*\n?/gm, ''); + r = r.replace(/^last-extension-version=.*\n?/gm, ''); + r = r.replace(/^last-prefs-version=.*\n?/gm, ''); r = r.replace(/^destroy-dialogs=.*\n?/gm, ''); r = r.replace(/^disable-on-battery=.*\n?/gm, ''); r = r.replace(/^disable-on-power-save=.*\n?/gm, ''); @@ -53,15 +62,14 @@ function migrate() { r = r.replace('[/]\n', ''); r = r.trim(); - // There were some inconsistencies in the key names. + // There were some inconsistencies in the key names. The update fixes them. r.replace('flame-', 'fire-'); r.replace('claw-', 'trex-'); - let lines = r.split('\n'); - // Find all effects which were used for openeing and closing. This first extracts // all lines which end in "-open-effect=true" and then remove this suffix from the // lines. This leaves only the effect nicks. + let lines = r.split('\n'); let openEffects = lines.filter(l => l.includes('-open-effect=true')) .map(l => l.replace('-open-effect=true', '')); let closeEffects = lines.filter(l => l.includes('-close-effect=true')) @@ -73,11 +81,13 @@ function migrate() { closeEffects.push('fire'); } + // Print all effects we've found. utils.debug('Enabled open effects: ' + openEffects); utils.debug('Enabled close effects: ' + closeEffects); - // If the same effects were used for opening and closing windows, only one profile - // is required. Else we have to create two new profiles. + // If the same effects were used for opening and closing windows or there is either + // no opening or no closing animation configured, only one profile is required. Else + // we have to create two new profiles. const numProfiles = openEffects.join() == closeEffects.join() ? 1 : 2; if (numProfiles == 1) { utils.debug('Only one profile is required.'); @@ -89,69 +99,121 @@ function migrate() { utils.debug('Two profiles are required.'); } - const profile = + // Remove all open / close lines. + const profileLines = lines.filter(l => !l.includes('-close-effect=') && !l.includes('-open-effect=')); - profile.unshift('[burn-my-windows-profile]'); + // Add the header. + profileLines.unshift('[burn-my-windows-profile]'); + + // Push the line which makes the profile only apply to normal windows. if (!destroyDialogs) { - profile.push('profile-window-type=1'); + profileLines.push('profile-window-type=1'); } + // Push the line which makes the profile only active when plugged in. if (disableOnBattery) { - profile.push('profile-power-mode=2'); + profileLines.push('profile-power-mode=2'); } + // Push the line which makes the profile only active when on balanced or performance + // mode. if (disableOnPowerSave) { - profile.push('profile-power-profile=5'); + profileLines.push('profile-power-profile=5'); } + // Now comes a bit of code repetition, but it is more readable this way. if (numProfiles == 1) { - openEffects.forEach(e => profile.push(e + '-enable-effect=true')); + + // Add all relevant effects. + openEffects.forEach(e => profileLines.push(e + '-enable-effect=true')); + + // The fire effect is enabled per default, so we have to explicitly disable it if + // not required. if (!openEffects.includes('fire')) { - profile.push('fire-enable-effect=false'); + profileLines.push('fire-enable-effect=false'); } + // Print and save the new profile. + const profile = profileLines.join('\n'); + utils.debug('The new profile:'); - utils.debug(profile.join('\n')); + utils.debug(profile); + + profileManager.createProfile(profile); } else if (closeEffects.length == 0) { - profile.push('profile-animation-type=1') - openEffects.forEach(e => profile.push(e + '-enable-effect=true')); + // This profile is only for window openeing. + profileLines.push('profile-animation-type=1') + + // Add all relevant effects. + openEffects.forEach(e => profileLines.push(e + '-enable-effect=true')); + + // The fire effect is enabled per default, so we have to explicitly disable it if + // not required. if (!openEffects.includes('fire')) { - profile.push('fire-enable-effect=false'); + profileLines.push('fire-enable-effect=false'); } + // Print and save the new profile. + const profile = profileLines.join('\n'); + utils.debug('The new window-open profile:'); - utils.debug(profile.join('\n')); + utils.debug(profile); + + profileManager.createProfile(profile); } else if (openEffects.length == 0) { - profile.push('profile-animation-type=2') - closeEffects.forEach(e => profile.push(e + '-enable-effect=true')); + // This profile is only for window closing. + profileLines.push('profile-animation-type=2') + + // Add all relevant effects. + closeEffects.forEach(e => profileLines.push(e + '-enable-effect=true')); + + // The fire effect is enabled per default, so we have to explicitly disable it if + // not required. if (!closeEffects.includes('fire')) { - profile.push('fire-enable-effect=false'); + profileLines.push('fire-enable-effect=false'); } + // Print and save the new profile. + const profile = profileLines.join('\n'); + utils.debug('The new window-close profile:'); - utils.debug(profile.join('\n')); + utils.debug(profile); + + profileManager.createProfile(profile); + } else { - const openProfile = [...profile, 'profile-animation-type=1']; - const closeProfile = [...profile, 'profile-animation-type=2']; + const openProfileLines = [...profile, 'profile-animation-type=1']; + const closeProfileLines = [...profile, 'profile-animation-type=2']; - openEffects.forEach(e => openProfile.push(e + '-enable-effect=true')); - closeEffects.forEach(e => closeProfile.push(e + '-enable-effect=true')); + // Add all relevant effects. + openEffects.forEach(e => openProfileLines.push(e + '-enable-effect=true')); + closeEffects.forEach(e => closeProfileLines.push(e + '-enable-effect=true')); + + // The fire effect is enabled per default, so we have to explicitly disable it if + // not required. if (!closeEffects.includes('fire')) { - closeProfile.push('fire-enable-effect=false'); + closeProfileLines.push('fire-enable-effect=false'); } + // Print and save the new profiles. + const openProfile = openProfileLines.join('\n'); + const closeProfile = closeProfileLines.join('\n'); + utils.debug('The new open-window profile:'); - utils.debug(openProfile.join('\n')); + utils.debug(openProfile); utils.debug('The new close-window profile:'); - utils.debug(closeProfile.join('\n')); + utils.debug(closeProfile); + + profileManager.createProfile(openProfile); + profileManager.createProfile(closeProfile); } }) - .catch(r => utils.debug('Failed migrate settings: ' + r)); + .catch(r => utils.debug('Failed to migrate settings: ' + r)); } \ No newline at end of file