diff --git a/internal/device/streamdeck.go b/internal/device/streamdeck.go index 841add6..be7c545 100644 --- a/internal/device/streamdeck.go +++ b/internal/device/streamdeck.go @@ -3,6 +3,7 @@ package device import ( "bytes" "encoding/binary" + "errors" "fmt" "image" "image/jpeg" @@ -10,6 +11,7 @@ import ( "runtime" "strings" "sync" + "time" "github.com/sstallion/go-hid" "golang.org/x/image/draw" @@ -194,8 +196,13 @@ func (sd *StreamDeck) ClearKey(keyIndex int) error { // Returns (nil, nil) on timeout — callers should check context and retry. func (sd *StreamDeck) ReadButtons() ([]bool, error) { data := make([]byte, readReportSize) - n, err := sd.dev.ReadWithTimeout(data, 250) + // ReadWithTimeout takes a time.Duration: a bare 250 would be 250ns, which + // truncates to a 0 ms (non-blocking) hid_read_timeout and busy-spins the loop. + n, err := sd.dev.ReadWithTimeout(data, 250*time.Millisecond) if err != nil { + if errors.Is(err, hid.ErrTimeout) { + return nil, nil + } // Linux hidraw returns errors (not (0,nil)) for non-fatal conditions: // timeout waiting for data, or EINTR (signal interrupted). // macOS IOHIDManager usually returns (0, nil) on timeout, but may also diff --git a/mac-support.md b/mac-support.md index dde443a..eaaf9d5 100644 --- a/mac-support.md +++ b/mac-support.md @@ -96,3 +96,4 @@ The biggest architectural difference is privileged commands: | Log location | `journalctl --user -u streamdeck-go` | `~/Library/Logs/streamdeck-go.log` | | Config path | `~/.config/streamdeck-go/` (XDG) | `~/.config/streamdeck-go/` (XDG — works fine on macOS for CLI tools) | | Sleep/wake | Handled by reconnect loop | Handled by reconnect loop (same code) | +| HID read timeout | `hid_read_timeout` → `poll()`; 0 ms = non-blocking | `hid_read_timeout` → `pthread_cond_timedwait`; 0 ms = non-blocking. Same fix applies: pass `250*time.Millisecond` |