%PDF- %PDF-
Direktori : /usr/share/gnome-shell/ |
Current File : //usr/share/gnome-shell/org.gnome.Extensions.src.gresource |
GVariant p ( N | p v � 7 �H�. 7 v @ ?% ��Q� ?% v P% -4 ��0* -4 v @4 ?d VCb� ?d L Dd Ld Oq�e Ld v `d �l KP� �l L �l �l �s� �l L �l �l Ե �����l L �l �l ��$0 �l L m m � �� m v m Jm �0I Jm L Pm dm extensionsWindow.js � import Adw from 'gi://Adw?version=1'; import GLib from 'gi://GLib'; import Gio from 'gi://Gio'; import GObject from 'gi://GObject'; import Shew from 'gi://Shew'; const Package = imports.package; import * as Gettext from 'gettext'; import * as Config from './misc/config.js'; import {ExtensionRow} from './extensionRow.js'; Gio._promisify(Adw.AlertDialog.prototype, 'choose'); Gio._promisify(Gio.DBusConnection.prototype, 'call'); Gio._promisify(Shew.WindowExporter.prototype, 'export'); export const ExtensionsWindow = GObject.registerClass({ GTypeName: 'ExtensionsWindow', Template: 'resource:///org/gnome/Extensions/ui/extensions-window.ui', InternalChildren: [ 'sortModel', 'searchFilter', 'userListModel', 'systemListModel', 'searchListModel', 'userGroup', 'userList', 'systemGroup', 'systemList', 'searchList', 'mainStack', 'searchBar', 'searchEntry', 'updatesBanner', ], }, class ExtensionsWindow extends Adw.ApplicationWindow { _init(params) { super._init(params); if (Config.PROFILE === 'development') this.add_css_class('devel'); this._exporter = new Shew.WindowExporter({window: this}); this._exportedHandle = ''; this.add_action_entries( [{ name: 'show-about', activate: () => this._showAbout(), }, { name: 'logout', activate: () => this._logout(), }, { name: 'user-extensions-enabled', state: 'false', change_state: (a, state) => { const {extensionManager} = this.application; extensionManager.userExtensionsEnabled = state.get_boolean(); }, }]); const settings = new Gio.Settings({ schema_id: 'org.gnome.Extensions', }); settings.bind('window-width', this, 'default-width', Gio.SettingsBindFlags.DEFAULT); settings.bind('window-height', this, 'default-height', Gio.SettingsBindFlags.DEFAULT); settings.bind('window-maximized', this, 'maximized', Gio.SettingsBindFlags.DEFAULT); this._searchEntry.connect('search-changed', () => (this._searchFilter.search = this._searchEntry.text)); this._searchBar.connect('notify::search-mode-enabled', () => this._syncVisiblePage()); this._searchListModel.connect('notify::n-items', () => this._syncVisiblePage()); this._userList.connect('row-activated', (_list, row) => row.activate()); this._userGroup.connect('notify::visible', () => this._syncVisiblePage()); this._systemList.connect('row-activated', (_list, row) => row.activate()); this._systemGroup.connect('notify::visible', () => this._syncVisiblePage()); const {extensionManager} = this.application; extensionManager.connect('notify::failed', () => this._syncVisiblePage()); extensionManager.connect('notify::n-updates', () => this._checkUpdates()); extensionManager.connect('notify::user-extensions-enabled', this._onUserExtensionsEnabledChanged.bind(this)); this._onUserExtensionsEnabledChanged(); this._sortModel.model = extensionManager.extensions; this._userList.bind_model(this._userListModel, extension => new ExtensionRow(extension)); this._systemList.bind_model(this._systemListModel, extension => new ExtensionRow(extension)); this._searchList.bind_model(this._searchListModel, extension => new ExtensionRow(extension)); extensionManager.connect('extensions-loaded', () => this._extensionsLoaded()); } async uninstall(extension) { const dialog = new Adw.AlertDialog({ heading: _('Remove “%s”?').format(extension.name), body: _('If you remove the extension, you need to return to download it if you want to enable it again'), }); dialog.add_response('cancel', _('_Cancel')); dialog.add_response('remove', _('_Remove')); dialog.set_response_appearance('remove', Adw.ResponseAppearance.DESTRUCTIVE); const {extensionManager} = this.application; const response = await dialog.choose(this, null); if (response === 'remove') extensionManager.uninstallExtension(extension.uuid); } async openPrefs(extension) { if (!this._exportedHandle) { try { this._exportedHandle = await this._exporter.export(); } catch (e) { console.warn(`Failed to export window: ${e.message}`); } } const {extensionManager} = this.application; extensionManager.openExtensionPrefs(extension.uuid, this._exportedHandle); } _showAbout() { const [version] = Package.version.split(' '); const aboutDialog = Adw.AboutDialog.new_from_appdata( '/org/gnome/Extensions/metainfo.xml', version); aboutDialog.set({ developers: [ 'Florian Müllner <fmuellner@gnome.org>', 'Jasper St. Pierre <jstpierre@mecheye.net>', 'Didier Roche <didrocks@ubuntu.com>', 'Romain Vigier <contact@romainvigier.fr>', ], designers: [ 'Allan Day <allanpday@gmail.com>', 'Tobias Bernard <tbernard@gnome.org>', ], translator_credits: _('translator-credits'), version: Package.version, }); aboutDialog.present(this); } _logout() { this.application.get_dbus_connection().call( 'org.gnome.SessionManager', '/org/gnome/SessionManager', 'org.gnome.SessionManager', 'Logout', new GLib.Variant('(u)', [0]), null, Gio.DBusCallFlags.NONE, -1, null); } _onUserExtensionsEnabledChanged() { const {userExtensionsEnabled} = this.application.extensionManager; const action = this.lookup_action('user-extensions-enabled'); action.set_state(new GLib.Variant('b', userExtensionsEnabled)); } _syncVisiblePage() { const {extensionManager} = this.application; const {searchModeEnabled} = this._searchBar; if (extensionManager.failed) this._mainStack.visible_child_name = 'noshell'; else if (searchModeEnabled && this._searchListModel.get_n_items() > 0) this._mainStack.visible_child_name = 'search'; else if (searchModeEnabled) this._mainStack.visible_child_name = 'noresults'; else if (this._userGroup.visible || this._systemGroup.visible) this._mainStack.visible_child_name = 'main'; else this._mainStack.visible_child_name = 'placeholder'; } _checkUpdates() { const {nUpdates} = this.application.extensionManager; this._updatesBanner.title = Gettext.ngettext( '%d extension will be updated on next login.', '%d extensions will be updated on next login.', nUpdates).format(nUpdates); this._updatesBanner.revealed = nUpdates > 0; } _extensionsLoaded() { this._syncVisiblePage(); this._checkUpdates(); } }); (uuay)main.js � import Adw from 'gi://Adw?version=1'; import GLib from 'gi://GLib'; import GObject from 'gi://GObject'; import {setConsoleLogDomain} from 'console'; const Package = imports.package; Package.initFormat(); import {ExtensionManager} from './extensionManager.js'; import {ExtensionsWindow} from './extensionsWindow.js'; var Application = GObject.registerClass( class Application extends Adw.Application { _init() { GLib.set_prgname('gnome-extensions-app'); super._init({ application_id: Package.name, version: Package.version, }); this.connect('window-removed', (a, window) => window.run_dispose()); } get extensionManager() { return this._extensionManager; } vfunc_activate() { this._extensionManager.checkForUpdates(); this._window.present(); } vfunc_startup() { super.vfunc_startup(); this.add_action_entries( [{ name: 'quit', activate: () => this._window.close(), }]); this.set_accels_for_action('app.quit', ['<Primary>q']); this._extensionManager = new ExtensionManager(); this._window = new ExtensionsWindow({application: this}); } }); /** * Main entrypoint for the app * * @param {string[]} argv - command line arguments * @returns {void} */ export async function main(argv) { Package.initGettext(); setConsoleLogDomain('Extensions'); await new Application().runAsync(argv); } (uuay)extensionRow.js � import Adw from 'gi://Adw?version=1'; import Gio from 'gi://Gio'; import GObject from 'gi://GObject'; import {ExtensionState} from './misc/extensionUtils.js'; import {Extension} from './extensionManager.js'; export const ExtensionRow = GObject.registerClass({ GTypeName: 'ExtensionRow', Template: 'resource:///org/gnome/Extensions/ui/extension-row.ui', Properties: { 'extension': GObject.ParamSpec.object( 'extension', null, null, GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, Extension), }, InternalChildren: [ 'detailsPopover', 'versionLabel', 'switch', 'actionsBox', ], }, class ExtensionRow extends Adw.ActionRow { constructor(extension) { super({extension}); this._app = Gio.Application.get_default(); this._actionGroup = new Gio.SimpleActionGroup(); this.insert_action_group('row', this._actionGroup); const actionEntries = [ { name: 'show-prefs', activate: () => { this._detailsPopover.popdown(); this.get_root().openPrefs(extension); }, enabledProp: 'has-prefs', }, { name: 'show-url', activate: () => { this._detailsPopover.popdown(); Gio.AppInfo.launch_default_for_uri( extension.url, this.get_display().get_app_launch_context()); }, enabledProp: 'url', enabledTransform: s => s !== '', }, { name: 'uninstall', activate: () => { this._detailsPopover.popdown(); this.get_root().uninstall(extension).catch(logError); }, enabledProp: 'is-user', }, ]; this._actionGroup.add_action_entries(actionEntries); this._bindActionEnabled(actionEntries); this._switch.connect('state-set', (sw, state) => { const {uuid, enabled} = this._extension; if (enabled === state) return true; if (state) this._app.extensionManager.enableExtension(uuid); else this._app.extensionManager.disableExtension(uuid); return true; }); this._extension.bind_property_full('state', this._switch, 'state', GObject.BindingFlags.SYNC_CREATE, (bind, source) => [true, source === ExtensionState.ACTIVE], null); this._extension.bind_property_full('version', this._versionLabel, 'label', GObject.BindingFlags.SYNC_CREATE, (bind, source) => [true, _('Version %s').format(source)], null); } get extension() { return this._extension ?? null; } set extension(ext) { this._extension = ext; } _bindActionEnabled(entries) { for (const entry of entries) { const {name, enabledProp, enabledTransform} = entry; if (!enabledProp) continue; const action = this._actionGroup.lookup_action(name); if (enabledTransform) { this._extension.bind_property_full(enabledProp, action, 'enabled', GObject.BindingFlags.SYNC_CREATE, (bind, source) => [true, enabledTransform(source)], null); } else { this._extension.bind_property(enabledProp, action, 'enabled', GObject.BindingFlags.SYNC_CREATE); } } } }); (uuay)extensionManager.js�/ import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; import GObject from 'gi://GObject'; import { ExtensionState, ExtensionType, deserializeExtension } from './misc/extensionUtils.js'; const GnomeShellIface = loadInterfaceXML('org.gnome.Shell.Extensions'); const GnomeShellProxy = Gio.DBusProxy.makeProxyWrapper(GnomeShellIface); let shellVersion; function loadInterfaceXML(iface) { const uri = `resource:///org/gnome/Extensions/dbus-interfaces/${iface}.xml`; const f = Gio.File.new_for_uri(uri); try { let [ok_, bytes] = f.load_contents(null); return new TextDecoder().decode(bytes); } catch (e) { console.error(`Failed to load D-Bus interface ${iface}`); } return null; } const Extension = GObject.registerClass({ GTypeName: 'Extension', Properties: { 'uuid': GObject.ParamSpec.string( 'uuid', null, null, GObject.ParamFlags.READABLE, ''), 'name': GObject.ParamSpec.string( 'name', null, null, GObject.ParamFlags.READABLE, ''), 'description': GObject.ParamSpec.string( 'description', null, null, GObject.ParamFlags.READABLE, ''), 'state': GObject.ParamSpec.int( 'state', null, null, GObject.ParamFlags.READABLE, 1, 99, ExtensionState.INITIALIZED), 'enabled': GObject.ParamSpec.boolean( 'enabled', null, null, GObject.ParamFlags.READABLE, false), 'creator': GObject.ParamSpec.string( 'creator', null, null, GObject.ParamFlags.READABLE, ''), 'url': GObject.ParamSpec.string( 'url', null, null, GObject.ParamFlags.READABLE, ''), 'version': GObject.ParamSpec.string( 'version', null, null, GObject.ParamFlags.READABLE, ''), 'error': GObject.ParamSpec.string( 'error', null, null, GObject.ParamFlags.READABLE, ''), 'has-error': GObject.ParamSpec.boolean( 'has-error', null, null, GObject.ParamFlags.READABLE, false), 'has-prefs': GObject.ParamSpec.boolean( 'has-prefs', null, null, GObject.ParamFlags.READABLE, false), 'has-update': GObject.ParamSpec.boolean( 'has-update', null, null, GObject.ParamFlags.READABLE, false), 'has-version': GObject.ParamSpec.boolean( 'has-version', null, null, GObject.ParamFlags.READABLE, false), 'can-change': GObject.ParamSpec.boolean( 'can-change', null, null, GObject.ParamFlags.READABLE, false), 'is-user': GObject.ParamSpec.boolean( 'is-user', null, null, GObject.ParamFlags.READABLE, false), }, }, class Extension extends GObject.Object { constructor(variant) { super(); this.update(variant); } update(variant) { const deserialized = deserializeExtension(variant); const { uuid, type, state, enabled, error, hasPrefs, hasUpdate, canChange, metadata, } = deserialized; if (!this._uuid) this._uuid = uuid; if (this._uuid !== uuid) throw new Error(`Invalid update of extension ${this._uuid} with data from ${uuid}`); this.freeze_notify(); const {name} = metadata; if (this._name !== name) { this._name = name; this.notify('name'); } const [desc] = metadata.description.split('\n'); if (this._description !== desc) { this._description = desc; this.notify('description'); } if (this._type !== type) { this._type = type; this.notify('is-user'); } if (this._errorDetail !== error) { this._errorDetail = error; this.notify('error'); } if (this._enabled !== enabled) { this._enabled = enabled; this.notify('enabled'); } if (this._state !== state) { const hadError = this.hasError; this._state = state; this.notify('state'); // Compat with older shell versions if (this._enabled === undefined) this.notify('enabled'); if (this.hasError !== hadError) { this.notify('has-error'); this.notify('error'); } } const creator = metadata.creator ?? ''; if (this._creator !== creator) { this._creator = creator; this.notify('creator'); } const url = metadata.url ?? ''; if (this._url !== url) { this._url = url; this.notify('url'); } const version = String( metadata['version-name'] || metadata['version'] || ''); if (this._version !== version) { this._version = version; this.notify('version'); this.notify('has-version'); } if (this._hasPrefs !== hasPrefs) { this._hasPrefs = hasPrefs; this.notify('has-prefs'); } if (this._hasUpdate !== hasUpdate) { this._hasUpdate = hasUpdate; this.notify('has-update'); } if (this._canChange !== canChange) { this._canChange = canChange; this.notify('can-change'); } this.thaw_notify(); } get uuid() { return this._uuid; } get name() { return this._name; } get description() { return this._description; } get state() { return this._state; } get enabled() { // Compat with older shell versions if (this._enabled === undefined) { return this.state === ExtensionState.ACTIVE || this.state === ExtensionState.ACTIVATING; } return this._enabled; } get creator() { return this._creator; } get url() { return this._url; } get version() { return this._version; } get error() { if (!this.hasError) return ''; if (this.state === ExtensionState.OUT_OF_DATE) { return this.version !== '' ? _('The installed version of this extension (%s) is incompatible with the current version of GNOME (%s). The extension has been disabled.').format(this.version, shellVersion) : _('The installed version of this extension is incompatible with the current version of GNOME (%s). The extension has been disabled.').format(shellVersion); } const message = [ _('An error has occurred in this extension. This could cause issues elsewhere in the system. It is recommended to turn the extension off until the error is resolved.'), ]; if (this._errorDetail) { message.push( // translators: Details for an extension error _('Error details:'), this._errorDetail); } return message.join('\n\n'); } get hasError() { return this.state === ExtensionState.OUT_OF_DATE || this.state === ExtensionState.ERROR; } get hasPrefs() { return this._hasPrefs; } get hasUpdate() { return this._hasUpdate; } get hasVersion() { return this._version !== ''; } get canChange() { return this._canChange; } get isUser() { return this._type === ExtensionType.PER_USER; } }); const {$gtype: TYPE_EXTENSION} = Extension; export {TYPE_EXTENSION as Extension}; export const ExtensionManager = GObject.registerClass({ Properties: { 'user-extensions-enabled': GObject.ParamSpec.boolean( 'user-extensions-enabled', null, null, GObject.ParamFlags.READWRITE, true), 'extensions': GObject.ParamSpec.object( 'extensions', null, null, GObject.ParamFlags.READABLE, Gio.ListModel), 'n-updates': GObject.ParamSpec.int( 'n-updates', null, null, GObject.ParamFlags.READABLE, 0, 999, 0), 'failed': GObject.ParamSpec.boolean( 'failed', null, null, GObject.ParamFlags.READABLE, false), }, Signals: { 'extensions-loaded': {}, }, }, class ExtensionManager extends GObject.Object { constructor() { super(); this._extensions = new Gio.ListStore({itemType: Extension}); this._proxyReady = false; this._shellProxy = new GnomeShellProxy(Gio.DBus.session, 'org.gnome.Shell.Extensions', '/org/gnome/Shell/Extensions', () => { this._proxyReady = true; shellVersion = this._shellProxy.ShellVersion; this._shellProxy.connect('notify::g-name-owner', () => this.notify('failed')); this.notify('failed'); }); this._shellProxy.connect('g-properties-changed', (proxy, properties) => { const enabledChanged = !!properties.lookup_value('UserExtensionsEnabled', null); if (enabledChanged) this.notify('user-extensions-enabled'); }); this._shellProxy.connectSignal( 'ExtensionStateChanged', this._onExtensionStateChanged.bind(this)); this._loadExtensions().catch(console.error); } get extensions() { return this._extensions; } get userExtensionsEnabled() { return this._shellProxy.UserExtensionsEnabled ?? false; } set userExtensionsEnabled(enabled) { this._shellProxy.UserExtensionsEnabled = enabled; } get nUpdates() { let nUpdates = 0; for (const ext of this._extensions) { if (ext.isUser && ext.hasUpdate) nUpdates++; } return nUpdates; } get failed() { return this._proxyReady && this._shellProxy.gNameOwner === null; } enableExtension(uuid) { this._shellProxy.EnableExtensionAsync(uuid).catch(console.error); } disableExtension(uuid) { this._shellProxy.DisableExtensionAsync(uuid).catch(console.error); } uninstallExtension(uuid) { this._shellProxy.UninstallExtensionAsync(uuid).catch(console.error); } openExtensionPrefs(uuid, parentHandle) { this._shellProxy.OpenExtensionPrefsAsync(uuid, parentHandle, {modal: new GLib.Variant('b', true)}).catch(console.error); } checkForUpdates() { this._shellProxy.CheckForUpdatesAsync().catch(console.error); } async _loadExtensions() { const [extensionsMap] = await this._shellProxy.ListExtensionsAsync(); for (let uuid in extensionsMap) { const extension = new Extension(extensionsMap[uuid]); this._extensions.append(extension); } this.emit('extensions-loaded'); } _findExtension(uuid) { const len = this._extensions.get_n_items(); for (let pos = 0; pos < len; pos++) { const extension = this._extensions.get_item(pos); if (extension.uuid === uuid) return [extension, pos]; } return [null, -1]; } _onExtensionStateChanged(p, sender, [uuid, newState]) { const [extension, pos] = this._findExtension(uuid); if (extension) extension.update(newState); if (!extension) this._extensions.append(new Extension(newState)); else if (extension.state === ExtensionState.UNINSTALLED) this._extensions.remove(pos); if (this._updatesCheckId) return; this._updatesCheckId = GLib.timeout_add_seconds( GLib.PRIORITY_DEFAULT, 1, () => { this.notify('n-updates'); delete this._updatesCheckId; return GLib.SOURCE_REMOVE; }); } }); (uuay)misc/ extensionUtils.js k // Common utils for the extension system, the extensions D-Bus service // and the Extensions app import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; export const ExtensionType = { SYSTEM: 1, PER_USER: 2, }; /** * @enum {number} */ export const ExtensionState = { ACTIVE: 1, INACTIVE: 2, ERROR: 3, OUT_OF_DATE: 4, DOWNLOADING: 5, INITIALIZED: 6, DEACTIVATING: 7, ACTIVATING: 8, // Used as an error state for operations on unknown extensions, // should never be in a real extensionMeta object. UNINSTALLED: 99, }; const SERIALIZED_PROPERTIES = [ 'type', 'state', 'enabled', 'path', 'error', 'hasPrefs', 'hasUpdate', 'canChange', ]; /** * Serialize extension into an object that can be used * in a vardict {GLib.Variant} * * @param {object} extension - an extension object * @returns {object} */ export function serializeExtension(extension) { let obj = {...extension.metadata}; SERIALIZED_PROPERTIES.forEach(prop => { obj[prop] = extension[prop]; }); let res = {}; for (let key in obj) { let val = obj[key]; let type; switch (typeof val) { case 'string': type = 's'; break; case 'number': type = 'd'; break; case 'boolean': type = 'b'; break; default: continue; } res[key] = GLib.Variant.new(type, val); } return res; } /** * Deserialize an unpacked variant into an extension object * * @param {object} variant - an unpacked {GLib.Variant} * @returns {object} */ export function deserializeExtension(variant) { let res = {metadata: {}}; for (let prop in variant) { let val = variant[prop].unpack(); if (SERIALIZED_PROPERTIES.includes(prop)) res[prop] = val; else res.metadata[prop] = val; } // add the 2 additional properties to create a valid extension object, as createExtensionObject() res.uuid = res.metadata.uuid; res.dir = Gio.File.new_for_path(res.path); return res; } (uuay)org/ Extensions/ / gnome/ config.js "