Merged changes from hunter. Attempt to fix cthulhu announcing modifiers when suspended for xterm.

This commit is contained in:
Storm Dragon committed 2026-10-31 16:07:29 -04:00
1 parent 27867a48cb
commit f2cc47c8a0
24 files changed
+679 -67

No files matched your search

+43 -8
View File
@@ -10,6 +10,7 @@ import (
"time"
"git.stormux.org/storm/cthulhu/internal/atspi"
"git.stormux.org/storm/cthulhu/internal/config"
)
// Fixture recorded from Niri 26.04 on 2026-10-31: a workspace switch emits
@@ -40,10 +41,10 @@ func TestAnnouncementSpeaksPoliteWorkspaceSwitch(t *testing.T) {
speech, adapter, done := announcementFixture(t)
defer done()
adapter.events <- announcementEvent("Workspace 2 Mistworld", 1)
adapter.events <- announcementEvent("Workspace 2 Example", 1)
select {
case request := <-speech.requests:
if request.Text != "Workspace 2 Mistworld" || request.Role != "system" || request.Cause != atspi.AnnouncementEvent ||
if request.Text != "Workspace 2 Example" || request.Role != "system" || request.Cause != atspi.AnnouncementEvent ||
!request.Append || !request.BypassFocusGate {
t.Fatalf("announcement request = %+v", request)
}
@@ -52,14 +53,14 @@ func TestAnnouncementSpeaksPoliteWorkspaceSwitch(t *testing.T) {
}
}
func TestAnnouncementAssertiveInterrupts(t *testing.T) {
func TestAnnouncementAssertiveQueues(t *testing.T) {
speech, adapter, done := announcementFixture(t)
defer done()
adapter.events <- announcementEvent("Config parse error", 2)
select {
case request := <-speech.requests:
if request.Text != "Config parse error" || request.Append {
if request.Text != "Config parse error" || !request.Append {
t.Fatalf("assertive request = %+v", request)
}
case <-time.After(time.Second):
@@ -71,10 +72,10 @@ func TestAnnouncementDedupesTrimmedRepeatButNotNewText(t *testing.T) {
speech, adapter, done := announcementFixture(t)
defer done()
adapter.events <- announcementEvent("Workspace 2 Mistworld", 1)
adapter.events <- announcementEvent("Workspace 2 Example", 1)
// Niri can append a space to force re-emission of identical text.
adapter.events <- announcementEvent("Workspace 2 Mistworld ", 1)
adapter.events <- announcementEvent("Workspace 1 Generic Shit", 1)
adapter.events <- announcementEvent("Workspace 2 Example ", 1)
adapter.events <- announcementEvent("Workspace 1 Example", 1)
var got []string
for range 2 {
@@ -90,7 +91,7 @@ func TestAnnouncementDedupesTrimmedRepeatButNotNewText(t *testing.T) {
t.Fatalf("duplicate announcement spoken: %+v (all: %v)", request, got)
case <-time.After(30 * time.Millisecond):
}
if got[0] != "Workspace 2 Mistworld" || got[1] != "Workspace 1 Generic Shit" {
if got[0] != "Workspace 2 Example" || got[1] != "Workspace 1 Example" {
t.Fatalf("announcements = %v", got)
}
}
@@ -106,3 +107,37 @@ func TestEmptyAnnouncementDoesNotSpeak(t *testing.T) {
case <-time.After(30 * time.Millisecond):
}
}
func TestAnnouncementBrailleAndLockGate(t *testing.T) {
live := &liveSettings{}
live.Store(config.DefaultSettings())
speech := &remoteSpeechQueue{}
display := &flashRecorder{}
gate := newFocusGate(nil)
handler := &accessibilityEventHandler{
state: &eventLoopState{}, focusGate: gate,
speech: gatedSpeech{gate: gate, speech: outputQueue{speech: speech, braille: display, live: live}},
}
event := announcementEvent("Workspace 2 Example", 1)
handler.presentAnnouncement(event)
if len(display.messages) != 1 || display.messages[0] != event.Payload {
t.Fatalf("Braille flashes = %v", display.messages)
}
gate.secure.Store(true)
handler.handle(announcementEvent("Locked workspace", 1))
if len(display.messages) != 1 {
t.Fatal("locked announcement flashed")
}
}
func TestIdenticalAnnouncementFromAnotherProviderIsPreserved(t *testing.T) {
speech := &remoteSpeechQueue{}
handler := &accessibilityEventHandler{state: &eventLoopState{}, speech: speech}
event := announcementEvent("Example message", 1)
handler.presentAnnouncement(event)
event.Source.BusName = ":1.21"
handler.presentAnnouncement(event)
if len(speech.requests) != 2 {
t.Fatalf("requests = %d, want 2", len(speech.requests))
}
}