From f1152d3dbb4c6deefea168d66f15f77b7155a5fe Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Wed, 15 Mar 2023 17:26:13 +0100 Subject: Basic D-Bus API (#866) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: basic D-Bus API Expose muted state and the number of unread message over D-Bus when running on Linux. This is useful for, e.g., displaying notifications on a window manager status bar. Signed-off-by: Kristóf Marussy * docs: create docs directory Move the documentation to a separate directory so that new documentation can be added into one place. We keep the following files still in the repository root by convention: * CHANGELOG.md * CODE_OF_CONDUCT.md * CONTRIBUTING.md * LICENSE.md * README.md * SECURITY.md Signed-off-by: Kristóf Marussy * docs: D-Bus usage example Signed-off-by: Kristóf Marussy * fix: remove unneeded D-Bus signals Only notify clients that the message counts or the mute status has changed if there actually was a change. Signed-off-by: Kristóf Marussy * docs: rewrite sample bar client * docs: better unread --services help * docs: update dbus docs * docs: use ferdium-dbus in dbus bar example * docs: make command argument required in bar example --------- Signed-off-by: Kristóf Marussy Co-authored-by: Victor Bonnelle --- src/lib/DBus.ts | 69 ++++++++++++++++++++++++++++++++++++-- src/lib/dbus/Ferdium.ts | 88 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 src/lib/dbus/Ferdium.ts (limited to 'src/lib') diff --git a/src/lib/DBus.ts b/src/lib/DBus.ts index bbff405c4..530e30c85 100644 --- a/src/lib/DBus.ts +++ b/src/lib/DBus.ts @@ -1,28 +1,92 @@ +import { ipcMain } from 'electron'; +import { comparer } from 'mobx'; + import { MessageBus, sessionBus } from 'dbus-next'; import { isLinux } from '../environment'; import TrayIcon from './Tray'; +import Ferdium, { type UnreadServices } from './dbus/Ferdium'; export default class DBus { - bus: MessageBus | null = null; + private bus: MessageBus | null = null; trayIcon: TrayIcon; + private ferdium: Ferdium | null = null; + + muted = false; + + unreadDirectMessageCount = 0; + + unreadIndirectMessageCount = 0; + + unreadServices: UnreadServices = []; + constructor(trayIcon: TrayIcon) { this.trayIcon = trayIcon; + ipcMain.on('initialAppSettings', (_, appSettings) => { + this.updateSettings(appSettings); + }); + ipcMain.on('updateAppSettings', (_, appSettings) => { + this.updateSettings(appSettings); + }); + ipcMain.on( + 'updateDBusUnread', + ( + _, + unreadDirectMessageCount, + unreadIndirectMessageCount, + unreadServices, + ) => { + this.setUnread( + unreadDirectMessageCount, + unreadIndirectMessageCount, + unreadServices, + ); + }, + ); + } + + private updateSettings(appSettings): void { + const muted = !!appSettings.data.isAppMuted; + if (this.muted !== muted) { + this.muted = muted; + this.ferdium?.emitMutedChanged(); + } } - start() { + private setUnread( + unreadDirectMessageCount: number, + unreadIndirectMessageCount: number, + unreadServices: UnreadServices, + ): void { + if ( + this.unreadDirectMessageCount !== unreadDirectMessageCount || + this.unreadIndirectMessageCount !== unreadIndirectMessageCount || + !comparer.structural(this.unreadServices, unreadServices) + ) { + this.unreadDirectMessageCount = unreadDirectMessageCount; + this.unreadIndirectMessageCount = unreadIndirectMessageCount; + this.unreadServices = unreadServices; + this.ferdium?.emitUnreadChanged(); + } + } + + async start() { if (!isLinux || this.bus) { return; } try { this.bus = sessionBus(); + await this.bus.requestName('org.ferdium.Ferdium', 0); } catch { // Error connecting to the bus. return; } + this.ferdium = new Ferdium(this); + this.bus.export('/org/ferdium', this.ferdium); + // HACK Hook onto the MessageBus to track StatusNotifierWatchers // @ts-expect-error Property '_addMatch' does not exist on type 'MessageBus'. this.bus._addMatch( @@ -56,5 +120,6 @@ export default class DBus { this.bus.disconnect(); this.bus = null; + this.ferdium = null; } } diff --git a/src/lib/dbus/Ferdium.ts b/src/lib/dbus/Ferdium.ts new file mode 100644 index 000000000..b2a9105f4 --- /dev/null +++ b/src/lib/dbus/Ferdium.ts @@ -0,0 +1,88 @@ +import * as dbus from 'dbus-next'; + +import type DBus from '../DBus'; + +export type UnreadServices = [string, number, number][]; + +export default class Ferdium extends dbus.interface.Interface { + constructor(private readonly dbus: DBus) { + super('org.ferdium.Ferdium'); + } + + emitMutedChanged(): void { + Ferdium.emitPropertiesChanged(this, { Muted: this.dbus.muted }, []); + } + + get Muted(): boolean { + return this.dbus.muted; + } + + set Muted(muted: boolean) { + if (this.dbus.muted !== muted) { + this.ToggleMute(); + } + } + + ToggleMute(): void { + this.dbus.trayIcon.mainWindow?.webContents.send('muteApp'); + } + + ToggleWindow(): void { + this.dbus.trayIcon._toggleWindow(); + } + + emitUnreadChanged(): void { + Ferdium.emitPropertiesChanged( + this, + { + UnreadDirectMessageCount: this.dbus.unreadDirectMessageCount, + UnreadIndirectMessageCount: this.dbus.unreadIndirectMessageCount, + UnreadServices: this.dbus.unreadServices, + }, + [], + ); + } + + get UnreadDirectMessageCount(): number { + return this.dbus.unreadDirectMessageCount; + } + + get UnreadIndirectMessageCount(): number { + return this.dbus.unreadIndirectMessageCount; + } + + get UnreadServices(): UnreadServices { + return this.dbus.unreadServices; + } +} + +Ferdium.configureMembers({ + methods: { + ToggleMute: { + inSignature: '', + outSignature: '', + }, + ToggleWindow: { + inSignature: '', + outSignature: '', + }, + }, + properties: { + Muted: { + signature: 'b', + access: dbus.interface.ACCESS_READWRITE, + }, + UnreadDirectMessageCount: { + signature: 'u', + access: dbus.interface.ACCESS_READ, + }, + UnreadIndirectMessageCount: { + signature: 'u', + access: dbus.interface.ACCESS_READ, + }, + UnreadServices: { + signature: 'a(suu)', + access: dbus.interface.ACCESS_READ, + }, + }, +}); -- cgit v1.2.3-70-g09d2