From 923c0563f0389c4011c99b93a3edcfb09f5f3309 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Fri, 23 Oct 2026 19:50:40 +0200 Subject: companion: sing when the Sony headphones connect She now reacts to the headphones connecting with a short singing pose: eyes shut, blindfold gone, mouth wide open, headphones on, a few musical notes. The frames are new klein edits in frames.txt (dress/sing from dress/base, leotard/sing from dress/sing), so a post-self-destruct sing has its own art. Detection is the island's rule (StatusPill.qml): by model prefix, since BlueZ gives no class that says "headphones". The device's BlueZ Name is WH-CH720N, which the alias does not affect. The reaction is a one-shot hold in Mood.js (singHold, 4 s), placed after wave like the other event reactions. A 4 s startup grace keeps a shell start with the headphones already on from counting as a connection, since Bluetooth reads empty for about 2 s. --- desktop/companion/Companion.qml | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'desktop/companion/Companion.qml') diff --git a/desktop/companion/Companion.qml b/desktop/companion/Companion.qml index 6eb7370..f8d50e9 100644 --- a/desktop/companion/Companion.qml +++ b/desktop/companion/Companion.qml @@ -12,6 +12,7 @@ import Quickshell import Quickshell.Io import Quickshell.Wayland +import Quickshell.Bluetooth import Quickshell.Hyprland import QtQuick import ".." @@ -105,6 +106,7 @@ PanelWindow { property double pulseAt: 0 property double wakeAt: 0 property double admireAt: 0 + property double singAt: 0 property double lastLineAt: 0 property double lastMonologueAt: 0 property bool monologue: false @@ -210,6 +212,24 @@ PanelWindow { } } + // Sony headphones connecting, by the island's rule (island/StatusPill.qml): + // recognised by their model prefix, since BlueZ gives no class that says + // "headphones". A connection makes her sing for Mood.T.singHold. Bluetooth + // reads empty for about 2 s after launch, so a shell start with them + // already on is not a connection: btReady waits past that. + readonly property bool phonesConnected: (Bluetooth.devices?.values ?? []) + .some(d => d.connected && /^(WH|WF)-|^LinkBuds/.test(d.deviceName)) + property bool btReady: false + // shortcut: a genuine connection in the first 4 s after a shell start is + // missed along with the startup populate; tighten if that bites. + Timer { interval: 4000; running: true; onTriggered: root.btReady = true } + onPhonesConnectedChanged: { + if (!root.phonesConnected || !root.btReady) return; + root.singAt = Date.now(); + root.now = root.singAt; + root.say("headphones"); + } + // A listed wallpaper on an empty workspace: she turns to it and blushes. // Checked after the events above (switching to an empty workspace, // closing its last window) and whenever she stops being away: shown, @@ -265,7 +285,7 @@ PanelWindow { shovedAt: root.shovedAt, recording: root.asst.recording, busy: root.asst.busy, repliedAt: root.repliedAt, clicks: root.clicks, admireAt: root.admireAt, mockAt: root.mockAt, typingAt: root.typingAt, wakeAt: root.wakeAt, - monologue: root.monologue, idle: root.idle, + singAt: root.singAt, monologue: root.monologue, idle: root.idle, }) readonly property string outfit: Mood.outfit(root.destructAt, root.now, Prefs.companionSkirtless * 1000) // Anything above the monologue cuts it short. -- cgit v1.2.3