Compare commits

..

1 Commits

Author SHA1 Message Date
Georges Basile Stavracas Neto
602cec9b8e workspace, workspaceThumbnail: Use window content (WIP) 2019-07-16 14:31:30 -03:00
21 changed files with 672 additions and 1008 deletions

26
NEWS
View File

@@ -1,29 +1,3 @@
3.33.4
======
* Fix unintentional interference between gestures [Jonas; !598]
* Fix unintentional loop while polkit dialog is active [Ray; !602]
* Fix alt-tab icon size on HiDPI [Jonas; !587]
* Style fixes and improvements [Frederik, Jakub; !610, #1446, #1449]
* Fix style updates for non-background CSS properties [Florian; #1212]
* Fix cursor visibility in screen recordings [Illya; #1208]
* Add option for disabling the hot corner [Florian; #688320]
* Use more fine-grained levels in battery indicator [Florian; !561, #1442]
* Fix the calculation of the maximum number of app search results [Jonas; !110]
* Handle horizontal workspace layout with gestures/animations [Florian; !575]
* Improve handling of session mode extensions [Florian, Didier; #789852]
* Misc. bug fixes and cleanups [Jonas, Florian, Sonny, Carlos, Mario, Benjamin,
Marco, Ting-Wei; !599, !600, !591, !606, !152, !607, !604, !495, !608, !611,
!614, !612, !615, !618, #369, !620, #774, !621, !616, #1065, !609, !626,
!491, !631, !632, !633, #1457]
Contributors:
Benjamin Berg, Jonas Dreßler, Frederik Feichtmeier, Carlos Garnacho,
Illya Klymov, Ting-Wei Lan, Florian Müllner, Sonny Piers, Mario Sanchez Prada,
Didier Roche, Jakub Steiner, Ray Strode, Jor Teron, Marco Trevisan (Treviño)
Translators:
Jordi Mas [ca], Jor Teron [mjw]
3.33.3
======
* Prepare for optional X11 [Carlos; !378]

View File

@@ -173,30 +173,6 @@
<arg type="s" direction="in" name="uuid"/>
</method>
<!--
EnableExtension:
@uuid: The UUID of the extension
@success: Whether the operation was successful
Enable an extension.
-->
<method name="EnableExtension"> \
<arg type="s" direction="in" name="uuid"/> \
<arg type="b" direction="out" name="success"/> \
</method> \
<!--
DisableExtension:
@uuid: The UUID of the extension
@success: Whether the operation was successful
Disable an extension.
-->
<method name="DisableExtension"> \
<arg type="s" direction="in" name="uuid"/> \
<arg type="b" direction="out" name="success"/> \
</method> \
<!--
LaunchExtensionPrefs:
@uuid: The UUID of the extension
@@ -213,15 +189,6 @@
-->
<method name="CheckForUpdates"/>
<signal name="ExtensionStateChanged">
<arg type="s" name="uuid"/>
<arg type="a{sv}" name="state"/>
</signal>
<!--
ExtensionStatusChanged:
Deprecated for ExtensionStateChanged
-->
<signal name="ExtensionStatusChanged">
<arg type="s" name="uuid"/>
<arg type="i" name="state"/>

View File

@@ -21,17 +21,6 @@
EnableExtension and DisableExtension D-Bus methods on org.gnome.Shell.
</description>
</key>
<key name="disabled-extensions" type="as">
<default>[]</default>
<summary>UUIDs of extensions to force disabling</summary>
<description>
GNOME Shell extensions have a UUID property; this key lists extensions
which should be disabled, even if loaded as part of the current mode.
You can also manipulate this list with the EnableExtension and
DisableExtension D-Bus methods on org.gnome.Shell.
This key takes precedence over the “enabled-extensions” setting.
</description>
</key>
<key name="disable-user-extensions" type="b">
<default>false</default>
<summary>Disable user extensions</summary>

View File

@@ -624,12 +624,16 @@ StScrollBar {
*************/
/* Outline for low res icons */
.lowres-icon {
icon-shadow: 0 1px 2px rgba(0,0,0,0.3);
icon-shadow: 0 -1px rgba(0,0,0,0.05),
1px 0 rgba(0,0,0,0.1),
0 1px rgba(0,0,0,0.3),
-1px 0 rgba(0,0,0,0.1);
}
/* Drapshadow for large icons */
.icon-dropshadow {
icon-shadow: 0 1px 2px rgba(0,0,0,0.4);
icon-shadow: 0 2px 12px rgba(0,0,0,0.1),
0 1px 2px rgba(0,0,0,0.5);
}
/* OSD */
@@ -740,8 +744,7 @@ StScrollBar {
spacing: 8px;
}
.ws-switcher-active-up, .ws-switcher-active-down,
.ws-switcher-active-left, .ws-switcher-active-right {
.ws-switcher-active-up, .ws-switcher-active-down {
height: 50px;
background-color: $selected_bg_color;
color: $selected_fg_color;

View File

@@ -8,8 +8,6 @@ const Config = imports.misc.config;
const ExtensionUtils = imports.misc.extensionUtils;
const { loadInterfaceXML } = imports.misc.fileUtils;
const { ExtensionState } = ExtensionUtils;
const GnomeShellIface = loadInterfaceXML('org.gnome.Shell.Extensions');
const GnomeShellProxy = Gio.DBusProxy.makeProxyWrapper(GnomeShellIface);
@@ -19,54 +17,74 @@ function stripPrefix(string, prefix) {
return string;
}
var Application = GObject.registerClass({
GTypeName: 'ExtensionPrefs_Application'
}, class Application extends Gtk.Application {
_init() {
var Application = class {
constructor() {
GLib.set_prgname('gnome-shell-extension-prefs');
super._init({
this.application = new Gtk.Application({
application_id: 'org.gnome.shell.ExtensionPrefs',
flags: Gio.ApplicationFlags.HANDLES_COMMAND_LINE
});
this.application.connect('activate', this._onActivate.bind(this));
this.application.connect('command-line', this._onCommandLine.bind(this));
this.application.connect('startup', this._onStartup.bind(this));
this._extensionPrefsModules = {};
this._startupUuid = null;
this._loaded = false;
this._skipMainWindow = false;
this._shellProxy = null;
}
get shellProxy() {
return this._shellProxy;
}
_extensionAvailable(uuid) {
let extension = ExtensionUtils.extensions[uuid];
_showPrefs(uuid) {
let row = this._extensionSelector.get_children().find(c => {
return c.uuid === uuid && c.hasPrefs;
});
if (!row)
if (!extension)
return false;
if (!extension.dir.get_child('prefs.js').query_exists(null))
return false;
return true;
}
_getExtensionPrefsModule(extension) {
let uuid = extension.metadata.uuid;
if (this._extensionPrefsModules.hasOwnProperty(uuid))
return this._extensionPrefsModules[uuid];
ExtensionUtils.installImporter(extension);
let prefsModule = extension.imports.prefs;
prefsModule.init(extension.metadata);
this._extensionPrefsModules[uuid] = prefsModule;
return prefsModule;
}
_selectExtension(uuid) {
if (!this._extensionAvailable(uuid))
return;
let extension = ExtensionUtils.extensions[uuid];
let widget;
try {
widget = row.prefsModule.buildPrefsWidget();
let prefsModule = this._getExtensionPrefsModule(extension);
widget = prefsModule.buildPrefsWidget();
} catch (e) {
widget = this._buildErrorUI(row, e);
widget = this._buildErrorUI(extension, e);
}
let dialog = new Gtk.Window({
modal: !this._skipMainWindow,
type_hint: Gdk.WindowTypeHint.DIALOG
});
dialog.set_titlebar(new Gtk.HeaderBar({
show_close_button: true,
title: row.name,
visible: true
}));
let dialog = new Gtk.Window({ modal: !this._skipMainWindow,
type_hint: Gdk.WindowTypeHint.DIALOG });
dialog.set_titlebar(new Gtk.HeaderBar({ show_close_button: true,
title: extension.metadata.name,
visible: true }));
if (this._skipMainWindow) {
this.add_window(dialog);
this.application.add_window(dialog);
if (this._window)
this._window.destroy();
this._window = dialog;
@@ -80,7 +98,7 @@ var Application = GObject.registerClass({
dialog.show();
}
_buildErrorUI(row, exc) {
_buildErrorUI(extension, exc) {
let scroll = new Gtk.ScrolledWindow({
hscrollbar_policy: Gtk.PolicyType.NEVER,
propagate_natural_height: true
@@ -152,7 +170,7 @@ var Application = GObject.registerClass({
let clipboard = Gtk.Clipboard.get_default(w.get_display());
// markdown for pasting in gitlab issues
let lines = [
`The settings of extension ${row.uuid} had an error:`,
`The settings of extension ${extension.uuid} had an error:`,
'```',
`${exc}`,
'```',
@@ -174,13 +192,13 @@ var Application = GObject.registerClass({
label: _("Homepage"),
tooltip_text: _("Visit extension homepage"),
no_show_all: true,
visible: row.url != null
visible: extension.metadata.url != null
});
toolbar.add(urlButton);
urlButton.connect('clicked', w => {
let context = w.get_display().get_app_launch_context();
Gio.AppInfo.launch_default_for_uri(row.url, context);
Gio.AppInfo.launch_default_for_uri(extension.metadata.url, context);
});
let expandedBox = new Gtk.Box({
@@ -195,8 +213,8 @@ var Application = GObject.registerClass({
return scroll;
}
_buildUI() {
this._window = new Gtk.ApplicationWindow({ application: this,
_buildUI(app) {
this._window = new Gtk.ApplicationWindow({ application: app,
window_position: Gtk.WindowPosition.CENTER });
this._window.set_default_size(800, 500);
@@ -230,14 +248,18 @@ var Application = GObject.registerClass({
this._mainStack.add_named(new EmptyPlaceholder(), 'placeholder');
this._shellProxy = new GnomeShellProxy(Gio.DBus.session, 'org.gnome.Shell', '/org/gnome/Shell');
this._shellProxy.connectSignal('ExtensionStateChanged',
this._onExtensionStateChanged.bind(this));
this._shellProxy.connectSignal('ExtensionStatusChanged', (proxy, senderName, [uuid, state, error]) => {
if (ExtensionUtils.extensions[uuid] !== undefined)
this._scanExtensions();
});
this._window.show_all();
}
_sortList(row1, row2) {
return row1.name.localeCompare(row2.name);
let name1 = ExtensionUtils.extensions[row1.uuid].metadata.name;
let name2 = ExtensionUtils.extensions[row2.uuid].metadata.name;
return name1.localeCompare(name2);
}
_updateHeader(row, before) {
@@ -248,55 +270,19 @@ var Application = GObject.registerClass({
row.set_header(sep);
}
_findExtensionRow(uuid) {
return this._extensionSelector.get_children().find(c => c.uuid === uuid);
}
_onExtensionStateChanged(proxy, senderName, [uuid, newState]) {
let row = this._findExtensionRow(uuid);
if (row) {
let { state } = ExtensionUtils.deserializeExtension(newState);
if (state == ExtensionState.UNINSTALLED)
row.destroy();
return; // we only deal with new and deleted extensions here
}
this._shellProxy.GetExtensionInfoRemote(uuid, ([serialized]) => {
let extension = ExtensionUtils.deserializeExtension(serialized);
if (!extension)
return;
// check the extension wasn't added in between
if (this._findExtensionRow(uuid) != null)
return;
this._addExtensionRow(extension);
});
}
_scanExtensions() {
this._shellProxy.ListExtensionsRemote(([extensionsMap], e) => {
if (e) {
if (e instanceof Gio.DBusError) {
log(`Failed to connect to shell proxy: ${e}`);
this._mainStack.add_named(new NoShellPlaceholder(), 'noshell');
this._mainStack.visible_child_name = 'noshell';
} else
throw e;
return;
}
for (let uuid in extensionsMap) {
let extension = ExtensionUtils.deserializeExtension(extensionsMap[uuid]);
this._addExtensionRow(extension);
}
this._extensionsLoaded();
});
let finder = new ExtensionUtils.ExtensionFinder();
finder.connect('extension-found', this._extensionFound.bind(this));
finder.scanExtensions();
this._extensionsLoaded();
}
_addExtensionRow(extension) {
let row = new ExtensionRow(extension);
_extensionFound(finder, extension) {
let row = new ExtensionRow(extension.uuid);
row.prefsButton.visible = this._extensionAvailable(row.uuid);
row.prefsButton.connect('clicked', () => {
this._showPrefs(row.uuid);
this._selectExtension(row.uuid);
});
row.show_all();
@@ -309,26 +295,24 @@ var Application = GObject.registerClass({
else
this._mainStack.visible_child_name = 'placeholder';
if (this._startupUuid)
this._showPrefs(this._startupUuid);
if (this._startupUuid && this._extensionAvailable(this._startupUuid))
this._selectExtension(this._startupUuid);
this._startupUuid = null;
this._skipMainWindow = false;
this._loaded = true;
}
vfunc_activate() {
_onActivate() {
this._window.present();
}
vfunc_startup() {
super.vfunc_startup();
this._buildUI();
_onStartup(app) {
this._buildUI(app);
this._scanExtensions();
}
vfunc_command_line(commandLine) {
this.activate();
_onCommandLine(app, commandLine) {
app.activate();
let args = commandLine.get_arguments();
if (args.length) {
@@ -339,14 +323,16 @@ var Application = GObject.registerClass({
// Strip off "extension:///" prefix which fakes a URI, if it exists
uuid = stripPrefix(uuid, "extension:///");
if (!this._loaded)
if (this._extensionAvailable(uuid))
this._selectExtension(uuid);
else if (!this._loaded)
this._startupUuid = uuid;
else if (!this._showPrefs(uuid))
else
this._skipMainWindow = false;
}
return 0;
}
});
};
var Expander = GObject.registerClass({
Properties: {
@@ -513,35 +499,6 @@ class EmptyPlaceholder extends Gtk.Box {
}
});
var NoShellPlaceholder = GObject.registerClass(
class NoShellPlaceholder extends Gtk.Box {
_init() {
super._init({
orientation: Gtk.Orientation.VERTICAL,
spacing: 12,
margin: 100,
margin_bottom: 60
});
let label = new Gtk.Label({
label: '<span size="x-large">%s</span>'.format(
_("Somethings gone wrong")),
use_markup: true
});
label.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL);
this.add(label);
label = new Gtk.Label({
label: _("Were very sorry, but it was not possible to get the list of installed extensions. Make sure you are logged into GNOME and try again."),
justify: Gtk.Justification.CENTER,
wrap: true
});
this.add(label);
this.show_all();
}
});
var DescriptionLabel = GObject.registerClass(
class DescriptionLabel extends Gtk.Label {
vfunc_get_preferred_height_for_width(width) {
@@ -554,55 +511,30 @@ class DescriptionLabel extends Gtk.Label {
var ExtensionRow = GObject.registerClass(
class ExtensionRow extends Gtk.ListBoxRow {
_init(extension) {
_init(uuid) {
super._init();
this._app = Gio.Application.get_default();
this._extension = extension;
this._prefsModule = null;
this.uuid = uuid;
this._extensionStateChangedId = this._app.shellProxy.connectSignal(
'ExtensionStateChanged', (p, sender, [uuid, newState]) => {
if (this.uuid !== uuid)
return;
this._extension = ExtensionUtils.deserializeExtension(newState);
let state = (this._extension.state == ExtensionState.ENABLED);
this._switch.state = state;
this._switch.sensitive = this._canToggle();
this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' });
this._settings.connect('changed::enabled-extensions', () => {
this._switch.state = this._isEnabled();
});
this._settings.connect('changed::disable-extension-version-validation',
() => {
this._switch.sensitive = this._canEnable();
});
this._settings.connect('changed::disable-user-extensions',
() => {
this._switch.sensitive = this._canEnable();
});
this.connect('destroy', this._onDestroy.bind(this));
this._buildUI();
}
get uuid() {
return this._extension.uuid;
}
get name() {
return this._extension.metadata.name;
}
get hasPrefs() {
return this._extension.hasPrefs;
}
get url() {
return this._extension.metadata.url;
}
_onDestroy() {
if (!this._app.shellProxy)
return;
if (this._extensionStateChangedId)
this._app.shellProxy.disconnectSignal(this._extensionStateChangedId);
this._extensionStateChangedId = 0;
}
_buildUI() {
let extension = ExtensionUtils.extensions[this.uuid];
let hbox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL,
hexpand: true, margin_end: 24, spacing: 24,
margin: 12 });
@@ -612,20 +544,19 @@ class ExtensionRow extends Gtk.ListBoxRow {
spacing: 6, hexpand: true });
hbox.add(vbox);
let name = GLib.markup_escape_text(this.name, -1);
let name = GLib.markup_escape_text(extension.metadata.name, -1);
let label = new Gtk.Label({ label: '<b>' + name + '</b>',
use_markup: true,
halign: Gtk.Align.START });
vbox.add(label);
let desc = this._extension.metadata.description.split('\n')[0];
let desc = extension.metadata.description.split('\n')[0];
label = new DescriptionLabel({ label: desc, wrap: true, lines: 2,
ellipsize: Pango.EllipsizeMode.END,
xalign: 0, yalign: 0 });
vbox.add(label);
let button = new Gtk.Button({ valign: Gtk.Align.CENTER,
visible: this.hasPrefs,
no_show_all: true });
button.set_image(new Gtk.Image({ icon_name: 'emblem-system-symbolic',
icon_size: Gtk.IconSize.BUTTON,
@@ -635,37 +566,51 @@ class ExtensionRow extends Gtk.ListBoxRow {
this.prefsButton = button;
this._switch = new Gtk.Switch({
valign: Gtk.Align.CENTER,
sensitive: this._canToggle(),
state: this._extension.state === ExtensionState.ENABLED
});
this._switch = new Gtk.Switch({ valign: Gtk.Align.CENTER,
sensitive: this._canEnable(),
state: this._isEnabled() });
this._switch.connect('notify::active', () => {
if (this._switch.active)
this._app.shellProxy.EnableExtensionRemote(this.uuid);
this._enable();
else
this._app.shellProxy.DisableExtensionRemote(this.uuid);
this._disable();
});
this._switch.connect('state-set', () => true);
hbox.add(this._switch);
}
_canToggle() {
return this._extension.canChange;
_canEnable() {
let extension = ExtensionUtils.extensions[this.uuid];
let checkVersion = !this._settings.get_boolean('disable-extension-version-validation');
return !this._settings.get_boolean('disable-user-extensions') &&
!(checkVersion && ExtensionUtils.isOutOfDate(extension));
}
get prefsModule() {
if (!this._prefsModule) {
ExtensionUtils.installImporter(this._extension);
_isEnabled() {
let extensions = this._settings.get_strv('enabled-extensions');
return extensions.includes(this.uuid);
}
// give extension prefs access to their own extension object
ExtensionUtils.getCurrentExtension = () => this._extension;
_enable() {
let extensions = this._settings.get_strv('enabled-extensions');
if (extensions.includes(this.uuid))
return;
this._prefsModule = this._extension.imports.prefs;
this._prefsModule.init(this._extension.metadata);
}
extensions.push(this.uuid);
this._settings.set_strv('enabled-extensions', extensions);
}
return this._prefsModule;
_disable() {
let extensions = this._settings.get_strv('enabled-extensions');
let pos = extensions.indexOf(this.uuid);
if (pos == -1)
return;
do {
extensions.splice(pos, 1);
pos = extensions.indexOf(this.uuid);
} while (pos != -1);
this._settings.set_strv('enabled-extensions', extensions);
}
});
@@ -693,5 +638,6 @@ function main(argv) {
Gettext.bindtextdomain(Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
Gettext.textdomain(Config.GETTEXT_PACKAGE);
new Application().run(argv);
let app = new Application();
app.application.run(argv);
}

View File

@@ -3,32 +3,21 @@
// Common utils for the extension system and the extension
// preferences tool
const { Gio, GLib } = imports.gi;
const Gettext = imports.gettext;
const Lang = imports.lang;
const Signals = imports.signals;
const Gio = imports.gi.Gio;
const Config = imports.misc.config;
const FileUtils = imports.misc.fileUtils;
var ExtensionType = {
SYSTEM: 1,
PER_USER: 2
};
var ExtensionState = {
ENABLED: 1,
DISABLED: 2,
ERROR: 3,
OUT_OF_DATE: 4,
DOWNLOADING: 5,
INITIALIZED: 6,
// 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', 'path', 'error', 'hasPrefs', 'canChange'];
// Maps uuid -> metadata object
var extensions = {};
/**
* getCurrentExtension:
@@ -60,17 +49,13 @@ function getCurrentExtension() {
if (!match)
return null;
// local import, as the module is used from outside the gnome-shell process
// as well (not this function though)
let extensionManager = imports.ui.main.extensionManager;
let path = match[1];
let file = Gio.File.new_for_path(path);
// Walk up the directory tree, looking for an extension with
// the same UUID as a directory name.
while (file != null) {
let extension = extensionManager.lookup(file.get_basename());
let extension = extensions[file.get_basename()];
if (extension !== undefined)
return extension;
file = file.get_parent();
@@ -176,50 +161,52 @@ function isOutOfDate(extension) {
return false;
}
function serializeExtension(extension) {
let obj = {};
Lang.copyProperties(extension.metadata, obj);
function createExtensionObject(uuid, dir, type) {
let metadataFile = dir.get_child('metadata.json');
if (!metadataFile.query_exists(null)) {
throw new Error('Missing metadata.json');
}
SERIALIZED_PROPERTIES.forEach(prop => {
obj[prop] = extension[prop];
});
let metadataContents, success, tag;
try {
[success, metadataContents, tag] = metadataFile.load_contents(null);
if (metadataContents instanceof Uint8Array)
metadataContents = imports.byteArray.toString(metadataContents);
} catch (e) {
throw new Error(`Failed to load metadata.json: ${e}`);
}
let meta;
try {
meta = JSON.parse(metadataContents);
} catch (e) {
throw new Error(`Failed to parse metadata.json: ${e}`);
}
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;
let requiredProperties = ['uuid', 'name', 'description', 'shell-version'];
for (let i = 0; i < requiredProperties.length; i++) {
let prop = requiredProperties[i];
if (!meta[prop]) {
throw new Error(`missing "${prop}" property in metadata.json`);
}
res[key] = GLib.Variant.new(type, val);
}
return res;
}
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;
if (uuid != meta.uuid) {
throw new Error(`uuid "${meta.uuid}" from metadata.json does not match directory name "${uuid}"`);
}
// 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;
let extension = {};
extension.metadata = meta;
extension.uuid = meta.uuid;
extension.type = type;
extension.dir = dir;
extension.path = dir.get_path();
extension.error = '';
extension.hasPrefs = dir.get_child('prefs.js').query_exists(null);
extensions[uuid] = extension;
return extension;
}
function installImporter(extension) {
@@ -230,3 +217,36 @@ function installImporter(extension) {
extension.imports = imports[extension.uuid];
imports.searchPath = oldSearchPath;
}
var ExtensionFinder = class {
_loadExtension(extensionDir, info, perUserDir) {
let fileType = info.get_file_type();
if (fileType != Gio.FileType.DIRECTORY)
return;
let uuid = info.get_name();
let existing = extensions[uuid];
if (existing) {
log('Extension %s already installed in %s. %s will not be loaded'.format(uuid, existing.path, extensionDir.get_path()));
return;
}
let extension;
let type = extensionDir.has_prefix(perUserDir) ? ExtensionType.PER_USER
: ExtensionType.SYSTEM;
try {
extension = createExtensionObject(uuid, extensionDir, type);
} catch (e) {
logError(e, 'Could not load extension %s'.format(uuid));
return;
}
this.emit('extension-found', extension);
}
scanExtensions() {
let perUserDir = Gio.File.new_for_path(global.userdatadir);
FileUtils.collectFromDatadirs('extensions', true, (dir, info) => {
this._loadExtension(dir, info, perUserDir);
});
}
};
Signals.addSignalMethods(ExtensionFinder.prototype);

View File

@@ -633,7 +633,7 @@ var Animation = class Animation {
}
load(callback) {
this._show = new GnomeDesktop.BGSlideShow({ file: this.file });
this._show = new GnomeDesktop.BGSlideShow({ filename: this.file.get_path() });
this._show.load_async(null, (object, result) => {
this.loaded = true;

View File

@@ -5,8 +5,8 @@ const { Clutter, Gio, GLib, GObject, Soup } = imports.gi;
const Config = imports.misc.config;
const Dialog = imports.ui.dialog;
const ExtensionUtils = imports.misc.extensionUtils;
const ExtensionSystem = imports.ui.extensionSystem;
const FileUtils = imports.misc.fileUtils;
const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
var REPOSITORY_URL_BASE = 'https://extensions.gnome.org';
@@ -24,7 +24,7 @@ function installExtension(uuid, invocation) {
_httpSession.queue_message(message, (session, message) => {
if (message.status_code != Soup.KnownStatusCode.OK) {
Main.extensionManager.logExtensionError(uuid, `downloading info: ${message.status_code}`);
ExtensionSystem.logExtensionError(uuid, `downloading info: ${message.status_code}`);
invocation.return_dbus_error('org.gnome.Shell.DownloadInfoError', message.status_code.toString());
return;
}
@@ -33,7 +33,7 @@ function installExtension(uuid, invocation) {
try {
info = JSON.parse(message.response_body.data);
} catch (e) {
Main.extensionManager.logExtensionError(uuid, `parsing info: ${e}`);
ExtensionSystem.logExtensionError(uuid, `parsing info: ${e}`);
invocation.return_dbus_error('org.gnome.Shell.ParseInfoError', e.toString());
return;
}
@@ -44,7 +44,7 @@ function installExtension(uuid, invocation) {
}
function uninstallExtension(uuid) {
let extension = Main.extensionManager.lookup(uuid);
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
return false;
@@ -52,7 +52,7 @@ function uninstallExtension(uuid) {
if (extension.type != ExtensionUtils.ExtensionType.PER_USER)
return false;
if (!Main.extensionManager.unloadExtension(extension))
if (!ExtensionSystem.unloadExtension(extension))
return false;
FileUtils.recursivelyDeleteDir(extension.dir, true);
@@ -113,10 +113,10 @@ function updateExtension(uuid) {
_httpSession.queue_message(message, (session, message) => {
gotExtensionZipFile(session, message, uuid, newExtensionTmpDir, () => {
let oldExtension = Main.extensionManager.lookup(uuid);
let oldExtension = ExtensionUtils.extensions[uuid];
let extensionDir = oldExtension.dir;
if (!Main.extensionManager.unloadExtension(oldExtension))
if (!ExtensionSystem.unloadExtension(oldExtension))
return;
FileUtils.recursivelyMoveDir(extensionDir, oldExtensionTmpDir);
@@ -125,11 +125,11 @@ function updateExtension(uuid) {
let extension = null;
try {
extension = Main.extensionManager.createExtensionObject(uuid, extensionDir, ExtensionUtils.ExtensionType.PER_USER);
Main.extensionManager.loadExtension(extension);
extension = ExtensionUtils.createExtensionObject(uuid, extensionDir, ExtensionUtils.ExtensionType.PER_USER);
ExtensionSystem.loadExtension(extension);
} catch (e) {
if (extension)
Main.extensionManager.unloadExtension(extension);
ExtensionSystem.unloadExtension(extension);
logError(e, 'Error loading extension %s'.format(uuid));
@@ -138,7 +138,7 @@ function updateExtension(uuid) {
// Restore what was there before. We can't do much if we
// fail here.
Main.extensionManager.loadExtension(oldExtension);
ExtensionSystem.loadExtension(oldExtension);
return;
}
@@ -151,9 +151,9 @@ function updateExtension(uuid) {
function checkForUpdates() {
let metadatas = {};
Main.extensionManager.getUuids().forEach(uuid => {
metadatas[uuid] = Main.extensionManager.extensions[uuid].metadata;
});
for (let uuid in ExtensionUtils.extensions) {
metadatas[uuid] = ExtensionUtils.extensions[uuid].metadata;
}
let params = { shell_version: Config.PACKAGE_VERSION,
installed: JSON.stringify(metadatas) };
@@ -224,11 +224,16 @@ class InstallExtensionDialog extends ModalDialog.ModalDialog {
}
function callback() {
// Add extension to 'enabled-extensions' for the user, always...
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
if (!enabledExtensions.includes(uuid)) {
enabledExtensions.push(uuid);
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
}
try {
let extension = Main.extensionManager.createExtensionObject(uuid, dir, ExtensionUtils.ExtensionType.PER_USER);
Main.extensionManager.loadExtension(extension);
if (!Main.extensionManager.enableExtension(uuid))
throw new Error(`Cannot add ${uuid} to enabled extensions gsettings key`);
let extension = ExtensionUtils.createExtensionObject(uuid, dir, ExtensionUtils.ExtensionType.PER_USER);
ExtensionSystem.loadExtension(extension);
} catch (e) {
uninstallExtension(uuid);
errback('LoadExtensionError', e);

View File

@@ -4,519 +4,371 @@ const { Gio, St } = imports.gi;
const Signals = imports.signals;
const ExtensionUtils = imports.misc.extensionUtils;
const FileUtils = imports.misc.fileUtils;
const Main = imports.ui.main;
const { ExtensionState, ExtensionType } = ExtensionUtils;
var ExtensionState = {
ENABLED: 1,
DISABLED: 2,
ERROR: 3,
OUT_OF_DATE: 4,
DOWNLOADING: 5,
INITIALIZED: 6,
// Used as an error state for operations on unknown extensions,
// should never be in a real extensionMeta object.
UNINSTALLED: 99
};
// Arrays of uuids
var enabledExtensions;
// Contains the order that extensions were enabled in.
var extensionOrder = [];
// We don't really have a class to add signals on. So, create
// a simple dummy object, add the signal methods, and export those
// publically.
var _signals = {};
Signals.addSignalMethods(_signals);
var connect = _signals.connect.bind(_signals);
var disconnect = _signals.disconnect.bind(_signals);
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
const DISABLED_EXTENSIONS_KEY = 'disabled-extensions';
const DISABLE_USER_EXTENSIONS_KEY = 'disable-user-extensions';
const EXTENSION_DISABLE_VERSION_CHECK_KEY = 'disable-extension-version-validation';
var ExtensionManager = class {
constructor() {
this._initted = false;
this._enabled = false;
var initted = false;
var enabled;
this._extensions = new Map();
this._enabledExtensions = [];
this._extensionOrder = [];
function disableExtension(uuid) {
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
return;
Main.sessionMode.connect('updated', this._sessionUpdated.bind(this));
}
if (extension.state != ExtensionState.ENABLED)
return;
init() {
this._sessionUpdated();
}
// "Rebase" the extension order by disabling and then enabling extensions
// in order to help prevent conflicts.
lookup(uuid) {
return this._extensions.get(uuid);
}
// Example:
// order = [A, B, C, D, E]
// user disables C
// this should: disable E, disable D, disable C, enable D, enable E
getUuids() {
return [...this._extensions.keys()];
}
let orderIdx = extensionOrder.indexOf(uuid);
let order = extensionOrder.slice(orderIdx + 1);
let orderReversed = order.slice().reverse();
_callExtensionDisable(uuid) {
let extension = this.lookup(uuid);
if (!extension)
return;
if (extension.state != ExtensionState.ENABLED)
return;
// "Rebase" the extension order by disabling and then enabling extensions
// in order to help prevent conflicts.
// Example:
// order = [A, B, C, D, E]
// user disables C
// this should: disable E, disable D, disable C, enable D, enable E
let orderIdx = this._extensionOrder.indexOf(uuid);
let order = this._extensionOrder.slice(orderIdx + 1);
let orderReversed = order.slice().reverse();
for (let i = 0; i < orderReversed.length; i++) {
let uuid = orderReversed[i];
try {
this.lookup(uuid).stateObj.disable();
} catch (e) {
this.logExtensionError(uuid, e);
}
for (let i = 0; i < orderReversed.length; i++) {
let uuid = orderReversed[i];
try {
ExtensionUtils.extensions[uuid].stateObj.disable();
} catch (e) {
logExtensionError(uuid, e);
}
}
if (extension.stylesheet) {
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
theme.unload_stylesheet(extension.stylesheet);
delete extension.stylesheet;
}
try {
extension.stateObj.disable();
} catch (e) {
logExtensionError(uuid, e);
}
for (let i = 0; i < order.length; i++) {
let uuid = order[i];
try {
ExtensionUtils.extensions[uuid].stateObj.enable();
} catch (e) {
logExtensionError(uuid, e);
}
}
extensionOrder.splice(orderIdx, 1);
if ( extension.state != ExtensionState.ERROR ) {
extension.state = ExtensionState.DISABLED;
_signals.emit('extension-state-changed', extension);
}
}
function enableExtension(uuid) {
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
return;
if (extension.state == ExtensionState.INITIALIZED)
initExtension(uuid);
if (extension.state != ExtensionState.DISABLED)
return;
extensionOrder.push(uuid);
let stylesheetNames = [`${global.session_mode}.css`, 'stylesheet.css'];
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
for (let i = 0; i < stylesheetNames.length; i++) {
try {
let stylesheetFile = extension.dir.get_child(stylesheetNames[i]);
theme.load_stylesheet(stylesheetFile);
extension.stylesheet = stylesheetFile;
break;
} catch (e) {
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
continue; // not an error
log(`Failed to load stylesheet for extension ${uuid}: ${e.message}`);
return;
}
}
try {
extension.stateObj.enable();
extension.state = ExtensionState.ENABLED;
_signals.emit('extension-state-changed', extension);
return;
} catch (e) {
if (extension.stylesheet) {
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
theme.unload_stylesheet(extension.stylesheet);
delete extension.stylesheet;
}
try {
extension.stateObj.disable();
} catch (e) {
this.logExtensionError(uuid, e);
}
for (let i = 0; i < order.length; i++) {
let uuid = order[i];
try {
this.lookup(uuid).stateObj.enable();
} catch (e) {
this.logExtensionError(uuid, e);
}
}
this._extensionOrder.splice(orderIdx, 1);
if (extension.state != ExtensionState.ERROR) {
extension.state = ExtensionState.DISABLED;
this.emit('extension-state-changed', extension);
}
logExtensionError(uuid, e);
return;
}
}
_callExtensionEnable(uuid) {
let extension = this.lookup(uuid);
if (!extension)
return;
function logExtensionError(uuid, error) {
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
return;
if (extension.state == ExtensionState.INITIALIZED)
this._callExtensionInit(uuid);
let message = `${error}`;
if (extension.state != ExtensionState.DISABLED)
return;
extension.state = ExtensionState.ERROR;
if (!extension.errors)
extension.errors = [];
extension.errors.push(message);
this._extensionOrder.push(uuid);
log('Extension "%s" had error: %s'.format(uuid, message));
_signals.emit('extension-state-changed', { uuid: uuid,
error: message,
state: extension.state });
}
let stylesheetNames = [`${global.session_mode}.css`, 'stylesheet.css'];
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
for (let i = 0; i < stylesheetNames.length; i++) {
try {
let stylesheetFile = extension.dir.get_child(stylesheetNames[i]);
theme.load_stylesheet(stylesheetFile);
extension.stylesheet = stylesheetFile;
break;
} catch (e) {
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
continue; // not an error
log(`Failed to load stylesheet for extension ${uuid}: ${e.message}`);
function loadExtension(extension) {
// Default to error, we set success as the last step
extension.state = ExtensionState.ERROR;
let checkVersion = !global.settings.get_boolean(EXTENSION_DISABLE_VERSION_CHECK_KEY);
if (checkVersion && ExtensionUtils.isOutOfDate(extension)) {
extension.state = ExtensionState.OUT_OF_DATE;
} else {
let enabled = enabledExtensions.includes(extension.uuid);
if (enabled) {
if (!initExtension(extension.uuid))
return;
}
}
try {
extension.stateObj.enable();
extension.state = ExtensionState.ENABLED;
this.emit('extension-state-changed', extension);
return;
} catch (e) {
if (extension.stylesheet) {
theme.unload_stylesheet(extension.stylesheet);
delete extension.stylesheet;
}
this.logExtensionError(uuid, e);
return;
}
}
enableExtension(uuid) {
if (!this._extensions.has(uuid))
return false;
let enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
let disabledExtensions = global.settings.get_strv(DISABLED_EXTENSIONS_KEY);
if (disabledExtensions.includes(uuid)) {
disabledExtensions = disabledExtensions.filter(item => item !== uuid);
global.settings.set_strv(DISABLED_EXTENSIONS_KEY, disabledExtensions);
}
if (!enabledExtensions.includes(uuid)) {
enabledExtensions.push(uuid);
global.settings.set_strv(ENABLED_EXTENSIONS_KEY, enabledExtensions);
}
return true;
}
disableExtension(uuid) {
if (!this._extensions.has(uuid))
return false;
let enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
let disabledExtensions = global.settings.get_strv(DISABLED_EXTENSIONS_KEY);
if (enabledExtensions.includes(uuid)) {
enabledExtensions = enabledExtensions.filter(item => item !== uuid);
global.settings.set_strv(ENABLED_EXTENSIONS_KEY, enabledExtensions);
}
if (!disabledExtensions.includes(uuid)) {
disabledExtensions.push(uuid);
global.settings.set_strv(DISABLED_EXTENSIONS_KEY, disabledExtensions);
}
return true;
}
logExtensionError(uuid, error) {
let extension = this.lookup(uuid);
if (!extension)
return;
let message = `${error}`;
extension.error = message;
extension.state = ExtensionState.ERROR;
if (!extension.errors)
extension.errors = [];
extension.errors.push(message);
log('Extension "%s" had error: %s'.format(uuid, message));
this.emit('extension-state-changed', extension);
}
createExtensionObject(uuid, dir, type) {
let metadataFile = dir.get_child('metadata.json');
if (!metadataFile.query_exists(null)) {
throw new Error('Missing metadata.json');
}
let metadataContents, success;
try {
[success, metadataContents] = metadataFile.load_contents(null);
if (metadataContents instanceof Uint8Array)
metadataContents = imports.byteArray.toString(metadataContents);
} catch (e) {
throw new Error(`Failed to load metadata.json: ${e}`);
}
let meta;
try {
meta = JSON.parse(metadataContents);
} catch (e) {
throw new Error(`Failed to parse metadata.json: ${e}`);
}
let requiredProperties = ['uuid', 'name', 'description', 'shell-version'];
for (let i = 0; i < requiredProperties.length; i++) {
let prop = requiredProperties[i];
if (!meta[prop]) {
throw new Error(`missing "${prop}" property in metadata.json`);
}
}
if (uuid != meta.uuid) {
throw new Error(`uuid "${meta.uuid}" from metadata.json does not match directory name "${uuid}"`);
}
let extension = {
metadata: meta,
uuid: meta.uuid,
type,
dir,
path: dir.get_path(),
error: '',
hasPrefs: dir.get_child('prefs.js').query_exists(null),
canChange: false
};
this._extensions.set(uuid, extension);
return extension;
}
loadExtension(extension) {
// Default to error, we set success as the last step
extension.state = ExtensionState.ERROR;
let checkVersion = !global.settings.get_boolean(EXTENSION_DISABLE_VERSION_CHECK_KEY);
if (checkVersion && ExtensionUtils.isOutOfDate(extension)) {
extension.state = ExtensionState.OUT_OF_DATE;
if (extension.state == ExtensionState.DISABLED)
enableExtension(extension.uuid);
} else {
let enabled = this._enabledExtensions.includes(extension.uuid);
if (enabled) {
if (!this._callExtensionInit(extension.uuid))
return;
if (extension.state == ExtensionState.DISABLED)
this._callExtensionEnable(extension.uuid);
} else {
extension.state = ExtensionState.INITIALIZED;
}
extension.state = ExtensionState.INITIALIZED;
}
this._updateCanChange(extension);
this.emit('extension-state-changed', extension);
}
unloadExtension(extension) {
// Try to disable it -- if it's ERROR'd, we can't guarantee that,
// but it will be removed on next reboot, and hopefully nothing
// broke too much.
this._callExtensionDisable(extension.uuid);
_signals.emit('extension-state-changed', extension);
}
extension.state = ExtensionState.UNINSTALLED;
this.emit('extension-state-changed', extension);
function unloadExtension(extension) {
// Try to disable it -- if it's ERROR'd, we can't guarantee that,
// but it will be removed on next reboot, and hopefully nothing
// broke too much.
disableExtension(extension.uuid);
this._extensions.delete(extension.uuid);
return true;
extension.state = ExtensionState.UNINSTALLED;
_signals.emit('extension-state-changed', extension);
delete ExtensionUtils.extensions[extension.uuid];
return true;
}
function reloadExtension(oldExtension) {
// Grab the things we'll need to pass to createExtensionObject
// to reload it.
let { uuid: uuid, dir: dir, type: type } = oldExtension;
// Then unload the old extension.
unloadExtension(oldExtension);
// Now, recreate the extension and load it.
let newExtension;
try {
newExtension = ExtensionUtils.createExtensionObject(uuid, dir, type);
} catch (e) {
logExtensionError(uuid, e);
return;
}
reloadExtension(oldExtension) {
// Grab the things we'll need to pass to createExtensionObject
// to reload it.
let { uuid: uuid, dir: dir, type: type } = oldExtension;
loadExtension(newExtension);
}
// Then unload the old extension.
this.unloadExtension(oldExtension);
function initExtension(uuid) {
let extension = ExtensionUtils.extensions[uuid];
let dir = extension.dir;
// Now, recreate the extension and load it.
let newExtension;
if (!extension)
throw new Error("Extension was not properly created. Call loadExtension first");
let extensionJs = dir.get_child('extension.js');
if (!extensionJs.query_exists(null)) {
logExtensionError(uuid, new Error('Missing extension.js'));
return false;
}
let extensionModule;
let extensionState = null;
ExtensionUtils.installImporter(extension);
try {
extensionModule = extension.imports.extension;
} catch (e) {
logExtensionError(uuid, e);
return false;
}
if (extensionModule.init) {
try {
newExtension = this.createExtensionObject(uuid, dir, type);
extensionState = extensionModule.init(extension);
} catch (e) {
this.logExtensionError(uuid, e);
return;
}
this.loadExtension(newExtension);
}
_callExtensionInit(uuid) {
let extension = this.lookup(uuid);
let dir = extension.dir;
if (!extension)
throw new Error("Extension was not properly created. Call loadExtension first");
let extensionJs = dir.get_child('extension.js');
if (!extensionJs.query_exists(null)) {
this.logExtensionError(uuid, new Error('Missing extension.js'));
logExtensionError(uuid, e);
return false;
}
let extensionModule;
let extensionState = null;
ExtensionUtils.installImporter(extension);
try {
extensionModule = extension.imports.extension;
} catch (e) {
this.logExtensionError(uuid, e);
return false;
}
if (extensionModule.init) {
try {
extensionState = extensionModule.init(extension);
} catch (e) {
this.logExtensionError(uuid, e);
return false;
}
}
if (!extensionState)
extensionState = extensionModule;
extension.stateObj = extensionState;
extension.state = ExtensionState.DISABLED;
this.emit('extension-loaded', uuid);
return true;
}
_getModeExtensions() {
if (Array.isArray(Main.sessionMode.enabledExtensions))
return Main.sessionMode.enabledExtensions;
return [];
}
if (!extensionState)
extensionState = extensionModule;
extension.stateObj = extensionState;
_updateCanChange(extension) {
let hasError =
extension.state == ExtensionState.ERROR ||
extension.state == ExtensionState.OUT_OF_DATE;
extension.state = ExtensionState.DISABLED;
_signals.emit('extension-loaded', uuid);
return true;
}
let isMode = this._getModeExtensions().includes(extension.uuid);
let modeOnly = global.settings.get_boolean(DISABLE_USER_EXTENSIONS_KEY);
function getEnabledExtensions() {
let extensions;
if (Array.isArray(Main.sessionMode.enabledExtensions))
extensions = Main.sessionMode.enabledExtensions;
else
extensions = [];
let changeKey = isMode
? DISABLE_USER_EXTENSIONS_KEY
: ENABLED_EXTENSIONS_KEY;
if (global.settings.get_boolean(DISABLE_USER_EXTENSIONS_KEY))
return extensions;
extension.canChange =
!hasError &&
global.settings.is_writable(changeKey) &&
(isMode || !modeOnly);
}
return extensions.concat(global.settings.get_strv(ENABLED_EXTENSIONS_KEY));
}
_getEnabledExtensions() {
let extensions = this._getModeExtensions();
function onEnabledExtensionsChanged() {
let newEnabledExtensions = getEnabledExtensions();
if (!global.settings.get_boolean(DISABLE_USER_EXTENSIONS_KEY))
extensions = extensions.concat(global.settings.get_strv(ENABLED_EXTENSIONS_KEY));
if (!enabled)
return;
// filter out 'disabled-extensions' which takes precedence
let disabledExtensions = global.settings.get_strv(DISABLED_EXTENSIONS_KEY);
return extensions.filter(item => !disabledExtensions.includes(item));
}
// Find and enable all the newly enabled extensions: UUIDs found in the
// new setting, but not in the old one.
newEnabledExtensions.filter(
uuid => !enabledExtensions.includes(uuid)
).forEach(uuid => {
enableExtension(uuid);
});
_onUserExtensionsEnabledChanged() {
this._onEnabledExtensionsChanged();
this._onSettingsWritableChanged();
}
// Find and disable all the newly disabled extensions: UUIDs found in the
// old setting, but not in the new one.
enabledExtensions.filter(
item => !newEnabledExtensions.includes(item)
).forEach(uuid => {
disableExtension(uuid);
});
_onEnabledExtensionsChanged() {
let newEnabledExtensions = this._getEnabledExtensions();
enabledExtensions = newEnabledExtensions;
}
if (!this._enabled)
return;
function _onVersionValidationChanged() {
// we want to reload all extensions, but only enable
// extensions when allowed by the sessionMode, so
// temporarily disable them all
enabledExtensions = [];
for (let uuid in ExtensionUtils.extensions)
reloadExtension(ExtensionUtils.extensions[uuid]);
enabledExtensions = getEnabledExtensions();
// Find and enable all the newly enabled extensions: UUIDs found in the
// new setting, but not in the old one.
newEnabledExtensions.filter(
uuid => !this._enabledExtensions.includes(uuid)
).forEach(uuid => {
this._callExtensionEnable(uuid);
if (Main.sessionMode.allowExtensions) {
enabledExtensions.forEach(uuid => {
enableExtension(uuid);
});
}
}
// Find and disable all the newly disabled extensions: UUIDs found in the
// old setting, but not in the new one.
this._enabledExtensions.filter(
item => !newEnabledExtensions.includes(item)
).forEach(uuid => {
this._callExtensionDisable(uuid);
function _loadExtensions() {
global.settings.connect(`changed::${ENABLED_EXTENSIONS_KEY}`, onEnabledExtensionsChanged);
global.settings.connect(`changed::${DISABLE_USER_EXTENSIONS_KEY}`, onEnabledExtensionsChanged);
global.settings.connect(`changed::${EXTENSION_DISABLE_VERSION_CHECK_KEY}`, _onVersionValidationChanged);
enabledExtensions = getEnabledExtensions();
let finder = new ExtensionUtils.ExtensionFinder();
finder.connect('extension-found', (finder, extension) => {
loadExtension(extension);
});
finder.scanExtensions();
}
function enableAllExtensions() {
if (enabled)
return;
if (!initted) {
_loadExtensions();
initted = true;
} else {
enabledExtensions.forEach(uuid => {
enableExtension(uuid);
});
this._enabledExtensions = newEnabledExtensions;
}
enabled = true;
}
_onSettingsWritableChanged() {
for (let extension of this._extensions.values()) {
this._updateCanChange(extension);
this.emit('extension-state-changed', extension);
}
}
function disableAllExtensions() {
if (!enabled)
return;
_onVersionValidationChanged() {
// we want to reload all extensions, but only enable
// extensions when allowed by the sessionMode, so
// temporarily disable them all
this._enabledExtensions = [];
// The loop modifies the extensions map, so iterate over a copy
let extensions = [...this._extensions.values()];
for (let extension of extensions)
this.reloadExtension(extension);
this._enabledExtensions = this._getEnabledExtensions();
if (Main.sessionMode.allowExtensions) {
this._enabledExtensions.forEach(uuid => {
this._callExtensionEnable(uuid);
});
}
}
_loadExtensions() {
global.settings.connect(`changed::${ENABLED_EXTENSIONS_KEY}`,
this._onEnabledExtensionsChanged.bind(this));
global.settings.connect(`changed::${DISABLED_EXTENSIONS_KEY}`,
this._onEnabledExtensionsChanged.bind(this));
global.settings.connect(`changed::${DISABLE_USER_EXTENSIONS_KEY}`,
this._onUserExtensionsEnabledChanged.bind(this));
global.settings.connect(`changed::${EXTENSION_DISABLE_VERSION_CHECK_KEY}`,
this._onVersionValidationChanged.bind(this));
global.settings.connect(`writable-changed::${ENABLED_EXTENSIONS_KEY}`,
this._onSettingsWritableChanged.bind(this));
global.settings.connect(`writable-changed::${DISABLED_EXTENSIONS_KEY}`,
this._onSettingsWritableChanged.bind(this));
this._enabledExtensions = this._getEnabledExtensions();
let perUserDir = Gio.File.new_for_path(global.userdatadir);
FileUtils.collectFromDatadirs('extensions', true, (dir, info) => {
let fileType = info.get_file_type();
if (fileType != Gio.FileType.DIRECTORY)
return;
let uuid = info.get_name();
let existing = this.lookup(uuid);
if (existing) {
log(`Extension ${uuid} already installed in ${existing.path}. ${dir.get_path()} will not be loaded`);
return;
}
let extension;
let type = dir.has_prefix(perUserDir)
? ExtensionType.PER_USER
: ExtensionType.SYSTEM;
try {
extension = this.createExtensionObject(uuid, dir, type);
} catch (e) {
logError(e, `Could not load extension ${uuid}`);
return;
}
this.loadExtension(extension);
if (initted) {
extensionOrder.slice().reverse().forEach(uuid => {
disableExtension(uuid);
});
}
_enableAllExtensions() {
if (this._enabled)
return;
enabled = false;
}
if (!this._initted) {
this._loadExtensions();
this._initted = true;
} else {
this._enabledExtensions.forEach(uuid => {
this._callExtensionEnable(uuid);
});
}
this._enabled = true;
function _sessionUpdated() {
// For now sessionMode.allowExtensions controls extensions from both the
// 'enabled-extensions' preference and the sessionMode.enabledExtensions
// property; it might make sense to make enabledExtensions independent
// from allowExtensions in the future
if (Main.sessionMode.allowExtensions) {
if (initted)
enabledExtensions = getEnabledExtensions();
enableAllExtensions();
} else {
disableAllExtensions();
}
}
_disableAllExtensions() {
if (!this._enabled)
return;
if (this._initted) {
this._extensionOrder.slice().reverse().forEach(uuid => {
this._callExtensionDisable(uuid);
});
}
this._enabled = false;
}
_sessionUpdated() {
// For now sessionMode.allowExtensions controls extensions from both the
// 'enabled-extensions' preference and the sessionMode.enabledExtensions
// property; it might make sense to make enabledExtensions independent
// from allowExtensions in the future
if (Main.sessionMode.allowExtensions) {
if (this._initted)
this._enabledExtensions = this._getEnabledExtensions();
this._enableAllExtensions();
} else {
this._disableAllExtensions();
}
}
};
Signals.addSignalMethods(ExtensionManager.prototype);
function init() {
Main.sessionMode.connect('updated', _sessionUpdated);
_sessionUpdated();
}

View File

@@ -7,14 +7,13 @@ const Signals = imports.signals;
const System = imports.system;
const History = imports.misc.history;
const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionUtils = imports.misc.extensionUtils;
const ShellEntry = imports.ui.shellEntry;
const Tweener = imports.ui.tweener;
const Main = imports.ui.main;
const JsParse = imports.misc.jsParse;
const { ExtensionState } = ExtensionUtils;
const CHEVRON = '>>> ';
/* Imports...feel free to add here as needed */
@@ -624,16 +623,15 @@ var Extensions = class Extensions {
this._extensionsList.add(this._noExtensions);
this.actor.add(this._extensionsList);
Main.extensionManager.getUuids().forEach(uuid => {
for (let uuid in ExtensionUtils.extensions)
this._loadExtension(null, uuid);
});
Main.extensionManager.connect('extension-loaded',
this._loadExtension.bind(this));
ExtensionSystem.connect('extension-loaded',
this._loadExtension.bind(this));
}
_loadExtension(o, uuid) {
let extension = Main.extensionManager.lookup(uuid);
let extension = ExtensionUtils.extensions[uuid];
// There can be cases where we create dummy extension metadata
// that's not really a proper extension. Don't bother with these.
if (!extension.metadata.name)
@@ -690,16 +688,16 @@ var Extensions = class Extensions {
_stateToString(extensionState) {
switch (extensionState) {
case ExtensionState.ENABLED:
case ExtensionSystem.ExtensionState.ENABLED:
return _("Enabled");
case ExtensionState.DISABLED:
case ExtensionState.INITIALIZED:
case ExtensionSystem.ExtensionState.DISABLED:
case ExtensionSystem.ExtensionState.INITIALIZED:
return _("Disabled");
case ExtensionState.ERROR:
case ExtensionSystem.ExtensionState.ERROR:
return _("Error");
case ExtensionState.OUT_OF_DATE:
case ExtensionSystem.ExtensionState.OUT_OF_DATE:
return _("Out of date");
case ExtensionState.DOWNLOADING:
case ExtensionSystem.ExtensionState.DOWNLOADING:
return _("Downloading");
}
return 'Unknown'; // Not translated, shouldn't appear

View File

@@ -46,7 +46,6 @@ const LOG_DOMAIN = 'GNOME Shell';
const GNOMESHELL_STARTED_MESSAGE_ID = 'f3ea493c22934e26811cd62abe8e203a';
var componentManager = null;
var extensionManager = null;
var panel = null;
var overview = null;
var runDialog = null;
@@ -227,8 +226,7 @@ function _initializeUI() {
_startDate = new Date();
ExtensionDownloader.init();
extensionManager = new ExtensionSystem.ExtensionManager();
extensionManager.init();
ExtensionSystem.init();
if (sessionMode.isGreeter && screenShield) {
layoutManager.connect('startup-prepared', () => {

View File

@@ -1,8 +1,10 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const { Gio, GLib, Meta, Shell } = imports.gi;
const Lang = imports.lang;
const Config = imports.misc.config;
const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionDownloader = imports.ui.extensionDownloader;
const ExtensionUtils = imports.misc.extensionUtils;
const Main = imports.ui.main;
@@ -249,26 +251,59 @@ var GnomeShellExtensions = class {
constructor() {
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellExtensionsIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
Main.extensionManager.connect('extension-state-changed',
this._extensionStateChanged.bind(this));
ExtensionSystem.connect('extension-state-changed',
this._extensionStateChanged.bind(this));
}
ListExtensions() {
let out = {};
Main.extensionManager.getUuids().forEach(uuid => {
for (let uuid in ExtensionUtils.extensions) {
let dbusObj = this.GetExtensionInfo(uuid);
out[uuid] = dbusObj;
});
}
return out;
}
GetExtensionInfo(uuid) {
let extension = Main.extensionManager.lookup(uuid) || {};
return ExtensionUtils.serializeExtension(extension);
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
return {};
let obj = {};
Lang.copyProperties(extension.metadata, obj);
// Only serialize the properties that we actually need.
const serializedProperties = ["type", "state", "path", "error", "hasPrefs"];
serializedProperties.forEach(prop => {
obj[prop] = extension[prop];
});
let out = {};
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;
}
out[key] = GLib.Variant.new(type, val);
}
return out;
}
GetExtensionErrors(uuid) {
let extension = Main.extensionManager.lookup(uuid);
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
return [];
@@ -286,14 +321,6 @@ var GnomeShellExtensions = class {
return ExtensionDownloader.uninstallExtension(uuid);
}
EnableExtension(uuid) {
return Main.extensionManager.enableExtension(uuid);
}
DisableExtension(uuid) {
return Main.extensionManager.disableExtension(uuid);
}
LaunchExtensionPrefs(uuid) {
let appSys = Shell.AppSystem.get_default();
let app = appSys.lookup_app('gnome-shell-extension-prefs.desktop');
@@ -304,11 +331,11 @@ var GnomeShellExtensions = class {
}
ReloadExtension(uuid) {
let extension = Main.extensionManager.lookup(uuid);
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
return;
Main.extensionManager.reloadExtension(extension);
ExtensionSystem.reloadExtension(extension);
}
CheckForUpdates() {
@@ -320,10 +347,6 @@ var GnomeShellExtensions = class {
}
_extensionStateChanged(_, newState) {
let state = ExtensionUtils.serializeExtension(newState);
this._dbusImpl.emit_signal('ExtensionStateChanged',
new GLib.Variant('(sa{sv})', [newState.uuid, state]));
this._dbusImpl.emit_signal('ExtensionStatusChanged',
GLib.Variant.new('(sis)', [newState.uuid, newState.state, newState.error]));
}

View File

@@ -114,15 +114,8 @@ var Indicator = class extends PanelMenu.SystemIndicator {
? 'battery-level-100-charged-symbolic'
: `battery-level-${fillLevel}${chargingState}-symbolic`;
// Make sure we fall back to fallback-icon-name and not GThemedIcon's
// default fallbacks
let gicon = new Gio.ThemedIcon({
name: icon,
use_default_fallbacks: false
});
this._indicator.gicon = gicon;
this._item.icon.gicon = gicon;
this._indicator.icon_name = icon;
this._item.icon.icon_name = icon;
let fallbackIcon = this._proxy.IconName;
this._indicator.fallback_icon_name = fallbackIcon;

View File

@@ -2132,8 +2132,6 @@ var WindowManager = class {
let [action,,, target] = binding.get_name().split('-');
let newWs;
let direction;
let vertical = workspaceManager.layout_rows == -1;
let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL;
if (action == 'move') {
// "Moving" a window to another workspace doesn't make sense when
@@ -2146,12 +2144,7 @@ var WindowManager = class {
}
if (target == 'last') {
if (vertical)
direction = Meta.MotionDirection.DOWN;
else if (rtl)
direction = Meta.MotionDirection.LEFT;
else
direction = Meta.MotionDirection.RIGHT;
direction = Meta.MotionDirection.DOWN;
newWs = workspaceManager.get_workspace_by_index(workspaceManager.n_workspaces - 1);
} else if (isNaN(target)) {
// Prepend a new workspace dynamically
@@ -2167,33 +2160,16 @@ var WindowManager = class {
target--;
newWs = workspaceManager.get_workspace_by_index(target);
if (workspaceManager.get_active_workspace().index() > target) {
if (vertical)
direction = Meta.MotionDirection.UP;
else if (rtl)
direction = Meta.MotionDirection.RIGHT;
else
direction = Meta.MotionDirection.LEFT;
} else {
if (vertical)
direction = Meta.MotionDirection.DOWN;
else if (rtl)
direction = Meta.MotionDirection.LEFT;
else
direction = Meta.MotionDirection.RIGHT;
}
if (workspaceManager.get_active_workspace().index() > target)
direction = Meta.MotionDirection.UP;
else
direction = Meta.MotionDirection.DOWN;
}
if (workspaceManager.layout_rows == -1 &&
direction != Meta.MotionDirection.UP &&
if (direction != Meta.MotionDirection.UP &&
direction != Meta.MotionDirection.DOWN)
return;
if (workspaceManager.layout_columns == -1 &&
direction != Meta.MotionDirection.LEFT &&
direction != Meta.MotionDirection.RIGHT)
return;
if (action == 'switch')
this.actionMoveWorkspace(newWs);
else

View File

@@ -86,7 +86,7 @@ class WindowCloneLayout extends Clutter.LayoutManager {
if (child == container._delegate._windowClone)
realWindow = container._delegate.realWindow;
else
realWindow = child.source;
realWindow = child.content.window_actor;
child.allocate(this._makeBoxForWindow(realWindow.meta_window),
flags);
@@ -111,7 +111,7 @@ var WindowClone = GObject.registerClass({
this.metaWindow._delegate = this;
this._workspace = workspace;
this._windowClone = new Clutter.Clone({ source: realWindow });
this._windowClone = new Clutter.Actor({ content: realWindow.content });
// We expect this to be used for all interaction rather than
// this._windowClone; as the former is reactive and the latter
// is not, this just works for most cases. However, for DND all
@@ -205,7 +205,7 @@ var WindowClone = GObject.registerClass({
// Delete all windows, starting from the bottom-most (most-modal) one
let windows = this.get_children();
for (let i = windows.length - 1; i >= 1; i--) {
let realWindow = windows[i].source;
let realWindow = windows[i].content.window_actor;
let metaWindow = realWindow.meta_window;
metaWindow.delete(global.get_current_time());
@@ -237,7 +237,7 @@ var WindowClone = GObject.registerClass({
}
_doAddAttachedDialog(metaWin, realWin) {
let clone = new Clutter.Clone({ source: realWin });
let clone = new Clutter.Actor({ content: realWin.content });
clone._sizeChangedId = metaWin.connect('size-changed',
this._onMetaWindowSizeChanged.bind(this));
clone._posChangedId = metaWin.connect('position-changed',
@@ -290,7 +290,7 @@ var WindowClone = GObject.registerClass({
if (child == this._windowClone)
realWindow = this.realWindow;
else
realWindow = child.source;
realWindow = child.content.window_actor;
let metaWindow = realWindow.meta_window;
rect = rect.union(metaWindow.get_frame_rect());
@@ -335,7 +335,7 @@ var WindowClone = GObject.registerClass({
if (child == this._windowClone)
realWindow = this.realWindow;
else
realWindow = child.source;
realWindow = child.content.window_actor;
realWindow.meta_window.disconnect(child._sizeChangedId);
realWindow.meta_window.disconnect(child._posChangedId);

View File

@@ -17,74 +17,41 @@ class WorkspaceSwitcherPopupList extends St.Widget {
this._itemSpacing = 0;
this._childHeight = 0;
this._childWidth = 0;
this._orientation = global.workspace_manager.layout_rows == -1
? Clutter.Orientation.VERTICAL
: Clutter.Orientation.HORIZONTAL;
this.connect('style-changed', () => {
this._itemSpacing = this.get_theme_node().get_length('spacing');
});
}
_getPreferredSizeForOrientation(forSize) {
vfunc_get_preferred_height(forWidth) {
let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
let themeNode = this.get_theme_node();
let availSize;
if (this._orientation == Clutter.Orientation.HORIZONTAL)
availSize = workArea.width - themeNode.get_horizontal_padding();
else
availSize = workArea.height - themeNode.get_vertical_padding();
let availHeight = workArea.height;
availHeight -= themeNode.get_vertical_padding();
let size = 0;
let height = 0;
for (let child of this.get_children()) {
let [childMinHeight, childNaturalHeight] = child.get_preferred_height(-1);
let height = childNaturalHeight * workArea.width / workArea.height;
if (this._orientation == Clutter.Orientation.HORIZONTAL)
size += height * workArea.width / workArea.height;
else
size += height;
let [childMinWidth, childNaturalWidth] = child.get_preferred_width(childNaturalHeight);
height += childNaturalHeight * workArea.width / workArea.height;
}
let workspaceManager = global.workspace_manager;
let spacing = this._itemSpacing * (workspaceManager.n_workspaces - 1);
size += spacing;
size = Math.min(size, availSize);
height += spacing;
height = Math.min(height, availHeight);
if (this._orientation == Clutter.Orientation.HORIZONTAL) {
this._childWidth = (size - spacing) / workspaceManager.n_workspaces;
return themeNode.adjust_preferred_width(size, size);
} else {
this._childHeight = (size - spacing) / workspaceManager.n_workspaces;
return themeNode.adjust_preferred_height(size, size);
}
}
this._childHeight = (height - spacing) / workspaceManager.n_workspaces;
_getSizeForOppositeOrientation() {
let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
if (this._orientation == Clutter.Orientation.HORIZONTAL) {
this._childHeight = Math.round(this._childWidth * workArea.height / workArea.width);
return [this._childHeight, this._childHeight];
} else {
this._childWidth = Math.round(this._childHeight * workArea.width / workArea.height);
return [this._childWidth, this._childWidth];
}
}
vfunc_get_preferred_height(forWidth) {
if (this._orientation == Clutter.Orientation.HORIZONTAL)
return this._getSizeForOppositeOrientation();
else
return this._getPreferredSizeForOrientation(forWidth);
return themeNode.adjust_preferred_height(height, height);
}
vfunc_get_preferred_width(forHeight) {
if (this._orientation == Clutter.Orientation.HORIZONTAL)
return this._getPreferredSizeForOrientation(forHeight);
else
return this._getSizeForOppositeOrientation();
let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
this._childWidth = Math.round(this._childHeight * workArea.width / workArea.height);
return [this._childWidth, this._childWidth];
}
vfunc_allocate(box, flags) {
@@ -95,23 +62,15 @@ class WorkspaceSwitcherPopupList extends St.Widget {
let childBox = new Clutter.ActorBox();
let rtl = this.text_direction == Clutter.TextDirection.RTL;
let x = rtl ? box.x2 - this._childWidth : box.x1;
let y = box.y1;
let prevChildBoxY2 = box.y1 - this._itemSpacing;
for (let child of this.get_children()) {
childBox.x1 = Math.round(x);
childBox.x2 = Math.round(x + this._childWidth);
childBox.y1 = Math.round(y);
childBox.x1 = box.x1;
childBox.x2 = box.x1 + this._childWidth;
childBox.y1 = prevChildBoxY2 + this._itemSpacing;
childBox.y2 = Math.round(y + this._childHeight);
if (this._orientation == Clutter.Orientation.HORIZONTAL) {
if (rtl)
x -= this._childWidth + this._itemSpacing;
else
x += this._childWidth + this._itemSpacing;
} else {
y += this._childHeight + this._itemSpacing;
}
y += this._childHeight + this._itemSpacing;
prevChildBoxY2 = childBox.y2;
child.allocate(childBox, flags);
}
}
@@ -162,10 +121,6 @@ class WorkspaceSwitcherPopup extends St.Widget {
indicator = new St.Bin({ style_class: 'ws-switcher-active-up' });
else if (i == this._activeWorkspaceIndex && this._direction == Meta.MotionDirection.DOWN)
indicator = new St.Bin({ style_class: 'ws-switcher-active-down' });
else if (i == this._activeWorkspaceIndex && this._direction == Meta.MotionDirection.LEFT)
indicator = new St.Bin({ style_class: 'ws-switcher-active-left' });
else if (i == this._activeWorkspaceIndex && this._direction == Meta.MotionDirection.RIGHT)
indicator = new St.Bin({ style_class: 'ws-switcher-active-right' });
else
indicator = new St.Bin({ style_class: 'ws-switcher-box' });

View File

@@ -47,7 +47,8 @@ class PrimaryActorLayout extends Clutter.FixedLayout {
var WindowClone = class {
constructor(realWindow) {
this.clone = new Clutter.Clone({ source: realWindow });
this.clone = new Clutter.Actor({ content: realWindow.content,
request_mode: Clutter.RequestMode.CONTENT_SIZE });
/* Can't use a Shell.GenericContainer because of DND and reparenting... */
this.actor = new Clutter.Actor({ layout_manager: new PrimaryActorLayout(this.clone),
@@ -139,7 +140,8 @@ var WindowClone = class {
}
_doAddAttachedDialog(metaDialog, realDialog) {
let clone = new Clutter.Clone({ source: realDialog });
let clone = new Clutter.Actor({ content: realDialog.content,
request_mode: Clutter.RequestMode.CONTENT_SIZE });
this._updateDialogPosition(realDialog, clone);
clone._updateId = realDialog.connect('notify::position', dialog => {
@@ -165,7 +167,7 @@ var WindowClone = class {
_disconnectSignals() {
this.actor.get_children().forEach(child => {
let realWindow = child.source;
let realWindow = child.content.window_actor;
realWindow.disconnect(child._updateId);
realWindow.disconnect(child._destroyId);

View File

@@ -189,32 +189,26 @@ var WorkspacesView = class extends WorkspacesViewBase {
Tweener.removeTweens(workspace.actor);
let params = {};
if (workspaceManager.layout_rows == -1)
params.y = (w - active) * this._fullGeometry.height;
else if (this.actor.text_direction == Clutter.TextDirection.RTL)
params.x = (active - w) * this._fullGeometry.width;
else
params.x = (w - active) * this._fullGeometry.width;
let y = (w - active) * this._fullGeometry.height;
if (showAnimation) {
let tweenParams = Object.assign(params, {
time: WORKSPACE_SWITCH_TIME,
transition: 'easeOutQuad'
});
let params = { y: y,
time: WORKSPACE_SWITCH_TIME,
transition: 'easeOutQuad'
};
// we have to call _updateVisibility() once before the
// animation and once afterwards - it does not really
// matter which tween we use, so we pick the first one ...
if (w == 0) {
this._updateVisibility();
tweenParams.onComplete = () => {
params.onComplete = () => {
this._animating = false;
this._updateVisibility();
};
}
Tweener.addTween(workspace.actor, tweenParams);
Tweener.addTween(workspace.actor, params);
} else {
workspace.actor.set(params);
workspace.actor.set_position(0, y);
if (w == 0)
this._updateVisibility();
}
@@ -352,39 +346,22 @@ var WorkspacesView = class extends WorkspacesViewBase {
metaWorkspace.activate(global.get_current_time());
}
let last = this._workspaces.length - 1;
let firstWorkspaceY = this._workspaces[0].actor.y;
let lastWorkspaceY = this._workspaces[last].actor.y;
let workspacesHeight = lastWorkspaceY - firstWorkspaceY;
if (adj.upper == 1)
return;
let last = this._workspaces.length - 1;
let currentY = firstWorkspaceY;
let newY = - adj.value / (adj.upper - 1) * workspacesHeight;
if (workspaceManager.layout_rows == -1) {
let firstWorkspaceY = this._workspaces[0].actor.y;
let lastWorkspaceY = this._workspaces[last].actor.y;
let workspacesHeight = lastWorkspaceY - firstWorkspaceY;
let dy = newY - currentY;
let currentY = firstWorkspaceY;
let newY = -adj.value / (adj.upper - 1) * workspacesHeight;
let dy = newY - currentY;
for (let i = 0; i < this._workspaces.length; i++) {
this._workspaces[i].actor.visible = Math.abs(i - adj.value) <= 1;
this._workspaces[i].actor.y += dy;
}
} else {
let firstWorkspaceX = this._workspaces[0].actor.x;
let lastWorkspaceX = this._workspaces[last].actor.x;
let workspacesWidth = lastWorkspaceX - firstWorkspaceX;
let currentX = firstWorkspaceX;
let newX = -adj.value / (adj.upper - 1) * workspacesWidth;
let dx = newX - currentX;
for (let i = 0; i < this._workspaces.length; i++) {
this._workspaces[i].actor.visible = Math.abs(i - adj.value) <= 1;
this._workspaces[i].actor.x += dx;
}
for (let i = 0; i < this._workspaces.length; i++) {
this._workspaces[i].actor.visible = Math.abs(i - adj.value) <= 1;
this._workspaces[i].actor.y += dy;
}
}
};
@@ -532,12 +509,7 @@ var WorkspacesDisplay = class {
_onPan(action) {
let [dist, dx, dy] = action.get_motion_delta(0);
let adjustment = this._scrollAdjustment;
if (global.workspace_manager.layout_rows == -1)
adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
else if (this.actor.text_direction == Clutter.TextDirection.RTL)
adjustment.value += (dx / this.actor.width) * adjustment.page_size;
else
adjustment.value -= (dx / this.actor.width) * adjustment.page_size;
adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
return false;
}
@@ -569,12 +541,7 @@ var WorkspacesDisplay = class {
let workspaceManager = global.workspace_manager;
let active = workspaceManager.get_active_workspace_index();
let adjustment = this._scrollAdjustment;
if (workspaceManager.layout_rows == -1)
adjustment.value = (active - yRel / this.actor.height) * adjustment.page_size;
else if (this.actor.text_direction == Clutter.TextDirection.RTL)
adjustment.value = (active + xRel / this.actor.width) * adjustment.page_size;
else
adjustment.value = (active - xRel / this.actor.width) * adjustment.page_size;
adjustment.value = (active - yRel / this.actor.height) * adjustment.page_size;
}
_onSwitchWorkspaceActivated(action, direction) {
@@ -798,12 +765,6 @@ var WorkspacesDisplay = class {
case Clutter.ScrollDirection.DOWN:
ws = activeWs.get_neighbor(Meta.MotionDirection.DOWN);
break;
case Clutter.ScrollDirection.LEFT:
ws = activeWs.get_neighbor(Meta.MotionDirection.LEFT);
break;
case Clutter.ScrollDirection.RIGHT:
ws = activeWs.get_neighbor(Meta.MotionDirection.RIGHT);
break;
default:
return Clutter.EVENT_PROPAGATE;
}

View File

@@ -1,5 +1,5 @@
project('gnome-shell', 'c',
version: '3.33.4',
version: '3.33.3',
meson_version: '>= 0.47.0',
license: 'GPLv2+'
)
@@ -17,11 +17,13 @@ croco_req = '>= 0.6.8'
ecal_req = '>= 3.33.1'
eds_req = '>= 3.17.2'
gcr_req = '>= 3.7.5'
gdesktop_req = '>= 3.7.90'
gio_req = '>= 2.56.0'
gi_req = '>= 1.49.1'
gjs_req = '>= 1.57.3'
gtk_req = '>= 3.15.0'
mutter_req = '>= 3.33.4'
json_glib_req = '>= 0.13.2'
mutter_req = '>= 3.33.3'
polkit_req = '>= 0.100'
schemas_req = '>= 3.27.90'
startup_req = '>= 0.11'

View File

@@ -372,7 +372,7 @@ dump_gjs_stack_alarm_sigaction (int signo)
static void
dump_gjs_stack_on_signal_handler (int signo)
{
struct sigaction sa = { .sa_handler = dump_gjs_stack_alarm_sigaction };
struct sigaction sa = { 0 };
gsize i;
/* Ignore all the signals starting this point, a part the one we'll raise
@@ -388,6 +388,7 @@ dump_gjs_stack_on_signal_handler (int signo)
/* Waiting at least 5 seconds for the dumpstack, if it fails, we raise the error */
caught_signal = signo;
sa.sa_handler = dump_gjs_stack_alarm_sigaction;
sigemptyset (&sa.sa_mask);
sigaction (SIGALRM, &sa, NULL);
@@ -401,11 +402,10 @@ dump_gjs_stack_on_signal_handler (int signo)
static void
dump_gjs_stack_on_signal (int signo)
{
struct sigaction sa = {
.sa_flags = SA_RESETHAND | SA_NODEFER,
.sa_handler = dump_gjs_stack_on_signal_handler,
};
struct sigaction sa = { 0 };
sa.sa_flags = SA_RESETHAND | SA_NODEFER;
sa.sa_handler = dump_gjs_stack_on_signal_handler;
sigemptyset (&sa.sa_mask);
sigaction (signo, &sa, NULL);

View File

@@ -108,7 +108,7 @@ shell_secure_text_buffer_real_insert_text (ClutterTextBuffer *buffer,
/* Actual text insertion */
at = g_utf8_offset_to_pointer (self->text, position) - self->text;
memmove (self->text + at + n_bytes, self->text + at, self->text_bytes - at);
g_memmove (self->text + at + n_bytes, self->text + at, self->text_bytes - at);
memcpy (self->text + at, chars, n_bytes);
/* Book keeping */
@@ -138,7 +138,7 @@ shell_secure_text_buffer_real_delete_text (ClutterTextBuffer *buffer,
start = g_utf8_offset_to_pointer (self->text, position) - self->text;
end = g_utf8_offset_to_pointer (self->text, position + n_chars) - self->text;
memmove (self->text + start, self->text + end, self->text_bytes + 1 - end);
g_memmove (self->text + start, self->text + end, self->text_bytes + 1 - end);
self->text_chars -= n_chars;
self->text_bytes -= (end - start);