const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Lang = imports.lang; const Meta = imports.gi.Meta; const Shell = imports.gi.Shell; const INTROSPECT_SCHEMA = 'org.gnome.shell'; const INTROSPECT_KEY = 'introspect'; const APP_WHITELIST = ['org.freedesktop.impl.portal.desktop.gtk']; const { loadInterfaceXML } = imports.misc.fileUtils; const IntrospectDBusIface = loadInterfaceXML('org.gnome.Shell.Introspect'); var IntrospectService = new Lang.Class({ Name: 'IntrospectService', _init() { this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(IntrospectDBusIface, this); this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Introspect'); Gio.DBus.session.own_name('org.gnome.Shell.Introspect', Gio.BusNameOwnerFlags.REPLACE, null, null); this._runningApplications = {}; this._runningApplicationsDirty = true; this._activeApplication = null; this._activeApplicationDirty = true; this._appSystem = Shell.AppSystem.get_default(); this._appSystem.connect('app-state-changed', () => { this._runningApplicationsDirty = true; this._syncRunningApplications(); }); this._settings = new Gio.Settings({ schema_id: INTROSPECT_SCHEMA }); let tracker = Shell.WindowTracker.get_default(); tracker.connect('notify::focus-app', () => { this._activeApplicationDirty = true; this._syncRunningApplications(); }); this._syncRunningApplications(); }, _isStandaloneApp(app) { let windows = app.get_windows(); return app.get_windows().some(w => w.transient_for == null); }, _isIntrospectEnabled() { return this._settings.get_boolean(INTROSPECT_KEY); }, _isSenderWhitelisted(sender) { return APP_WHITELIST.includes(sender); }, _syncRunningApplications() { let tracker = Shell.WindowTracker.get_default(); let apps = this._appSystem.get_running(); let seatName = "seat0"; let newRunningApplications = {}; let newActiveApplication = null; let focusedApp = tracker.focus_app; for (let app of apps) { let appInfo = {}; let isAppActive = (focusedApp == app); if (!this._isStandaloneApp(app)) continue; if (isAppActive) { appInfo['active-on-seats'] = new GLib.Variant('as', [seatName]); newActiveApplication = app.get_id(); } newRunningApplications[app.get_id()] = appInfo; } if (this._runningApplicationsDirty || (this._activeApplicationDirty && this._activeApplication != newActiveApplication)) { this._runningApplications = newRunningApplications; this._activeApplication = newActiveApplication; this._dbusImpl.emit_signal('RunningApplicationsChanged', null); } this._runningApplicationsDirty = false; this._activeApplicationDirty = false; }, _isEligibleWindow(window) { if (window.is_override_redirect()) return false; let type = window.get_window_type(); return (type == Meta.WindowType.NORMAL || type == Meta.WindowType.DIALOG || type == Meta.WindowType.MODAL_DIALOG || type == Meta.WindowType.UTILITY); }, GetRunningApplicationsAsync(params, invocation) { if (!this._isIntrospectEnabled() && !this._isSenderWhitelisted(invocation.get_sender())) { invocation.return_error_literal(Gio.DBusError, Gio.DBusError.ACCESS_DENIED, 'App introspection not allowed'); return; } invocation.return_value(new GLib.Variant('(a{sa{sv}})', [this._runningApplications])); }, GetWindowsAsync(params, invocation) { let focusWindow = global.display.get_focus_window(); let apps = this._appSystem.get_running(); let windowsList = {}; if (!this._isIntrospectEnabled()) { invocation.return_error_literal(Gio.DBusError, Gio.DBusError.ACCESS_DENIED, 'App introspection not allowed'); return; } for (let app of apps) { let windows = app.get_windows(); for (let window of windows) { if (!this._isEligibleWindow(window)) continue; let windowId = window.get_id(); let frameRect = window.get_frame_rect(); let title = window.get_title(); let wmClass = window.get_wm_class(); windowsList[windowId] = { 'app-id': GLib.Variant.new('s', app.get_id()), 'client-type': GLib.Variant.new('u', window.get_client_type()), 'is-hidden': GLib.Variant.new('b', window.is_hidden()), 'has-focus': GLib.Variant.new('b', (window == focusWindow)), 'width': GLib.Variant.new('u', frameRect.width), 'height': GLib.Variant.new('u', frameRect.height) }; // These properties may not be available for all windows: if (title != null) windowsList[windowId]['title'] = GLib.Variant.new('s', title); if (wmClass != null) windowsList[windowId]['wm-class'] = GLib.Variant.new('s', wmClass); } } invocation.return_value(new GLib.Variant('(a{ta{sv}})', [windowsList])); } });