dbusServices: Add some base classes for small stand-alone services
There are a couple of D-Bus services that are currently provided by gnome-shell for which it makes sense to move them fully or partially into separate processes: - screen recording (performance) - FDO notifications (security) - Extensions (portalization) Add some base classes and build system glue to take care of the common boilerplate. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/547
This commit is contained in:

committed by
Florian Müllner

parent
f8db5aa106
commit
574c560677
159
js/dbusServices/dbusService.js
Normal file
159
js/dbusServices/dbusService.js
Normal file
@ -0,0 +1,159 @@
|
||||
/* exported DBusService, ServiceImplementation */
|
||||
|
||||
const { Gio, GLib } = imports.gi;
|
||||
|
||||
const Signals = imports.signals;
|
||||
|
||||
const IDLE_SHUTDOWN_TIME = 2; // s
|
||||
|
||||
var ServiceImplementation = class {
|
||||
constructor(info, objectPath) {
|
||||
this._objectPath = objectPath;
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(info, this);
|
||||
|
||||
this._injectTracking('return_dbus_error');
|
||||
this._injectTracking('return_error_literal');
|
||||
this._injectTracking('return_gerror');
|
||||
this._injectTracking('return_value');
|
||||
this._injectTracking('return_value_with_unix_fd_list');
|
||||
|
||||
this._senders = new Map();
|
||||
|
||||
this._hasSignals = this._dbusImpl.get_info().signals.length > 0;
|
||||
this._shutdownTimeoutId = 0;
|
||||
|
||||
// subclasses may override this to disable automatic shutdown
|
||||
this._autoShutdown = true;
|
||||
}
|
||||
|
||||
// subclasses may override this to own additional names
|
||||
register() {
|
||||
}
|
||||
|
||||
export() {
|
||||
this._dbusImpl.export(Gio.DBus.session, this._objectPath);
|
||||
}
|
||||
|
||||
unexport() {
|
||||
this._dbusImpl.unexport();
|
||||
}
|
||||
|
||||
/**
|
||||
* _handleError:
|
||||
* @param {Gio.DBusMethodInvocation}
|
||||
* @param {Error}
|
||||
*
|
||||
* Complete @invocation with an appropriate error if @error is set;
|
||||
* useful for implementing early returns from method implementations.
|
||||
*
|
||||
* @returns {bool} - true if @invocation was completed
|
||||
*/
|
||||
|
||||
_handleError(invocation, error) {
|
||||
if (error === null)
|
||||
return false;
|
||||
|
||||
if (error instanceof GLib.Error) {
|
||||
invocation.return_gerror(error);
|
||||
} else {
|
||||
let name = error.name;
|
||||
if (!name.includes('.')) // likely a normal JS error
|
||||
name = `org.gnome.gjs.JSError.${name}`;
|
||||
invocation.return_dbus_error(name, error.message);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_maybeShutdown() {
|
||||
if (!this._autoShutdown)
|
||||
return;
|
||||
|
||||
if (this._senders.size > 0)
|
||||
return;
|
||||
|
||||
this.emit('shutdown');
|
||||
}
|
||||
|
||||
_queueShutdownCheck() {
|
||||
if (this._shutdownTimeoutId)
|
||||
GLib.source_remove(this._shutdownTimeoutId);
|
||||
|
||||
this._shutdownTimeoutId = GLib.timeout_add_seconds(
|
||||
GLib.PRIORITY_DEFAULT, IDLE_SHUTDOWN_TIME,
|
||||
() => {
|
||||
this._shutdownTimeoutId = 0;
|
||||
this._maybeShutdown();
|
||||
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
_trackSender(sender) {
|
||||
if (this._senders.has(sender))
|
||||
return;
|
||||
|
||||
this._senders.set(sender,
|
||||
this._dbusImpl.get_connection().watch_name(
|
||||
sender,
|
||||
Gio.BusNameWatcherFlags.NONE,
|
||||
null,
|
||||
() => this._untrackSender(sender)));
|
||||
}
|
||||
|
||||
_untrackSender(sender) {
|
||||
const id = this._senders.get(sender);
|
||||
|
||||
if (id)
|
||||
this._dbusImpl.get_connection().unwatch_name(id);
|
||||
|
||||
if (this._senders.delete(sender))
|
||||
this._queueShutdownCheck();
|
||||
}
|
||||
|
||||
_injectTracking(methodName) {
|
||||
const { prototype } = Gio.DBusMethodInvocation;
|
||||
const origMethod = prototype[methodName];
|
||||
const that = this;
|
||||
|
||||
prototype[methodName] = function (...args) {
|
||||
origMethod.apply(this, args);
|
||||
|
||||
if (that._hasSignals)
|
||||
that._trackSender(this.get_sender());
|
||||
|
||||
that._queueShutdownCheck();
|
||||
};
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(ServiceImplementation.prototype);
|
||||
|
||||
var DBusService = class {
|
||||
constructor(name, service) {
|
||||
this._name = name;
|
||||
this._service = service;
|
||||
this._loop = new GLib.MainLoop(null, false);
|
||||
|
||||
this._service.connect('shutdown', () => this._loop.quit());
|
||||
}
|
||||
|
||||
run() {
|
||||
// Bail out when not running under gnome-shell
|
||||
Gio.DBus.watch_name(Gio.BusType.SESSION,
|
||||
'org.gnome.Shell',
|
||||
Gio.BusNameWatcherFlags.NONE,
|
||||
null,
|
||||
() => this._loop.quit());
|
||||
|
||||
this._service.register();
|
||||
|
||||
Gio.DBus.own_name(Gio.BusType.SESSION,
|
||||
this._name,
|
||||
Gio.BusNameOwnerFlags.REPLACE,
|
||||
() => this._service.export(),
|
||||
null,
|
||||
() => this._loop.quit());
|
||||
|
||||
this._loop.run();
|
||||
}
|
||||
};
|
Reference in New Issue
Block a user