From 2d813cbdd8d29c9f73403ddeacf057dac3a8f25d Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Wed, 22 Jun 2011 21:56:24 -0400 Subject: [PATCH] extensionSystem: Implement new live enable/disable system The rough sketches of the system are outlined here: http://mail.gnome.org/archives/gnome-shell-list/2011-June/msg00283.html Additionally, enable/disable extensions when the 'enabled-extensions' setting changes. Two new DBus methods, "EnableExtension" and "DisableExtension" allow users to manipulate this setting quite easily. https://bugzilla.gnome.org/show_bug.cgi?id=654770 --- js/ui/extensionSystem.js | 111 ++++++++++++++++++++++++++---- js/ui/shellDBus.js | 22 ++++++ src/gnome-shell-extension-tool.in | 22 +++--- 3 files changed, 134 insertions(+), 21 deletions(-) diff --git a/js/ui/extensionSystem.js b/js/ui/extensionSystem.js index c4723a0cb..6bd6b9177 100644 --- a/js/ui/extensionSystem.js +++ b/js/ui/extensionSystem.js @@ -14,7 +14,11 @@ const ExtensionState = { ENABLED: 1, DISABLED: 2, ERROR: 3, - OUT_OF_DATE: 4 + OUT_OF_DATE: 4, + + // Used as an error state for operations on unknown extensions, + // should never be in a real extensionMeta object. + UNINSTALLED: 99 }; const ExtensionType = { @@ -26,6 +30,8 @@ const ExtensionType = { const extensionMeta = {}; // Maps uuid -> importer object (extension directory tree) const extensions = {}; +// Maps uuid -> extension state object (returned from init()) +const extensionStateObjs = {}; // Arrays of uuids var enabledExtensions; // GFile for user extensions @@ -75,6 +81,46 @@ function versionCheck(required, current) { return false; } +function disableExtension(uuid) { + let meta = extensionMeta[uuid]; + if (!meta) + return; + + if (meta.state != ExtensionState.ENABLED) + return; + + let extensionState = extensionStateObjs[uuid]; + + try { + extensionState.disable(); + } catch(e) { + logExtensionError(uuid, e.toString()); + return; + } + + meta.state = ExtensionState.DISABLED; +} + +function enableExtension(uuid) { + let meta = extensionMeta[uuid]; + if (!meta) + return; + + if (meta.state != ExtensionState.DISABLED) + return; + + let extensionState = extensionStateObjs[uuid]; + + try { + extensionState.enable(); + } catch(e) { + logExtensionError(uuid, e.toString()); + return; + } + + meta.state = ExtensionState.ENABLED; +} + function logExtensionError(uuid, message) { if (!errors[uuid]) errors[uuid] = []; errors[uuid].push(message); @@ -136,16 +182,12 @@ function loadExtension(dir, enabled, type) { return; } - extensionMeta[meta.uuid] = meta; - extensionMeta[meta.uuid].type = type; - extensionMeta[meta.uuid].path = dir.get_path(); - if (!enabled) { - extensionMeta[meta.uuid].state = ExtensionState.DISABLED; - return; - } + extensionMeta[uuid] = meta; + meta.type = type; + meta.path = dir.get_path(); // Default to error, we set success as the last step - extensionMeta[meta.uuid].state = ExtensionState.ERROR; + meta.state = ExtensionState.ERROR; let extensionJs = dir.get_child('extension.js'); if (!extensionJs.query_exists(null)) { @@ -166,6 +208,7 @@ function loadExtension(dir, enabled, type) { } let extensionModule; + let extensionState = null; try { global.add_extension_importer('imports.ui.extensionSystem.extensions', meta.uuid, dir.get_path()); extensionModule = extensions[meta.uuid].extension; @@ -175,24 +218,65 @@ function loadExtension(dir, enabled, type) { logExtensionError(uuid, e); return; } - if (!extensionModule.main) { - logExtensionError(uuid, 'missing \'main\' function'); + + if (!extensionModule.init) { + logExtensionError(uuid, 'missing \'init\' function'); return; } + try { - extensionModule.main(meta); + extensionState = extensionModule.init(meta); } catch (e) { if (stylesheetPath != null) theme.unload_stylesheet(stylesheetPath); logExtensionError(uuid, 'Failed to evaluate init function:' + e); return; } - extensionMeta[meta.uuid].state = ExtensionState.ENABLED; + + if (!extensionState) + extensionState = extensionModule; + extensionStateObjs[uuid] = extensionState; + + if (!extensionState.enable) { + logExtensionError(uuid, 'missing \'enable\' function'); + return; + } + if (!extensionState.disable) { + logExtensionError(uuid, 'missing \'disable\' function'); + return; + } + + meta.state = ExtensionState.DISABLED; + + if (enabled) + enableExtension(uuid); _signals.emit('extension-loaded', meta.uuid); global.log('Loaded extension ' + meta.uuid); } +function onEnabledExtensionsChanged() { + let newEnabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY); + + // Find and enable all the newly enabled extensions: UUIDs found in the + // new setting, but not in the old one. + newEnabledExtensions.filter(function(uuid) { + return enabledExtensions.indexOf(uuid) == -1; + }).forEach(function(uuid) { + enableExtension(uuid); + }); + + // Find and disable all the newly disabled extensions: UUIDs found in the + // old setting, but not in the new one. + enabledExtensions.filter(function(item) { + return newEnabledExtensions.indexOf(item) == -1; + }).forEach(function(uuid) { + disableExtension(uuid); + }); + + enabledExtensions = newEnabledExtensions; +} + function init() { let userExtensionsPath = GLib.build_filenamev([global.userdatadir, 'extensions']); userExtensionsDir = Gio.file_new_for_path(userExtensionsPath); @@ -202,6 +286,7 @@ function init() { global.logError('' + e); } + global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged); enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY); } diff --git a/js/ui/shellDBus.js b/js/ui/shellDBus.js index 03adae927..ae679ad52 100644 --- a/js/ui/shellDBus.js +++ b/js/ui/shellDBus.js @@ -35,6 +35,14 @@ const GnomeShellIface = { { name: 'Screenshot', inSignature: 's', outSignature: 'b' + }, + { name: 'EnableExtension', + inSignature: 's', + outSignature: '' + }, + { name: 'DisableExtension', + inSignature: 's', + outSignature: '' } ], signals: [], @@ -144,6 +152,20 @@ GnomeShell.prototype = { return ExtensionSystem.errors[uuid] || []; }, + EnableExtension: function(uuid) { + let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY); + if (enabledExtensions.indexOf(uuid) == -1) + enabledExtensions.push(uuid); + global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions); + }, + + DisableExtension: function(uuid) { + let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY); + while (enabledExtensions.indexOf(uuid) != -1) + enabledExtensions.splice(enabledExtensions.indexOf(uuid), 1); + global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions); + }, + get OverviewActive() { return Main.overview.visible; }, diff --git a/src/gnome-shell-extension-tool.in b/src/gnome-shell-extension-tool.in index 46935732a..e610b462f 100644 --- a/src/gnome-shell-extension-tool.in +++ b/src/gnome-shell-extension-tool.in @@ -33,7 +33,7 @@ const St = imports.gi.St; const Main = imports.ui.main; const Tweener = imports.ui.tweener; -let text; +let text, button; function _hideHello() { Main.uiGroup.remove_actor(text); @@ -60,22 +60,28 @@ function _showHello() { onComplete: _hideHello }); } -function main() { - let button = new St.Bin({ style_class: 'panel-button', - reactive: true, - can_focus: true, - x_fill: true, - y_fill: false, - track_hover: true }); +function init() { + button = new St.Bin({ style_class: 'panel-button', + reactive: true, + can_focus: true, + x_fill: true, + y_fill: false, + track_hover: true }); let icon = new St.Icon({ icon_name: 'system-run', icon_type: St.IconType.SYMBOLIC, style_class: 'system-status-icon' }); button.set_child(icon); button.connect('button-press-event', _showHello); +} +function enable() { Main.panel._rightBox.insert_actor(button, 0); } + +function disable() { + Main.panel._rightBox.remove_actor(button); +} """, "stylesheet.css": """