import GObject, { register, property, signal } from "astal/gobject"; import { VSMonitor } from "./monitor"; import { VSOutput } from "./output"; import { CompositorConnection } from "@services/compositor"; import { EventType, VSOutputEvent } from "@services/compositor/types"; @register({ GTypeName: "OutputService", }) export class OutputService extends GObject.Object { #monitors: VSMonitor[] = []; #outputs: VSOutput[] = []; public get monitors(): readonly VSMonitor[] { return this.#monitors; } public get outputs(): readonly VSOutput[] { return this.#outputs; } @signal(VSMonitor) declare monitorConnected: (monitor: VSMonitor) => void; private onOutputEvent(event: VSOutputEvent) { console.log("processing output change event"); const idx = this.#monitors.findIndex( (monitor) => monitor.name === event.output, ); switch (event.type) { case EventType.CREATE: if (idx === -1) { console.log(`new monitor ${event.output} connected`); const newMonitor = new VSMonitor(event.adapter!); this.#monitors.push(newMonitor); this.monitorConnected(newMonitor); } else { console.warn( `Received create event for monitor ${event.output} we already know about. This is probably a bug!`, ); } break; case EventType.CHANGE: if (idx === -1) { console.warn( `Received change event for monitor ${event.output} we don't know about. This is probably a bug!`, ); } else { this.#monitors[idx].sync(event.adapter!); } break; case EventType.DESTROY: if (idx === -1) { console.warn( `Received destroy event for monitor ${event.output} we don't know about. This is probably a bug!`, ); } else { this.#monitors[idx].destroy(); this.#monitors.splice(idx, 1); } } } constructor() { super(); CompositorConnection.instance .getOutputs() .then((compositorOutputs) => { compositorOutputs.forEach((output) => this.#monitors.push(new VSMonitor(output)), ); }) .catch(console.error) .then(() => CompositorConnection.instance.connect( "output-change", (_, event: VSOutputEvent) => this.onOutputEvent(event), ), ); } }