ca4e563f55
The `GetWindows` method gives access to the list of windows for each application with some of their properties, so utilities such as dogtail can pick the window of their choice to interfere with using the provided window id. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/326
167 lines
5.9 KiB
JavaScript
167 lines
5.9 KiB
JavaScript
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]));
|
|
}
|
|
});
|