summaryrefslogtreecommitdiffstats
path: root/js/ui/shellDBus.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/ui/shellDBus.js')
-rw-r--r--js/ui/shellDBus.js540
1 files changed, 540 insertions, 0 deletions
diff --git a/js/ui/shellDBus.js b/js/ui/shellDBus.js
new file mode 100644
index 0000000..284d92b
--- /dev/null
+++ b/js/ui/shellDBus.js
@@ -0,0 +1,540 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported GnomeShell, ScreenSaverDBus */
+
+const { Gio, GLib, Meta, Shell } = imports.gi;
+
+const Config = imports.misc.config;
+const ExtensionDownloader = imports.ui.extensionDownloader;
+const ExtensionUtils = imports.misc.extensionUtils;
+const Main = imports.ui.main;
+const Screenshot = imports.ui.screenshot;
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+const { DBusSenderChecker } = imports.misc.util;
+const { ControlsState } = imports.ui.overviewControls;
+
+const GnomeShellIface = loadInterfaceXML('org.gnome.Shell');
+const ScreenSaverIface = loadInterfaceXML('org.gnome.ScreenSaver');
+
+var GnomeShell = class {
+ constructor() {
+ this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellIface, this);
+ this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
+
+ this._senderChecker = new DBusSenderChecker([
+ 'org.gnome.Settings',
+ 'org.gnome.SettingsDaemon.MediaKeys',
+ ]);
+
+ this._extensionsService = new GnomeShellExtensions();
+ this._screenshotService = new Screenshot.ScreenshotService();
+
+ this._grabbedAccelerators = new Map();
+ this._grabbers = new Map();
+
+ global.display.connect('accelerator-activated',
+ (display, action, device, timestamp) => {
+ this._emitAcceleratorActivated(action, device, timestamp);
+ });
+
+ this._cachedOverviewVisible = false;
+ Main.overview.connect('showing',
+ this._checkOverviewVisibleChanged.bind(this));
+ Main.overview.connect('hidden',
+ this._checkOverviewVisibleChanged.bind(this));
+ }
+
+ /**
+ * Eval:
+ * @param {string} code: A string containing JavaScript code
+ * @returns {Array}
+ *
+ * This function executes arbitrary code in the main
+ * loop, and returns a boolean success and
+ * JSON representation of the object as a string.
+ *
+ * If evaluation completes without throwing an exception,
+ * then the return value will be [true, JSON.stringify(result)].
+ * If evaluation fails, then the return value will be
+ * [false, JSON.stringify(exception)];
+ *
+ */
+ Eval(code) {
+ if (!global.context.unsafe_mode)
+ return [false, ''];
+
+ let returnValue;
+ let success;
+ try {
+ returnValue = JSON.stringify(eval(code));
+ // A hack; DBus doesn't have null/undefined
+ if (returnValue == undefined)
+ returnValue = '';
+ success = true;
+ } catch (e) {
+ returnValue = `${e}`;
+ success = false;
+ }
+ return [success, returnValue];
+ }
+
+ /**
+ * Focus the overview's search entry
+ *
+ * @async
+ * @param {...any} params - method parameters
+ * @param {Gio.DBusMethodInvocation} invocation - the invocation
+ * @returns {void}
+ */
+ async FocusSearchAsync(params, invocation) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ Main.overview.focusSearch();
+ invocation.return_value(null);
+ }
+
+ /**
+ * Show OSD with the specified parameters
+ *
+ * @async
+ * @param {...any} params - method parameters
+ * @param {Gio.DBusMethodInvocation} invocation - the invocation
+ * @returns {void}
+ */
+ async ShowOSDAsync([params], invocation) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ for (let param in params)
+ params[param] = params[param].deepUnpack();
+
+ const {
+ connector,
+ label,
+ level,
+ max_level: maxLevel,
+ icon: serializedIcon,
+ } = params;
+
+ let monitorIndex = -1;
+ if (connector) {
+ let monitorManager = Meta.MonitorManager.get();
+ monitorIndex = monitorManager.get_monitor_for_connector(connector);
+ }
+
+ let icon = null;
+ if (serializedIcon)
+ icon = Gio.Icon.new_for_string(serializedIcon);
+
+ Main.osdWindowManager.show(monitorIndex, icon, label, level, maxLevel);
+ invocation.return_value(null);
+ }
+
+ /**
+ * Focus specified app in the overview's app grid
+ *
+ * @async
+ * @param {string} id - an application ID
+ * @param {Gio.DBusMethodInvocation} invocation - the invocation
+ * @returns {void}
+ */
+ async FocusAppAsync([id], invocation) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ const appSys = Shell.AppSystem.get_default();
+ if (appSys.lookup_app(id) === null) {
+ invocation.return_error_literal(
+ Gio.DBusError,
+ Gio.DBusError.FILE_NOT_FOUND,
+ `No application with ID ${id}`);
+ return;
+ }
+
+ Main.overview.selectApp(id);
+ invocation.return_value(null);
+ }
+
+ /**
+ * Show the overview's app grid
+ *
+ * @async
+ * @param {...any} params - method parameters
+ * @param {Gio.DBusMethodInvocation} invocation - the invocation
+ * @returns {void}
+ */
+ async ShowApplicationsAsync(params, invocation) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ Main.overview.show(ControlsState.APP_GRID);
+ invocation.return_value(null);
+ }
+
+ async GrabAcceleratorAsync(params, invocation) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ let [accel, modeFlags, grabFlags] = params;
+ let sender = invocation.get_sender();
+ let bindingAction = this._grabAcceleratorForSender(accel, modeFlags, grabFlags, sender);
+ invocation.return_value(GLib.Variant.new('(u)', [bindingAction]));
+ }
+
+ async GrabAcceleratorsAsync(params, invocation) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ let [accels] = params;
+ let sender = invocation.get_sender();
+ let bindingActions = [];
+ for (let i = 0; i < accels.length; i++) {
+ let [accel, modeFlags, grabFlags] = accels[i];
+ bindingActions.push(this._grabAcceleratorForSender(accel, modeFlags, grabFlags, sender));
+ }
+ invocation.return_value(GLib.Variant.new('(au)', [bindingActions]));
+ }
+
+ async UngrabAcceleratorAsync(params, invocation) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ let [action] = params;
+ let sender = invocation.get_sender();
+ let ungrabSucceeded = this._ungrabAcceleratorForSender(action, sender);
+
+ invocation.return_value(GLib.Variant.new('(b)', [ungrabSucceeded]));
+ }
+
+ async UngrabAcceleratorsAsync(params, invocation) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ let [actions] = params;
+ let sender = invocation.get_sender();
+ let ungrabSucceeded = true;
+
+ for (let i = 0; i < actions.length; i++)
+ ungrabSucceeded &= this._ungrabAcceleratorForSender(actions[i], sender);
+
+ invocation.return_value(GLib.Variant.new('(b)', [ungrabSucceeded]));
+ }
+
+ async ScreenTransitionAsync(params, invocation) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ Main.layoutManager.screenTransition.run();
+
+ invocation.return_value(null);
+ }
+
+ _emitAcceleratorActivated(action, device, timestamp) {
+ let destination = this._grabbedAccelerators.get(action);
+ if (!destination)
+ return;
+
+ let connection = this._dbusImpl.get_connection();
+ let info = this._dbusImpl.get_info();
+ let params = {
+ 'timestamp': GLib.Variant.new('u', timestamp),
+ 'action-mode': GLib.Variant.new('u', Main.actionMode),
+ };
+
+ let deviceNode = device.get_device_node();
+ if (deviceNode)
+ params['device-node'] = GLib.Variant.new('s', deviceNode);
+
+ connection.emit_signal(
+ destination,
+ this._dbusImpl.get_object_path(),
+ info?.name ?? null,
+ 'AcceleratorActivated',
+ GLib.Variant.new('(ua{sv})', [action, params]));
+ }
+
+ _grabAcceleratorForSender(accelerator, modeFlags, grabFlags, sender) {
+ let bindingAction = global.display.grab_accelerator(accelerator, grabFlags);
+ if (bindingAction == Meta.KeyBindingAction.NONE)
+ return Meta.KeyBindingAction.NONE;
+
+ let bindingName = Meta.external_binding_name_for_action(bindingAction);
+ Main.wm.allowKeybinding(bindingName, modeFlags);
+
+ this._grabbedAccelerators.set(bindingAction, sender);
+
+ if (!this._grabbers.has(sender)) {
+ let id = Gio.bus_watch_name(Gio.BusType.SESSION, sender, 0, null,
+ this._onGrabberBusNameVanished.bind(this));
+ this._grabbers.set(sender, id);
+ }
+
+ return bindingAction;
+ }
+
+ _ungrabAccelerator(action) {
+ let ungrabSucceeded = global.display.ungrab_accelerator(action);
+ if (ungrabSucceeded)
+ this._grabbedAccelerators.delete(action);
+
+ return ungrabSucceeded;
+ }
+
+ _ungrabAcceleratorForSender(action, sender) {
+ let grabbedBy = this._grabbedAccelerators.get(action);
+ if (sender != grabbedBy)
+ return false;
+
+ return this._ungrabAccelerator(action);
+ }
+
+ _onGrabberBusNameVanished(connection, name) {
+ let grabs = this._grabbedAccelerators.entries();
+ for (let [action, sender] of grabs) {
+ if (sender == name)
+ this._ungrabAccelerator(action);
+ }
+ Gio.bus_unwatch_name(this._grabbers.get(name));
+ this._grabbers.delete(name);
+ }
+
+ async ShowMonitorLabelsAsync(params, invocation) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ let sender = invocation.get_sender();
+ let [dict] = params;
+ Main.osdMonitorLabeler.show(sender, dict);
+ invocation.return_value(null);
+ }
+
+ async HideMonitorLabelsAsync(params, invocation) {
+ try {
+ await this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
+ let sender = invocation.get_sender();
+ Main.osdMonitorLabeler.hide(sender);
+ invocation.return_value(null);
+ }
+
+ _checkOverviewVisibleChanged() {
+ if (Main.overview.visible !== this._cachedOverviewVisible) {
+ this._cachedOverviewVisible = Main.overview.visible;
+ this._dbusImpl.emit_property_changed('OverviewActive', new GLib.Variant('b', this._cachedOverviewVisible));
+ }
+ }
+
+ get Mode() {
+ return global.session_mode;
+ }
+
+ get OverviewActive() {
+ return this._cachedOverviewVisible;
+ }
+
+ set OverviewActive(visible) {
+ if (visible)
+ Main.overview.show();
+ else
+ Main.overview.hide();
+ }
+
+ get ShellVersion() {
+ return Config.PACKAGE_VERSION;
+ }
+};
+
+const GnomeShellExtensionsIface = loadInterfaceXML('org.gnome.Shell.Extensions');
+
+var GnomeShellExtensions = class {
+ constructor() {
+ this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellExtensionsIface, this);
+ this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
+
+ this._userExtensionsEnabled = this.UserExtensionsEnabled;
+ global.settings.connect('changed::disable-user-extensions', () => {
+ if (this._userExtensionsEnabled === this.UserExtensionsEnabled)
+ return;
+
+ this._userExtensionsEnabled = this.UserExtensionsEnabled;
+ this._dbusImpl.emit_property_changed('UserExtensionsEnabled',
+ new GLib.Variant('b', this._userExtensionsEnabled));
+ });
+
+ Main.extensionManager.connect('extension-state-changed',
+ this._extensionStateChanged.bind(this));
+ }
+
+ ListExtensions() {
+ let out = {};
+ Main.extensionManager.getUuids().forEach(uuid => {
+ let dbusObj = this.GetExtensionInfo(uuid);
+ out[uuid] = dbusObj;
+ });
+ return out;
+ }
+
+ GetExtensionInfo(uuid) {
+ let extension = Main.extensionManager.lookup(uuid) || {};
+ return ExtensionUtils.serializeExtension(extension);
+ }
+
+ GetExtensionErrors(uuid) {
+ let extension = Main.extensionManager.lookup(uuid);
+ if (!extension)
+ return [];
+
+ if (!extension.errors)
+ return [];
+
+ return extension.errors;
+ }
+
+ InstallRemoteExtensionAsync([uuid], invocation) {
+ return ExtensionDownloader.installExtension(uuid, invocation);
+ }
+
+ UninstallExtension(uuid) {
+ return ExtensionDownloader.uninstallExtension(uuid);
+ }
+
+ EnableExtension(uuid) {
+ return Main.extensionManager.enableExtension(uuid);
+ }
+
+ DisableExtension(uuid) {
+ return Main.extensionManager.disableExtension(uuid);
+ }
+
+ LaunchExtensionPrefs(uuid) {
+ this.OpenExtensionPrefs(uuid, '', {});
+ }
+
+ OpenExtensionPrefs(uuid, parentWindow, options) {
+ Main.extensionManager.openExtensionPrefs(uuid, parentWindow, options);
+ }
+
+ ReloadExtensionAsync(params, invocation) {
+ invocation.return_error_literal(
+ Gio.DBusError,
+ Gio.DBusError.NOT_SUPPORTED,
+ 'ReloadExtension is deprecated and does not work');
+ }
+
+ CheckForUpdates() {
+ ExtensionDownloader.checkForUpdates();
+ }
+
+ get ShellVersion() {
+ return Config.PACKAGE_VERSION;
+ }
+
+ get UserExtensionsEnabled() {
+ return !global.settings.get_boolean('disable-user-extensions');
+ }
+
+ set UserExtensionsEnabled(enable) {
+ global.settings.set_boolean('disable-user-extensions', !enable);
+ }
+
+ _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]));
+ }
+};
+
+var ScreenSaverDBus = class {
+ constructor(screenShield) {
+ this._screenShield = screenShield;
+ screenShield.connect('active-changed', shield => {
+ this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.active]));
+ });
+ screenShield.connect('wake-up-screen', () => {
+ this._dbusImpl.emit_signal('WakeUpScreen', null);
+ });
+
+ this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenSaverIface, this);
+ this._dbusImpl.export(Gio.DBus.session, '/org/gnome/ScreenSaver');
+
+ Gio.DBus.session.own_name('org.gnome.Shell.ScreenShield',
+ Gio.BusNameOwnerFlags.NONE, null, null);
+ }
+
+ LockAsync(parameters, invocation) {
+ let tmpId = this._screenShield.connect('lock-screen-shown', () => {
+ this._screenShield.disconnect(tmpId);
+
+ invocation.return_value(null);
+ });
+
+ this._screenShield.lock(true);
+ }
+
+ SetActive(active) {
+ if (active)
+ this._screenShield.activate(true);
+ else
+ this._screenShield.deactivate(false);
+ }
+
+ GetActive() {
+ return this._screenShield.active;
+ }
+
+ GetActiveTime() {
+ let started = this._screenShield.activationTime;
+ if (started > 0)
+ return Math.floor((GLib.get_monotonic_time() - started) / 1000000);
+ else
+ return 0;
+ }
+};