154 lines
4.5 KiB
TypeScript
154 lines
4.5 KiB
TypeScript
import Gio from "gi://Gio";
|
|
import GObject, { register } from "astal/gobject";
|
|
import { getDbusXml } from "@/utils";
|
|
import GLib from "gi://GLib";
|
|
|
|
let dbusConnectionResolve: ((connection: any) => void) | null = null;
|
|
export const Connection: Promise<any> = new Promise<any>(
|
|
(resolve) => (dbusConnectionResolve = resolve),
|
|
);
|
|
|
|
const ownerId = Gio.bus_own_name(
|
|
Gio.BusType.SESSION,
|
|
"dev.ezri.VoidShell",
|
|
Gio.BusNameOwnerFlags.NONE,
|
|
(connection) => {
|
|
dbusConnectionResolve?.(connection);
|
|
},
|
|
() => {},
|
|
() => {},
|
|
);
|
|
|
|
@register()
|
|
export class DBusObject extends GObject.Object {
|
|
protected dbusObj: Gio.DBusExportedObject | null = null;
|
|
#objectPath: string;
|
|
|
|
constructor(iface: string, objectPath: string) {
|
|
super();
|
|
this.#objectPath = objectPath;
|
|
getDbusXml(iface).then(async (xml) => {
|
|
try {
|
|
this.dbusObj = Gio.DBusExportedObject.wrapJSObject(xml, this.proxify());
|
|
this.dbusObj.export(await Connection, objectPath);
|
|
} catch (e) {
|
|
console.error(`Error exporting to D-Bus: ${e}`);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Creates a proxy of the dbus object that returns object paths of DBusObjects
|
|
* rather than the objects themselves.
|
|
* This proxy should be used with the DBus export, and should not be used in any other context.
|
|
*
|
|
* Due to the way this works, setting DBus object paths is NOT SUPPORTED! Use a method if you really need to do that.
|
|
*/
|
|
private proxify() {
|
|
return new Proxy(this, {
|
|
get(target, property: keyof DBusObject) {
|
|
const marshall_func = `marshall_${String(property)}` as string &
|
|
keyof typeof target;
|
|
if (
|
|
marshall_func in target &&
|
|
typeof target[marshall_func] === "function"
|
|
) {
|
|
const variant: GLib.Variant = target[marshall_func]();
|
|
return variant.deepUnpack();
|
|
}
|
|
if (!(property in target)) {
|
|
return undefined;
|
|
}
|
|
if (target[property] instanceof DBusObject) {
|
|
return target[property].objectPath;
|
|
}
|
|
if (target[property] instanceof Array) {
|
|
return target[property].map((prop) =>
|
|
prop instanceof DBusObject ? prop.objectPath : prop,
|
|
);
|
|
}
|
|
return target[property];
|
|
},
|
|
});
|
|
}
|
|
|
|
private marshallValue(
|
|
property: string & keyof this,
|
|
value: any,
|
|
): GLib.Variant | null {
|
|
const marshall_func_name = `marshall_${property}` as string & keyof this;
|
|
if (
|
|
marshall_func_name in this &&
|
|
typeof this[marshall_func_name] === "function"
|
|
) {
|
|
return this[marshall_func_name]();
|
|
}
|
|
switch (typeof value) {
|
|
case "string":
|
|
return GLib.Variant.new_string(value);
|
|
case "number":
|
|
return GLib.Variant.new_double(value);
|
|
case "bigint":
|
|
return GLib.Variant.new_int64(Number(value));
|
|
case "boolean":
|
|
return GLib.Variant.new_boolean(value);
|
|
case "symbol":
|
|
return null;
|
|
case "object":
|
|
if (value instanceof Array) {
|
|
if (value.every((val) => val instanceof DBusObject)) {
|
|
return new GLib.Variant(
|
|
"ao",
|
|
value.map((val) => val.objectPath),
|
|
);
|
|
}
|
|
return new GLib.Variant(
|
|
"av",
|
|
value.map(this.marshallValue.bind(this)),
|
|
);
|
|
} else {
|
|
console.warn(
|
|
"No marshall function found for object type, cannot implicitly marshall JS objects. Returning null.",
|
|
);
|
|
return null;
|
|
}
|
|
case "undefined":
|
|
return null;
|
|
case "function":
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// private deepMarshallObject(obj: Object): GLib.Variant {
|
|
// if (obj instanceof DBusObject) {
|
|
// return GLib.Variant.new_object_path(obj.objectPath);
|
|
// }
|
|
// if (obj instanceof Array) {
|
|
// return new GLib.Variant(
|
|
// "av",
|
|
// obj.map(this.marshallValue.bind(this)).filter((val) => val !== null),
|
|
// );
|
|
// }
|
|
// const newObj = Object.entries(obj).reduce((obj, [key, value]) => {
|
|
// const toEmit = this.marshallValue(, value);
|
|
// if (toEmit !== null) {
|
|
// obj[key] = toEmit;
|
|
// }
|
|
// return obj;
|
|
// }, {} as any);
|
|
// return new GLib.Variant("a{sv}", newObj);
|
|
// }
|
|
|
|
protected emitDBusProperty<Prop extends keyof this & string>(property: Prop) {
|
|
const toEmit = this.marshallValue(property, this[property]);
|
|
if (!toEmit) {
|
|
return;
|
|
}
|
|
this.dbusObj?.emit_property_changed(property, toEmit);
|
|
}
|
|
|
|
get objectPath(): string {
|
|
return this.#objectPath;
|
|
}
|
|
}
|