summaryrefslogtreecommitdiff
path: root/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev
diff options
context:
space:
mode:
authorNippy <nippy@rp1.hu>2026-05-09 13:33:09 +0200
committerNippy <nippy@rp1.hu>2026-05-09 13:33:09 +0200
commita2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5 (patch)
tree1a8769217f84bfe9d6216fafaadaf8cdd876d457 /raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev
parent34b9c34f982b2596cfa9e368a2d7f74e9a17477c (diff)
downloadRaveOS-PKGBUILD-a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5.tar.gz
RaveOS-PKGBUILD-a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5.zip
raveos update
Diffstat (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev')
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/handlers.go20
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/handlers_test.go130
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/manager.go413
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/manager_test.go344
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/models.go6
5 files changed, 0 insertions, 913 deletions
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/handlers.go
deleted file mode 100644
index 54a7bca..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/handlers.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package evdev
-
-import (
- "net"
-
- "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
-)
-
-func HandleRequest(conn net.Conn, req models.Request, m *Manager) {
- switch req.Method {
- case "evdev.getState":
- handleGetState(conn, req, m)
- default:
- models.RespondError(conn, req.ID, "unknown method: "+req.Method)
- }
-}
-
-func handleGetState(conn net.Conn, req models.Request, m *Manager) {
- models.Respond(conn, req.ID, m.GetState())
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/handlers_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/handlers_test.go
deleted file mode 100644
index f9bd21d..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/handlers_test.go
+++ /dev/null
@@ -1,130 +0,0 @@
-package evdev
-
-import (
- "bytes"
- "encoding/json"
- "errors"
- "net"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-
- mocks "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/evdev"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
-)
-
-type mockNetConn struct {
- net.Conn
- readBuf *bytes.Buffer
- writeBuf *bytes.Buffer
- closed bool
-}
-
-func newMockNetConn() *mockNetConn {
- return &mockNetConn{
- readBuf: &bytes.Buffer{},
- writeBuf: &bytes.Buffer{},
- }
-}
-
-func (m *mockNetConn) Read(b []byte) (n int, err error) {
- return m.readBuf.Read(b)
-}
-
-func (m *mockNetConn) Write(b []byte) (n int, err error) {
- return m.writeBuf.Write(b)
-}
-
-func (m *mockNetConn) Close() error {
- m.closed = true
- return nil
-}
-
-func TestHandleRequest(t *testing.T) {
- t.Run("getState request", func(t *testing.T) {
- mockDevice := mocks.NewMockEvdevDevice(t)
- mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe()
-
- m := &Manager{
- devices: []EvdevDevice{mockDevice},
- state: State{Available: true, CapsLock: true},
- closeChan: make(chan struct{}),
- }
-
- conn := newMockNetConn()
- req := models.Request{
- ID: 123,
- Method: "evdev.getState",
- Params: map[string]any{},
- }
-
- HandleRequest(conn, req, m)
-
- var resp models.Response[State]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
- require.NoError(t, err)
-
- assert.Equal(t, 123, resp.ID)
- assert.NotNil(t, resp.Result)
- assert.True(t, resp.Result.Available)
- assert.True(t, resp.Result.CapsLock)
- })
-
- t.Run("unknown method", func(t *testing.T) {
- mockDevice := mocks.NewMockEvdevDevice(t)
- mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe()
-
- m := &Manager{
- devices: []EvdevDevice{mockDevice},
- state: State{Available: true, CapsLock: false},
- closeChan: make(chan struct{}),
- }
-
- conn := newMockNetConn()
- req := models.Request{
- ID: 456,
- Method: "evdev.unknownMethod",
- Params: map[string]any{},
- }
-
- HandleRequest(conn, req, m)
-
- var resp models.Response[any]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
- require.NoError(t, err)
-
- assert.Equal(t, 456, resp.ID)
- assert.NotEmpty(t, resp.Error)
- assert.Contains(t, resp.Error, "unknown method")
- })
-}
-
-func TestHandleGetState(t *testing.T) {
- mockDevice := mocks.NewMockEvdevDevice(t)
- mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe()
-
- m := &Manager{
- devices: []EvdevDevice{mockDevice},
- state: State{Available: true, CapsLock: false},
- closeChan: make(chan struct{}),
- }
-
- conn := newMockNetConn()
- req := models.Request{
- ID: 789,
- Method: "evdev.getState",
- Params: map[string]any{},
- }
-
- handleGetState(conn, req, m)
-
- var resp models.Response[State]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
- require.NoError(t, err)
-
- assert.Equal(t, 789, resp.ID)
- assert.NotNil(t, resp.Result)
- assert.True(t, resp.Result.Available)
- assert.False(t, resp.Result.CapsLock)
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/manager.go
deleted file mode 100644
index b578481..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/manager.go
+++ /dev/null
@@ -1,413 +0,0 @@
-package evdev
-
-import (
- "fmt"
- "os"
- "path/filepath"
- "strings"
- "sync"
- "time"
-
- "github.com/AvengeMedia/DankMaterialShell/core/internal/log"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
- "github.com/fsnotify/fsnotify"
- evdev "github.com/holoplot/go-evdev"
-)
-
-const (
- evKeyType = 0x01
- evLedType = 0x11
- keyCapslockKey = 58
- ledCapslockKey = 1
- keyStateOn = 1
-)
-
-type EvdevDevice interface {
- Name() (string, error)
- Path() string
- Close() error
- ReadOne() (*evdev.InputEvent, error)
- State(t evdev.EvType) (evdev.StateMap, error)
-}
-
-type Manager struct {
- devices []EvdevDevice
- devicesMutex sync.RWMutex
- monitoredPaths map[string]bool
- state State
- stateMutex sync.RWMutex
- subscribers syncmap.Map[string, chan State]
- closeChan chan struct{}
- closeOnce sync.Once
- watcher *fsnotify.Watcher
-}
-
-func NewManager() (*Manager, error) {
- devices, err := findKeyboards()
- if err != nil {
- return nil, fmt.Errorf("failed to find keyboards: %w", err)
- }
-
- initialCapsLock := readInitialCapsLockState(devices[0])
-
- watcher, err := fsnotify.NewWatcher()
- if err != nil {
- log.Warnf("Failed to create fsnotify watcher, hotplug detection disabled: %v", err)
- watcher = nil
- } else if err := watcher.Add("/dev/input"); err != nil {
- log.Warnf("Failed to watch /dev/input, hotplug detection disabled: %v", err)
- watcher.Close()
- watcher = nil
- }
-
- monitoredPaths := make(map[string]bool)
- for _, device := range devices {
- monitoredPaths[device.Path()] = true
- }
-
- m := &Manager{
- devices: devices,
- monitoredPaths: monitoredPaths,
- state: State{Available: true, CapsLock: initialCapsLock},
-
- closeChan: make(chan struct{}),
- watcher: watcher,
- }
-
- for i, device := range devices {
- go m.monitorDevice(device, i)
- }
-
- if watcher != nil {
- go m.watchForNewKeyboards()
- }
-
- 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
- }
-
- return ledStates[ledCapslockKey]
-}
-
-func findKeyboards() ([]EvdevDevice, error) {
- pattern := "/dev/input/event*"
- matches, err := filepath.Glob(pattern)
- if err != nil {
- return nil, fmt.Errorf("failed to glob input devices: %w", err)
- }
-
- if len(matches) == 0 {
- return nil, fmt.Errorf("no input devices found")
- }
-
- var keyboards []EvdevDevice
- for _, path := range matches {
- device, err := evdev.Open(path)
- if err != nil {
- continue
- }
-
- if !isKeyboard(device) {
- device.Close()
- continue
- }
-
- deviceName, _ := device.Name()
- log.Debugf("Found keyboard: %s at %s", deviceName, path)
- keyboards = append(keyboards, device)
- }
-
- if len(keyboards) == 0 {
- return nil, fmt.Errorf("no keyboard device found")
- }
-
- return keyboards, nil
-}
-
-func isKeyboard(device EvdevDevice) bool {
- deviceName, err := device.Name()
- if err != nil {
- return false
- }
-
- name := strings.ToLower(deviceName)
-
- switch {
- case strings.Contains(name, "keyboard"):
- return true
- case strings.Contains(name, "kbd"):
- return true
- case strings.Contains(name, "input") && strings.Contains(name, "key"):
- return true
- }
-
- keyStates, err := device.State(evKeyType)
- if err != nil {
- return false
- }
-
- hasKeyA := len(keyStates) > 30
- hasKeyZ := len(keyStates) > 44
- hasEnter := len(keyStates) > 28
-
- return hasKeyA && hasKeyZ && hasEnter && len(keyStates) > 100
-}
-
-func (m *Manager) watchForNewKeyboards() {
- defer func() {
- if r := recover(); r != nil {
- log.Errorf("Panic in keyboard hotplug monitor: %v", r)
- }
- }()
-
- for {
- select {
- case <-m.closeChan:
- return
- case event, ok := <-m.watcher.Events:
- if !ok {
- return
- }
-
- if !strings.HasPrefix(filepath.Base(event.Name), "event") {
- continue
- }
-
- if event.Op&fsnotify.Create == fsnotify.Create {
- time.Sleep(100 * time.Millisecond)
-
- m.devicesMutex.Lock()
- if m.monitoredPaths[event.Name] {
- m.devicesMutex.Unlock()
- continue
- }
-
- device, err := evdev.Open(event.Name)
- if err != nil {
- m.devicesMutex.Unlock()
- continue
- }
-
- if !isKeyboard(device) {
- device.Close()
- m.devicesMutex.Unlock()
- continue
- }
-
- deviceName, _ := device.Name()
- log.Debugf("Hotplugged keyboard: %s at %s", deviceName, event.Name)
-
- m.devices = append(m.devices, device)
- m.monitoredPaths[event.Name] = true
- deviceIndex := len(m.devices) - 1
- m.devicesMutex.Unlock()
-
- go m.monitorDevice(device, deviceIndex)
- } else if event.Op&fsnotify.Remove == fsnotify.Remove {
- m.devicesMutex.Lock()
- if !m.monitoredPaths[event.Name] {
- m.devicesMutex.Unlock()
- continue
- }
-
- delete(m.monitoredPaths, event.Name)
- for i, device := range m.devices {
- if device != nil && device.Path() == event.Name {
- log.Debugf("Keyboard removed: %s", event.Name)
- device.Close()
- m.devices[i] = nil
- break
- }
- }
- m.devicesMutex.Unlock()
- }
-
- case err, ok := <-m.watcher.Errors:
- if !ok {
- return
- }
- log.Warnf("Keyboard hotplug watcher error: %v", err)
- }
- }
-}
-
-func (m *Manager) monitorDevice(device EvdevDevice, deviceIndex int) {
- defer func() {
- if r := recover(); r != nil {
- log.Errorf("Panic in evdev monitor: %v", r)
- }
- }()
-
- for {
- select {
- case <-m.closeChan:
- return
- default:
- }
-
- event, err := device.ReadOne()
- if err != nil {
- if isClosedError(err) {
- return
- }
- log.Warnf("Failed to read evdev event: %v", err)
- time.Sleep(100 * time.Millisecond)
- continue
- }
-
- if event == nil {
- continue
- }
-
- if event.Type == evKeyType && event.Code == keyCapslockKey && event.Value == keyStateOn {
- time.Sleep(50 * time.Millisecond)
- m.readAndUpdateCapsLockState(deviceIndex)
- } else if event.Type == evLedType && event.Code == ledCapslockKey {
- capsLockState := event.Value == keyStateOn
- m.updateCapsLockStateDirect(capsLockState)
- }
- }
-}
-
-func isClosedError(err error) bool {
- if err == nil {
- return false
- }
-
- errStr := err.Error()
- switch {
- case strings.Contains(errStr, "closed"):
- return true
- case strings.Contains(errStr, "bad file descriptor"):
- return true
- default:
- return false
- }
-}
-
-func (m *Manager) readAndUpdateCapsLockState(deviceIndex int) {
- m.devicesMutex.RLock()
- if deviceIndex >= len(m.devices) {
- m.devicesMutex.RUnlock()
- return
- }
- device := m.devices[deviceIndex]
- m.devicesMutex.RUnlock()
-
- ledStates, err := device.State(evLedType)
- if err != nil {
- log.Warnf("Failed to read LED state: %v", err)
- 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)
-}
-
-func (m *Manager) updateCapsLockStateDirect(capsLockState bool) {
- m.stateMutex.Lock()
- if m.state.CapsLock == capsLockState {
- m.stateMutex.Unlock()
- return
- }
-
- m.state.CapsLock = capsLockState
- newState := m.state
- m.stateMutex.Unlock()
-
- log.Debugf("Caps lock state: %v", newState.CapsLock)
- m.notifySubscribers(newState)
-}
-
-func (m *Manager) GetState() State {
- m.stateMutex.RLock()
- defer m.stateMutex.RUnlock()
- return m.state
-}
-
-func (m *Manager) Subscribe(id string) chan State {
- ch := make(chan State, 16)
- m.subscribers.Store(id, ch)
- return ch
-}
-
-func (m *Manager) Unsubscribe(id string) {
- if val, ok := m.subscribers.LoadAndDelete(id); ok {
- close(val)
- }
-}
-
-func (m *Manager) notifySubscribers(state State) {
- m.subscribers.Range(func(key string, ch chan State) bool {
- select {
- case ch <- state:
- default:
- }
- return true
- })
-}
-
-func (m *Manager) Close() {
- m.closeOnce.Do(func() {
- close(m.closeChan)
-
- if m.watcher != nil {
- m.watcher.Close()
- }
-
- m.devicesMutex.Lock()
- for _, device := range m.devices {
- if device == nil {
- continue
- }
- if err := device.Close(); err != nil && !isClosedError(err) {
- log.Warnf("Error closing evdev device: %v", err)
- }
- }
- m.devicesMutex.Unlock()
-
- m.subscribers.Range(func(key string, ch chan State) bool {
- close(ch)
- m.subscribers.Delete(key)
- return true
- })
- })
-}
-
-func InitializeManager() (*Manager, error) {
- if os.Getuid() != 0 && !hasInputGroupAccess() {
- return nil, fmt.Errorf("insufficient permissions to access input devices")
- }
-
- return NewManager()
-}
-
-func hasInputGroupAccess() bool {
- pattern := "/dev/input/event*"
- matches, err := filepath.Glob(pattern)
- if err != nil || len(matches) == 0 {
- return false
- }
-
- testFile, err := os.Open(matches[0])
- if err != nil {
- return false
- }
- testFile.Close()
- return true
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/manager_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/manager_test.go
deleted file mode 100644
index 667c078..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/manager_test.go
+++ /dev/null
@@ -1,344 +0,0 @@
-package evdev
-
-import (
- "errors"
- "testing"
-
- evdev "github.com/holoplot/go-evdev"
- "github.com/stretchr/testify/assert"
-
- mocks "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/evdev"
-)
-
-func TestManager_Creation(t *testing.T) {
- t.Run("manager created successfully with caps lock off", func(t *testing.T) {
- mockDevice := mocks.NewMockEvdevDevice(t)
- mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe()
-
- m := &Manager{
- devices: []EvdevDevice{mockDevice},
- state: State{Available: true, CapsLock: false},
- closeChan: make(chan struct{}),
- }
-
- assert.NotNil(t, m)
- assert.True(t, m.state.Available)
- assert.False(t, m.state.CapsLock)
- })
-
- t.Run("manager created successfully with caps lock on", func(t *testing.T) {
- mockDevice := mocks.NewMockEvdevDevice(t)
- mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe()
-
- m := &Manager{
- devices: []EvdevDevice{mockDevice},
- state: State{Available: true, CapsLock: true},
- closeChan: make(chan struct{}),
- }
-
- assert.NotNil(t, m)
- assert.True(t, m.state.Available)
- assert.True(t, m.state.CapsLock)
- })
-}
-
-func TestManager_GetState(t *testing.T) {
- mockDevice := mocks.NewMockEvdevDevice(t)
- mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe()
-
- m := &Manager{
- devices: []EvdevDevice{mockDevice},
- monitoredPaths: make(map[string]bool),
- state: State{Available: true, CapsLock: false},
- closeChan: make(chan struct{}),
- }
-
- state := m.GetState()
- assert.True(t, state.Available)
- assert.False(t, state.CapsLock)
-}
-
-func TestManager_Subscribe(t *testing.T) {
- mockDevice := mocks.NewMockEvdevDevice(t)
- mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe()
-
- m := &Manager{
- devices: []EvdevDevice{mockDevice},
- monitoredPaths: make(map[string]bool),
- state: State{Available: true, CapsLock: false},
- closeChan: make(chan struct{}),
- }
-
- ch := m.Subscribe("test-client")
- assert.NotNil(t, ch)
- count := 0
- m.subscribers.Range(func(key string, ch chan State) bool {
- count++
- return true
- })
- assert.Equal(t, 1, count)
-}
-
-func TestManager_Unsubscribe(t *testing.T) {
- mockDevice := mocks.NewMockEvdevDevice(t)
- mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe()
-
- m := &Manager{
- devices: []EvdevDevice{mockDevice},
- monitoredPaths: make(map[string]bool),
- state: State{Available: true, CapsLock: false},
- closeChan: make(chan struct{}),
- }
-
- ch := m.Subscribe("test-client")
- count := 0
- m.subscribers.Range(func(key string, ch chan State) bool {
- count++
- return true
- })
- assert.Equal(t, 1, count)
-
- m.Unsubscribe("test-client")
- count = 0
- m.subscribers.Range(func(key string, ch chan State) bool {
- count++
- return true
- })
- assert.Equal(t, 0, count)
-
- select {
- case _, ok := <-ch:
- assert.False(t, ok, "channel should be closed")
- default:
- t.Error("channel should be closed")
- }
-}
-
-func TestManager_UpdateCapsLock(t *testing.T) {
- mockDevice := mocks.NewMockEvdevDevice(t)
- mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe()
-
- m := &Manager{
- devices: []EvdevDevice{mockDevice},
- monitoredPaths: make(map[string]bool),
- state: State{Available: true, CapsLock: false},
- closeChan: make(chan struct{}),
- }
-
- ch := m.Subscribe("test-client")
-
- ledStateOn := evdev.StateMap{ledCapslockKey: true}
- mockDevice.EXPECT().State(evdev.EvType(evLedType)).Return(ledStateOn, nil).Once()
-
- go func() {
- m.readAndUpdateCapsLockState(0)
- }()
-
- newState := <-ch
- assert.True(t, newState.CapsLock)
-
- ledStateOff := evdev.StateMap{ledCapslockKey: false}
- mockDevice.EXPECT().State(evdev.EvType(evLedType)).Return(ledStateOff, nil).Once()
-
- go func() {
- m.readAndUpdateCapsLockState(0)
- }()
-
- newState = <-ch
- assert.False(t, newState.CapsLock)
-}
-
-func TestManager_Close(t *testing.T) {
- mockDevice := mocks.NewMockEvdevDevice(t)
- mockDevice.EXPECT().Close().Return(nil).Once()
- mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe()
-
- m := &Manager{
- devices: []EvdevDevice{mockDevice},
- monitoredPaths: make(map[string]bool),
- state: State{Available: true, CapsLock: false},
- closeChan: make(chan struct{}),
- }
-
- ch1 := m.Subscribe("client1")
- ch2 := m.Subscribe("client2")
-
- m.Close()
-
- select {
- case _, ok := <-ch1:
- assert.False(t, ok, "channel 1 should be closed")
- default:
- t.Error("channel 1 should be closed")
- }
-
- select {
- case _, ok := <-ch2:
- assert.False(t, ok, "channel 2 should be closed")
- default:
- t.Error("channel 2 should be closed")
- }
-
- count := 0
- m.subscribers.Range(func(key string, ch chan State) bool {
- count++
- return true
- })
- assert.Equal(t, 0, count)
-
- m.Close()
-}
-
-func TestIsKeyboard(t *testing.T) {
- tests := []struct {
- name string
- devName string
- expected bool
- }{
- {"keyboard in name", "AT Translated Set 2 keyboard", true},
- {"kbd in name", "USB kbd", true},
- {"input and key", "input key device", true},
- {"random device", "Mouse", false},
- {"empty name", "", false},
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- mockDevice := mocks.NewMockEvdevDevice(t)
- mockDevice.EXPECT().Name().Return(tt.devName, nil).Once()
-
- if !tt.expected {
- mockDevice.EXPECT().State(evdev.EvType(evKeyType)).Return(evdev.StateMap{}, nil).Maybe()
- }
-
- result := isKeyboard(mockDevice)
- assert.Equal(t, tt.expected, result)
- })
- }
-}
-
-func TestIsKeyboard_ErrorHandling(t *testing.T) {
- mockDevice := mocks.NewMockEvdevDevice(t)
- mockDevice.EXPECT().Name().Return("", errors.New("device error")).Once()
-
- result := isKeyboard(mockDevice)
- assert.False(t, result)
-}
-
-func TestManager_MonitorDevice(t *testing.T) {
- t.Run("caps lock key press updates state", func(t *testing.T) {
- mockDevice := mocks.NewMockEvdevDevice(t)
-
- capsLockEvent := &evdev.InputEvent{
- Type: evKeyType,
- Code: keyCapslockKey,
- Value: keyStateOn,
- }
-
- ledState := evdev.StateMap{ledCapslockKey: true}
-
- mockDevice.EXPECT().ReadOne().Return(capsLockEvent, nil).Once()
- mockDevice.EXPECT().State(evdev.EvType(evLedType)).Return(ledState, nil).Once()
- mockDevice.EXPECT().ReadOne().Return(nil, errors.New("stop")).Maybe()
- mockDevice.EXPECT().Close().Return(nil).Maybe()
-
- m := &Manager{
- devices: []EvdevDevice{mockDevice},
- state: State{Available: true, CapsLock: false},
- closeChan: make(chan struct{}),
- }
-
- ch := m.Subscribe("test")
-
- go m.monitorDevice(mockDevice, 0)
-
- state := <-ch
- assert.True(t, state.CapsLock)
-
- m.Close()
- })
-}
-
-func TestIsClosedError(t *testing.T) {
- tests := []struct {
- name string
- err error
- expected bool
- }{
- {"nil error", nil, false},
- {"closed error", errors.New("device closed"), true},
- {"bad file descriptor", errors.New("bad file descriptor"), true},
- {"other error", errors.New("some other error"), false},
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- result := isClosedError(tt.err)
- assert.Equal(t, tt.expected, result)
- })
- }
-}
-
-func TestNotifySubscribers(t *testing.T) {
- mockDevice := mocks.NewMockEvdevDevice(t)
- mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe()
- mockDevice.EXPECT().Close().Return(nil).Maybe()
-
- m := &Manager{
- devices: []EvdevDevice{mockDevice},
- monitoredPaths: make(map[string]bool),
- state: State{Available: true, CapsLock: false},
- closeChan: make(chan struct{}),
- }
-
- ch1 := m.Subscribe("client1")
- ch2 := m.Subscribe("client2")
-
- newState := State{Available: true, CapsLock: true}
- go m.notifySubscribers(newState)
-
- state1 := <-ch1
- state2 := <-ch2
-
- assert.Equal(t, newState, state1)
- assert.Equal(t, newState, state2)
-
- m.Close()
-}
-
-func TestReadInitialCapsLockState(t *testing.T) {
- t.Run("caps lock is on", func(t *testing.T) {
- mockDevice := mocks.NewMockEvdevDevice(t)
- ledState := evdev.StateMap{
- ledCapslockKey: true,
- }
- mockDevice.EXPECT().State(evdev.EvType(evLedType)).Return(ledState, nil).Once()
-
- result := readInitialCapsLockState(mockDevice)
- assert.True(t, result)
- })
-
- t.Run("caps lock is off", func(t *testing.T) {
- mockDevice := mocks.NewMockEvdevDevice(t)
- ledState := evdev.StateMap{
- ledCapslockKey: false,
- }
- mockDevice.EXPECT().State(evdev.EvType(evLedType)).Return(ledState, nil).Once()
-
- result := readInitialCapsLockState(mockDevice)
- assert.False(t, result)
- })
-
- t.Run("error reading LED state", func(t *testing.T) {
- mockDevice := mocks.NewMockEvdevDevice(t)
- mockDevice.EXPECT().State(evdev.EvType(evLedType)).Return(nil, errors.New("read error")).Once()
-
- result := readInitialCapsLockState(mockDevice)
- assert.False(t, result)
- })
-}
-
-func TestHasInputGroupAccess(t *testing.T) {
- result := hasInputGroupAccess()
- t.Logf("hasInputGroupAccess: %v", result)
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/models.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/models.go
deleted file mode 100644
index df8ec9e..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/models.go
+++ /dev/null
@@ -1,6 +0,0 @@
-package evdev
-
-type State struct {
- Available bool `json:"available"`
- CapsLock bool `json:"capsLock"`
-}