2582d16ca7
Any symbols (including class properties) that should be visible outside the module it's defined in need to be defined as global. For now gjs still allows the access for 'const', but get rid of the warnings spill now by changing it. https://bugzilla.gnome.org/show_bug.cgi?id=785084
295 lines
10 KiB
JavaScript
295 lines
10 KiB
JavaScript
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
|
|
const Gio = imports.gi.Gio;
|
|
const GLib = imports.gi.GLib;
|
|
const Lang = imports.lang;
|
|
const Params = imports.misc.params;
|
|
const Signals = imports.signals;
|
|
|
|
// Specified in the D-Bus specification here:
|
|
// http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager
|
|
const ObjectManagerIface = '<node> \
|
|
<interface name="org.freedesktop.DBus.ObjectManager"> \
|
|
<method name="GetManagedObjects"> \
|
|
<arg name="objects" type="a{oa{sa{sv}}}" direction="out"/> \
|
|
</method> \
|
|
<signal name="InterfacesAdded"> \
|
|
<arg name="objectPath" type="o"/> \
|
|
<arg name="interfaces" type="a{sa{sv}}" /> \
|
|
</signal> \
|
|
<signal name="InterfacesRemoved"> \
|
|
<arg name="objectPath" type="o"/> \
|
|
<arg name="interfaces" type="as" /> \
|
|
</signal> \
|
|
</interface> \
|
|
</node>';
|
|
|
|
const ObjectManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ObjectManagerIface);
|
|
|
|
var ObjectManager = new Lang.Class({
|
|
Name: 'ObjectManager',
|
|
_init: function(params) {
|
|
params = Params.parse(params, { connection: null,
|
|
name: null,
|
|
objectPath: null,
|
|
knownInterfaces: null,
|
|
cancellable: null,
|
|
onLoaded: null });
|
|
|
|
this._connection = params.connection;
|
|
this._serviceName = params.name;
|
|
this._managerPath = params.objectPath;
|
|
this._cancellable = params.cancellable;
|
|
|
|
this._managerProxy = new Gio.DBusProxy({ g_connection: this._connection,
|
|
g_interface_name: ObjectManagerInfo.name,
|
|
g_interface_info: ObjectManagerInfo,
|
|
g_name: this._serviceName,
|
|
g_object_path: this._managerPath,
|
|
g_flags: Gio.DBusProxyFlags.DO_NOT_AUTO_START });
|
|
|
|
this._interfaceInfos = {};
|
|
this._objects = {};
|
|
this._interfaces = {};
|
|
this._onLoaded = params.onLoaded;
|
|
|
|
if (params.knownInterfaces)
|
|
this._registerInterfaces(params.knownInterfaces);
|
|
|
|
// Start out inhibiting load until at least the proxy
|
|
// manager is loaded and the remote objects are fetched
|
|
this._numLoadInhibitors = 1;
|
|
this._managerProxy.init_async(GLib.PRIORITY_DEFAULT,
|
|
this._cancellable,
|
|
Lang.bind(this, this._onManagerProxyLoaded));
|
|
},
|
|
|
|
_tryToCompleteLoad: function() {
|
|
if (this._numLoadInhibitors == 0)
|
|
return;
|
|
|
|
this._numLoadInhibitors--;
|
|
if (this._numLoadInhibitors == 0) {
|
|
if (this._onLoaded)
|
|
this._onLoaded();
|
|
}
|
|
},
|
|
|
|
_addInterface: function(objectPath, interfaceName, onFinished) {
|
|
let info = this._interfaceInfos[interfaceName];
|
|
|
|
if (!info) {
|
|
if (onFinished)
|
|
onFinished();
|
|
return;
|
|
}
|
|
|
|
let proxy = new Gio.DBusProxy({ g_connection: this._connection,
|
|
g_name: this._serviceName,
|
|
g_object_path: objectPath,
|
|
g_interface_name: interfaceName,
|
|
g_interface_info: info,
|
|
g_flags: Gio.DBusProxyFlags.DO_NOT_AUTO_START });
|
|
|
|
proxy.init_async(GLib.PRIORITY_DEFAULT,
|
|
this._cancellable,
|
|
Lang.bind(this, function(initable, result) {
|
|
let error = null;
|
|
try {
|
|
initable.init_finish(result);
|
|
} catch(e) {
|
|
logError(e, 'could not initialize proxy for interface ' + interfaceName);
|
|
|
|
if (onFinished)
|
|
onFinished();
|
|
return;
|
|
}
|
|
|
|
let isNewObject;
|
|
if (!this._objects[objectPath]) {
|
|
this._objects[objectPath] = {};
|
|
isNewObject = true;
|
|
} else {
|
|
isNewObject = false;
|
|
}
|
|
|
|
this._objects[objectPath][interfaceName] = proxy;
|
|
|
|
if (!this._interfaces[interfaceName])
|
|
this._interfaces[interfaceName] = [];
|
|
|
|
this._interfaces[interfaceName].push(proxy);
|
|
|
|
if (isNewObject)
|
|
this.emit('object-added', objectPath);
|
|
|
|
this.emit('interface-added', interfaceName, proxy);
|
|
|
|
if (onFinished)
|
|
onFinished();
|
|
}));
|
|
},
|
|
|
|
_removeInterface: function(objectPath, interfaceName) {
|
|
if (!this._objects[objectPath])
|
|
return;
|
|
|
|
let proxy = this._objects[objectPath][interfaceName];
|
|
|
|
if (this._interfaces[interfaceName]) {
|
|
let index = this._interfaces[interfaceName].indexOf(proxy);
|
|
|
|
if (index >= 0)
|
|
this._interfaces[interfaceName].splice(index, 1);
|
|
|
|
if (this._interfaces[interfaceName].length == 0)
|
|
delete this._interfaces[interfaceName];
|
|
}
|
|
|
|
this.emit('interface-removed', interfaceName, proxy);
|
|
|
|
this._objects[objectPath][interfaceName] = null;
|
|
|
|
if (Object.keys(this._objects[objectPath]).length == 0) {
|
|
delete this._objects[objectPath];
|
|
this.emit('object-removed', objectPath);
|
|
}
|
|
},
|
|
|
|
_onManagerProxyLoaded: function(initable, result) {
|
|
let error = null;
|
|
try {
|
|
initable.init_finish(result);
|
|
} catch(e) {
|
|
logError(e, 'could not initialize object manager for object ' + params.name);
|
|
|
|
this._tryToCompleteLoad();
|
|
return;
|
|
}
|
|
|
|
this._managerProxy.connectSignal('InterfacesAdded',
|
|
Lang.bind(this, function(objectManager, sender, [objectPath, interfaces]) {
|
|
let interfaceNames = Object.keys(interfaces);
|
|
for (let i = 0; i < interfaceNames.length; i++)
|
|
this._addInterface(objectPath, interfaceNames[i]);
|
|
}));
|
|
this._managerProxy.connectSignal('InterfacesRemoved',
|
|
Lang.bind(this, function(objectManager, sender, [objectPath, interfaceNames]) {
|
|
for (let i = 0; i < interfaceNames.length; i++)
|
|
this._removeInterface(objectPath, interfaceNames[i]);
|
|
}));
|
|
|
|
if (Object.keys(this._interfaceInfos).length == 0) {
|
|
this._tryToCompleteLoad();
|
|
return;
|
|
}
|
|
|
|
this._managerProxy.connect('notify::g-name-owner', Lang.bind(this, function() {
|
|
if (this._managerProxy.g_name_owner)
|
|
this._onNameAppeared();
|
|
else
|
|
this._onNameVanished();
|
|
}));
|
|
|
|
if (this._managerProxy.g_name_owner)
|
|
this._onNameAppeared();
|
|
},
|
|
|
|
_onNameAppeared: function() {
|
|
this._managerProxy.GetManagedObjectsRemote(Lang.bind(this, function(result, error) {
|
|
if (!result) {
|
|
if (error) {
|
|
logError(error, 'could not get remote objects for service ' + this._serviceName + ' path ' + this._managerPath);
|
|
}
|
|
|
|
this._tryToCompleteLoad();
|
|
return;
|
|
}
|
|
|
|
let [objects] = result;
|
|
|
|
if (!objects) {
|
|
this._tryToCompleteLoad();
|
|
return;
|
|
}
|
|
|
|
let objectPaths = Object.keys(objects);
|
|
for (let i = 0; i < objectPaths.length; i++) {
|
|
let objectPath = objectPaths[i];
|
|
let object = objects[objectPath];
|
|
|
|
let interfaceNames = Object.getOwnPropertyNames(object);
|
|
for (let j = 0; j < interfaceNames.length; j++) {
|
|
let interfaceName = interfaceNames[j];
|
|
|
|
// Prevent load from completing until the interface is loaded
|
|
this._numLoadInhibitors++;
|
|
this._addInterface(objectPath,
|
|
interfaceName,
|
|
Lang.bind(this, this._tryToCompleteLoad));
|
|
}
|
|
}
|
|
this._tryToCompleteLoad();
|
|
}));
|
|
},
|
|
|
|
_onNameVanished: function() {
|
|
let objectPaths = Object.keys(this._objects);
|
|
for (let i = 0; i < objectPaths.length; i++) {
|
|
let object = this._objects[objectPaths];
|
|
|
|
let interfaceNames = Object.keys(object);
|
|
for (let j = 0; i < interfaceNames.length; i++) {
|
|
let interfaceName = interfaceNames[i];
|
|
|
|
if (object[interfaceName])
|
|
this._removeInterface(objectPath, interfaceName);
|
|
}
|
|
}
|
|
},
|
|
|
|
_registerInterfaces: function(interfaces) {
|
|
for (let i = 0; i < interfaces.length; i++) {
|
|
let info = Gio.DBusInterfaceInfo.new_for_xml(interfaces[i]);
|
|
this._interfaceInfos[info.name] = info;
|
|
}
|
|
},
|
|
|
|
getProxy: function(objectPath, interfaceName) {
|
|
let object = this._objects[objectPath];
|
|
|
|
if (!object)
|
|
return null;
|
|
|
|
return object[interfaceName];
|
|
},
|
|
|
|
getProxiesForInterface: function(interfaceName) {
|
|
let proxyList = this._interfaces[interfaceName];
|
|
|
|
if (!proxyList)
|
|
return [];
|
|
|
|
return proxyList;
|
|
},
|
|
|
|
getAllProxies: function() {
|
|
let proxies = [];
|
|
|
|
let objectPaths = Object.keys(this._objects);
|
|
for (let i = 0; i < objectPaths.length; i++) {
|
|
let object = this._objects[objectPaths];
|
|
|
|
let interfaceNames = Object.keys(object);
|
|
for (let j = 0; i < interfaceNames.length; i++) {
|
|
let interfaceName = interfaceNames[i];
|
|
if (object[interfaceName])
|
|
proxies.push(object(interfaceName));
|
|
}
|
|
}
|
|
|
|
return proxies;
|
|
}
|
|
});
|
|
Signals.addSignalMethods(ObjectManager.prototype);
|