import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; const Signals = imports.signals; Gio._promisify(Gio.DBusProxy.prototype, 'init_async', 'init_finish'); // Specified in the D-Bus specification here: // http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager const ObjectManagerIface = ` `; const ObjectManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ObjectManagerIface); ; export class ObjectManager { constructor(params) { var _a; this._connection = params.connection; this._serviceName = params.name; this._managerPath = params.objectPath; this._cancellable = (_a = params.cancellable) !== null && _a !== void 0 ? _a : null; 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); this._initManagerProxy(); } _completeLoad() { if (this._onLoaded) this._onLoaded(); } async _addInterface(objectPath, interfaceName) { let info = this._interfaceInfos[interfaceName]; if (!info) return; const 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, }); try { await proxy.init_async(GLib.PRIORITY_DEFAULT, this._cancellable); } catch (e) { logError(e, `could not initialize proxy for interface ${interfaceName}`); 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); } _removeInterface(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); delete this._objects[objectPath][interfaceName]; if (Object.keys(this._objects[objectPath]).length === 0) { delete this._objects[objectPath]; this.emit('object-removed', objectPath); } } async _initManagerProxy() { try { await this._managerProxy.init_async(GLib.PRIORITY_DEFAULT, this._cancellable); } catch (e) { logError(e, `could not initialize object manager for object ${this._serviceName}`); this._completeLoad(); return; } this._managerProxy.connectSignal('InterfacesAdded', (_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', (_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._completeLoad(); return; } this._managerProxy.connect('notify::g-name-owner', () => { if (this._managerProxy.g_name_owner) this._onNameAppeared(); else this._onNameVanished(); }); if (this._managerProxy.g_name_owner) this._onNameAppeared().catch(logError); } async _onNameAppeared() { try { const [objects] = await this._managerProxy.GetManagedObjectsAsync(); if (!objects) { this._completeLoad(); return; } const objectPaths = Object.keys(objects); await Promise.allSettled(objectPaths.flatMap(objectPath => { const object = objects[objectPath]; const interfaceNames = Object.getOwnPropertyNames(object); return interfaceNames.map(ifaceName => this._addInterface(objectPath, ifaceName)); })); } catch (error) { logError(error, `could not get remote objects for service ${this._serviceName} path ${this._managerPath}`); } finally { this._completeLoad(); } } _onNameVanished() { let objectPaths = Object.keys(this._objects); for (let i = 0; i < objectPaths.length; i++) { let objectPath = objectPaths[i]; let object = this._objects[objectPath]; let interfaceNames = Object.keys(object); for (let j = 0; j < interfaceNames.length; j++) { let interfaceName = interfaceNames[j]; if (object[interfaceName]) this._removeInterface(objectPath, interfaceName); } } } _registerInterfaces(interfaces) { for (let i = 0; i < interfaces.length; i++) { let info = Gio.DBusInterfaceInfo.new_for_xml(interfaces[i]); this._interfaceInfos[info.name] = info; } } getProxiesForInterface(interfaceName) { let proxyList = this._interfaces[interfaceName]; if (!proxyList) return []; return proxyList; } } Signals.addSignalMethods(ObjectManager.prototype);