diff --git a/core/internal/server/evdev/manager.go b/core/internal/server/evdev/manager.go index 324907237..34496046d 100644 --- a/core/internal/server/evdev/manager.go +++ b/core/internal/server/evdev/manager.go @@ -48,7 +48,7 @@ func NewManager() (*Manager, error) { return nil, fmt.Errorf("failed to find keyboards: %w", err) } - initialCapsLock := readInitialCapsLockState(devices[0]) + initialCapsLock, _ := capsLockFromDevices(devices) watcher, err := fsnotify.NewWatcher() if err != nil { @@ -85,14 +85,21 @@ func NewManager() (*Manager, error) { return m, nil } -func readInitialCapsLockState(device EvdevDevice) bool { - ledStates, err := device.State(evLedType) - if err != nil { - log.Debugf("Could not read LED state: %v", err) - return false +func capsLockFromDevices(devices []EvdevDevice) (bool, bool) { + for _, device := range devices { + if device == nil { + continue + } + + ledStates, err := device.State(evLedType) + if err != nil || len(ledStates) == 0 { + continue + } + + return ledStates[ledCapslockKey], true } - return ledStates[ledCapslockKey] + return false, false } func findKeyboards() ([]EvdevDevice, error) { @@ -297,25 +304,22 @@ func (m *Manager) readAndUpdateCapsLockState(deviceIndex int) { m.devicesMutex.RUnlock() return } - device := m.devices[deviceIndex] + ordered := make([]EvdevDevice, 0, len(m.devices)) + ordered = append(ordered, m.devices[deviceIndex]) + for i, device := range m.devices { + if i == deviceIndex { + continue + } + ordered = append(ordered, device) + } m.devicesMutex.RUnlock() - ledStates, err := device.State(evLedType) - if err != nil { - log.Warnf("Failed to read LED state: %v", err) + capsLockState, ok := capsLockFromDevices(ordered) + if !ok { + log.Debug("No LED-capable device available for caps lock state") return } - if len(ledStates) == 0 { - log.Debug("No LED state available (empty map)") - - // This means the device either: - // - doesn't support LED reporting at all, or - // - the kernel returned an empty state - return - } - - capsLockState := ledStates[ledCapslockKey] m.updateCapsLockStateDirect(capsLockState) } diff --git a/core/internal/server/evdev/manager_test.go b/core/internal/server/evdev/manager_test.go index 667c078ef..d1cf690a1 100644 --- a/core/internal/server/evdev/manager_test.go +++ b/core/internal/server/evdev/manager_test.go @@ -306,7 +306,7 @@ func TestNotifySubscribers(t *testing.T) { m.Close() } -func TestReadInitialCapsLockState(t *testing.T) { +func TestCapsLockFromDevices(t *testing.T) { t.Run("caps lock is on", func(t *testing.T) { mockDevice := mocks.NewMockEvdevDevice(t) ledState := evdev.StateMap{ @@ -314,7 +314,8 @@ func TestReadInitialCapsLockState(t *testing.T) { } mockDevice.EXPECT().State(evdev.EvType(evLedType)).Return(ledState, nil).Once() - result := readInitialCapsLockState(mockDevice) + result, ok := capsLockFromDevices([]EvdevDevice{mockDevice}) + assert.True(t, ok) assert.True(t, result) }) @@ -325,7 +326,8 @@ func TestReadInitialCapsLockState(t *testing.T) { } mockDevice.EXPECT().State(evdev.EvType(evLedType)).Return(ledState, nil).Once() - result := readInitialCapsLockState(mockDevice) + result, ok := capsLockFromDevices([]EvdevDevice{mockDevice}) + assert.True(t, ok) assert.False(t, result) }) @@ -333,9 +335,25 @@ func TestReadInitialCapsLockState(t *testing.T) { mockDevice := mocks.NewMockEvdevDevice(t) mockDevice.EXPECT().State(evdev.EvType(evLedType)).Return(nil, errors.New("read error")).Once() - result := readInitialCapsLockState(mockDevice) + result, ok := capsLockFromDevices([]EvdevDevice{mockDevice}) + assert.False(t, ok) assert.False(t, result) }) + + t.Run("falls back past device without LED state", func(t *testing.T) { + noLedDevice := mocks.NewMockEvdevDevice(t) + noLedDevice.EXPECT().State(evdev.EvType(evLedType)).Return(evdev.StateMap{}, nil).Once() + + ledDevice := mocks.NewMockEvdevDevice(t) + ledState := evdev.StateMap{ + ledCapslockKey: true, + } + ledDevice.EXPECT().State(evdev.EvType(evLedType)).Return(ledState, nil).Once() + + result, ok := capsLockFromDevices([]EvdevDevice{noLedDevice, nil, ledDevice}) + assert.True(t, ok) + assert.True(t, result) + }) } func TestHasInputGroupAccess(t *testing.T) {