mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-05 21:15:38 -05:00
core: improve evdev capslock detection, wayland context fixes
This commit is contained in:
13
.github/workflows/release.yml
vendored
13
.github/workflows/release.yml
vendored
@@ -35,6 +35,14 @@ jobs:
|
||||
with:
|
||||
go-version-file: ./core/go.mod
|
||||
|
||||
- name: Format check
|
||||
run: |
|
||||
if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then
|
||||
echo "The following files are not formatted:"
|
||||
gofmt -s -l .
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Run tests
|
||||
run: go test -v ./...
|
||||
|
||||
@@ -168,6 +176,11 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Fetch updated tag after version bump
|
||||
run: |
|
||||
git fetch origin --force tag ${{ github.ref_name }}
|
||||
git checkout ${{ github.ref_name }}
|
||||
|
||||
- name: Download core artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
|
||||
@@ -8,6 +8,7 @@ require (
|
||||
github.com/charmbracelet/bubbletea v1.3.10
|
||||
github.com/charmbracelet/lipgloss v1.1.0
|
||||
github.com/charmbracelet/log v0.4.2
|
||||
github.com/fsnotify/fsnotify v1.9.0
|
||||
github.com/godbus/dbus/v5 v5.1.0
|
||||
github.com/holoplot/go-evdev v0.0.0-20250804134636-ab1d56a1fe83
|
||||
github.com/spf13/cobra v1.10.1
|
||||
|
||||
@@ -50,6 +50,8 @@ github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||
github.com/go-git/gcfg/v2 v2.0.2 h1:MY5SIIfTGGEMhdA7d7JePuVVxtKL7Hp+ApGDJAJ7dpo=
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
func NewManager(display *wlclient.Display) (*Manager, error) {
|
||||
m := &Manager{
|
||||
display: display,
|
||||
ctx: display.Context(),
|
||||
outputs: make(map[uint32]*outputState),
|
||||
cmdq: make(chan cmd, 128),
|
||||
outputSetupReq: make(chan uint32, 16),
|
||||
@@ -86,7 +87,6 @@ func (m *Manager) waylandActor() {
|
||||
|
||||
func (m *Manager) setupRegistry() error {
|
||||
log.Info("DWL: starting registry setup")
|
||||
ctx := m.display.Context()
|
||||
|
||||
registry, err := m.display.GetRegistry()
|
||||
if err != nil {
|
||||
@@ -102,7 +102,7 @@ func (m *Manager) setupRegistry() error {
|
||||
switch e.Interface {
|
||||
case dwl_ipc.ZdwlIpcManagerV2InterfaceName:
|
||||
log.Infof("DWL: found %s", dwl_ipc.ZdwlIpcManagerV2InterfaceName)
|
||||
manager := dwl_ipc.NewZdwlIpcManagerV2(ctx)
|
||||
manager := dwl_ipc.NewZdwlIpcManagerV2(m.ctx)
|
||||
version := e.Version
|
||||
if version > 1 {
|
||||
version = 1
|
||||
@@ -128,7 +128,7 @@ func (m *Manager) setupRegistry() error {
|
||||
}
|
||||
case "wl_output":
|
||||
log.Debugf("DWL: found wl_output (name=%d)", e.Name)
|
||||
output := wlclient.NewOutput(ctx)
|
||||
output := wlclient.NewOutput(m.ctx)
|
||||
|
||||
outState := &outputState{
|
||||
registryName: e.Name,
|
||||
|
||||
@@ -36,6 +36,7 @@ type cmd struct {
|
||||
|
||||
type Manager struct {
|
||||
display *wlclient.Display
|
||||
ctx *wlclient.Context
|
||||
registry *wlclient.Registry
|
||||
manager interface{}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ func TestHandleRequest(t *testing.T) {
|
||||
mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe()
|
||||
|
||||
m := &Manager{
|
||||
device: mockDevice,
|
||||
devices: []EvdevDevice{mockDevice},
|
||||
state: State{Available: true, CapsLock: true},
|
||||
subscribers: make(map[string]chan State),
|
||||
closeChan: make(chan struct{}),
|
||||
@@ -77,7 +77,7 @@ func TestHandleRequest(t *testing.T) {
|
||||
mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe()
|
||||
|
||||
m := &Manager{
|
||||
device: mockDevice,
|
||||
devices: []EvdevDevice{mockDevice},
|
||||
state: State{Available: true, CapsLock: false},
|
||||
subscribers: make(map[string]chan State),
|
||||
closeChan: make(chan struct{}),
|
||||
@@ -107,7 +107,7 @@ func TestHandleGetState(t *testing.T) {
|
||||
mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe()
|
||||
|
||||
m := &Manager{
|
||||
device: mockDevice,
|
||||
devices: []EvdevDevice{mockDevice},
|
||||
state: State{Available: true, CapsLock: false},
|
||||
subscribers: make(map[string]chan State),
|
||||
closeChan: make(chan struct{}),
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
evdev "github.com/holoplot/go-evdev"
|
||||
)
|
||||
|
||||
@@ -29,31 +30,57 @@ type EvdevDevice interface {
|
||||
}
|
||||
|
||||
type Manager struct {
|
||||
device EvdevDevice
|
||||
state State
|
||||
stateMutex sync.RWMutex
|
||||
subscribers map[string]chan State
|
||||
subMutex sync.RWMutex
|
||||
closeChan chan struct{}
|
||||
closeOnce sync.Once
|
||||
devices []EvdevDevice
|
||||
devicesMutex sync.RWMutex
|
||||
monitoredPaths map[string]bool
|
||||
state State
|
||||
stateMutex sync.RWMutex
|
||||
subscribers map[string]chan State
|
||||
subMutex sync.RWMutex
|
||||
closeChan chan struct{}
|
||||
closeOnce sync.Once
|
||||
watcher *fsnotify.Watcher
|
||||
}
|
||||
|
||||
func NewManager() (*Manager, error) {
|
||||
device, err := findKeyboard()
|
||||
devices, err := findKeyboards()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find keyboard: %w", err)
|
||||
return nil, fmt.Errorf("failed to find keyboards: %w", err)
|
||||
}
|
||||
|
||||
initialCapsLock := readInitialCapsLockState(device)
|
||||
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{
|
||||
device: device,
|
||||
state: State{Available: true, CapsLock: initialCapsLock},
|
||||
subscribers: make(map[string]chan State),
|
||||
closeChan: make(chan struct{}),
|
||||
devices: devices,
|
||||
monitoredPaths: monitoredPaths,
|
||||
state: State{Available: true, CapsLock: initialCapsLock},
|
||||
subscribers: make(map[string]chan State),
|
||||
closeChan: make(chan struct{}),
|
||||
watcher: watcher,
|
||||
}
|
||||
|
||||
go m.monitorCapsLock()
|
||||
for i, device := range devices {
|
||||
go m.monitorDevice(device, i)
|
||||
}
|
||||
|
||||
if watcher != nil {
|
||||
go m.watchForNewKeyboards()
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
@@ -68,7 +95,7 @@ func readInitialCapsLockState(device EvdevDevice) bool {
|
||||
return ledStates[ledCapslockKey]
|
||||
}
|
||||
|
||||
func findKeyboard() (EvdevDevice, error) {
|
||||
func findKeyboards() ([]EvdevDevice, error) {
|
||||
pattern := "/dev/input/event*"
|
||||
matches, err := filepath.Glob(pattern)
|
||||
if err != nil {
|
||||
@@ -79,22 +106,28 @@ func findKeyboard() (EvdevDevice, error) {
|
||||
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) {
|
||||
deviceName, _ := device.Name()
|
||||
log.Debugf("Found keyboard: %s at %s", deviceName, path)
|
||||
return device, nil
|
||||
if !isKeyboard(device) {
|
||||
device.Close()
|
||||
continue
|
||||
}
|
||||
|
||||
device.Close()
|
||||
deviceName, _ := device.Name()
|
||||
log.Debugf("Found keyboard: %s at %s", deviceName, path)
|
||||
keyboards = append(keyboards, device)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("no keyboard device found")
|
||||
if len(keyboards) == 0 {
|
||||
return nil, fmt.Errorf("no keyboard device found")
|
||||
}
|
||||
|
||||
return keyboards, nil
|
||||
}
|
||||
|
||||
func isKeyboard(device EvdevDevice) bool {
|
||||
@@ -117,7 +150,85 @@ func isKeyboard(device EvdevDevice) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) monitorCapsLock() {
|
||||
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)
|
||||
@@ -131,11 +242,12 @@ func (m *Manager) monitorCapsLock() {
|
||||
default:
|
||||
}
|
||||
|
||||
event, err := m.device.ReadOne()
|
||||
event, err := device.ReadOne()
|
||||
if err != nil {
|
||||
if !isClosedError(err) {
|
||||
log.Warnf("Failed to read evdev event: %v", err)
|
||||
if isClosedError(err) {
|
||||
return
|
||||
}
|
||||
log.Warnf("Failed to read evdev event: %v", err)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
@@ -145,7 +257,11 @@ func (m *Manager) monitorCapsLock() {
|
||||
}
|
||||
|
||||
if event.Type == evKeyType && event.Code == keyCapslockKey && event.Value == keyStateOn {
|
||||
m.toggleCapsLock()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
m.readAndUpdateCapsLockState(deviceIndex)
|
||||
} else if event.Type == evLedType && event.Code == ledCapslockKey {
|
||||
capsLockState := event.Value == keyStateOn
|
||||
m.updateCapsLockStateDirect(capsLockState)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -166,13 +282,37 @@ func isClosedError(err error) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) toggleCapsLock() {
|
||||
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
|
||||
}
|
||||
|
||||
capsLockState := ledStates[ledCapslockKey]
|
||||
m.updateCapsLockStateDirect(capsLockState)
|
||||
}
|
||||
|
||||
func (m *Manager) updateCapsLockStateDirect(capsLockState bool) {
|
||||
m.stateMutex.Lock()
|
||||
m.state.CapsLock = !m.state.CapsLock
|
||||
if m.state.CapsLock == capsLockState {
|
||||
m.stateMutex.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
m.state.CapsLock = capsLockState
|
||||
newState := m.state
|
||||
m.stateMutex.Unlock()
|
||||
|
||||
log.Debugf("Caps lock toggled: %v", newState.CapsLock)
|
||||
log.Debugf("Caps lock state: %v", newState.CapsLock)
|
||||
m.notifySubscribers(newState)
|
||||
}
|
||||
|
||||
@@ -195,10 +335,13 @@ func (m *Manager) Unsubscribe(id string) {
|
||||
m.subMutex.Lock()
|
||||
defer m.subMutex.Unlock()
|
||||
|
||||
if ch, ok := m.subscribers[id]; ok {
|
||||
close(ch)
|
||||
delete(m.subscribers, id)
|
||||
ch, ok := m.subscribers[id]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
close(ch)
|
||||
delete(m.subscribers, id)
|
||||
}
|
||||
|
||||
func (m *Manager) notifySubscribers(state State) {
|
||||
@@ -217,11 +360,20 @@ func (m *Manager) Close() {
|
||||
m.closeOnce.Do(func() {
|
||||
close(m.closeChan)
|
||||
|
||||
if m.device != nil {
|
||||
if err := m.device.Close(); err != nil && !isClosedError(err) {
|
||||
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.subMutex.Lock()
|
||||
for id, ch := range m.subscribers {
|
||||
|
||||
@@ -16,7 +16,7 @@ func TestManager_Creation(t *testing.T) {
|
||||
mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe()
|
||||
|
||||
m := &Manager{
|
||||
device: mockDevice,
|
||||
devices: []EvdevDevice{mockDevice},
|
||||
state: State{Available: true, CapsLock: false},
|
||||
subscribers: make(map[string]chan State),
|
||||
closeChan: make(chan struct{}),
|
||||
@@ -32,7 +32,7 @@ func TestManager_Creation(t *testing.T) {
|
||||
mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe()
|
||||
|
||||
m := &Manager{
|
||||
device: mockDevice,
|
||||
devices: []EvdevDevice{mockDevice},
|
||||
state: State{Available: true, CapsLock: true},
|
||||
subscribers: make(map[string]chan State),
|
||||
closeChan: make(chan struct{}),
|
||||
@@ -49,10 +49,11 @@ func TestManager_GetState(t *testing.T) {
|
||||
mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe()
|
||||
|
||||
m := &Manager{
|
||||
device: mockDevice,
|
||||
state: State{Available: true, CapsLock: false},
|
||||
subscribers: make(map[string]chan State),
|
||||
closeChan: make(chan struct{}),
|
||||
devices: []EvdevDevice{mockDevice},
|
||||
monitoredPaths: make(map[string]bool),
|
||||
state: State{Available: true, CapsLock: false},
|
||||
subscribers: make(map[string]chan State),
|
||||
closeChan: make(chan struct{}),
|
||||
}
|
||||
|
||||
state := m.GetState()
|
||||
@@ -65,10 +66,11 @@ func TestManager_Subscribe(t *testing.T) {
|
||||
mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe()
|
||||
|
||||
m := &Manager{
|
||||
device: mockDevice,
|
||||
state: State{Available: true, CapsLock: false},
|
||||
subscribers: make(map[string]chan State),
|
||||
closeChan: make(chan struct{}),
|
||||
devices: []EvdevDevice{mockDevice},
|
||||
monitoredPaths: make(map[string]bool),
|
||||
state: State{Available: true, CapsLock: false},
|
||||
subscribers: make(map[string]chan State),
|
||||
closeChan: make(chan struct{}),
|
||||
}
|
||||
|
||||
ch := m.Subscribe("test-client")
|
||||
@@ -81,10 +83,11 @@ func TestManager_Unsubscribe(t *testing.T) {
|
||||
mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe()
|
||||
|
||||
m := &Manager{
|
||||
device: mockDevice,
|
||||
state: State{Available: true, CapsLock: false},
|
||||
subscribers: make(map[string]chan State),
|
||||
closeChan: make(chan struct{}),
|
||||
devices: []EvdevDevice{mockDevice},
|
||||
monitoredPaths: make(map[string]bool),
|
||||
state: State{Available: true, CapsLock: false},
|
||||
subscribers: make(map[string]chan State),
|
||||
closeChan: make(chan struct{}),
|
||||
}
|
||||
|
||||
ch := m.Subscribe("test-client")
|
||||
@@ -101,28 +104,35 @@ func TestManager_Unsubscribe(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestManager_ToggleCapsLock(t *testing.T) {
|
||||
func TestManager_UpdateCapsLock(t *testing.T) {
|
||||
mockDevice := mocks.NewMockEvdevDevice(t)
|
||||
mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe()
|
||||
|
||||
m := &Manager{
|
||||
device: mockDevice,
|
||||
state: State{Available: true, CapsLock: false},
|
||||
subscribers: make(map[string]chan State),
|
||||
closeChan: make(chan struct{}),
|
||||
devices: []EvdevDevice{mockDevice},
|
||||
monitoredPaths: make(map[string]bool),
|
||||
state: State{Available: true, CapsLock: false},
|
||||
subscribers: make(map[string]chan State),
|
||||
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.toggleCapsLock()
|
||||
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.toggleCapsLock()
|
||||
m.readAndUpdateCapsLockState(0)
|
||||
}()
|
||||
|
||||
newState = <-ch
|
||||
@@ -135,10 +145,11 @@ func TestManager_Close(t *testing.T) {
|
||||
mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe()
|
||||
|
||||
m := &Manager{
|
||||
device: mockDevice,
|
||||
state: State{Available: true, CapsLock: false},
|
||||
subscribers: make(map[string]chan State),
|
||||
closeChan: make(chan struct{}),
|
||||
devices: []EvdevDevice{mockDevice},
|
||||
monitoredPaths: make(map[string]bool),
|
||||
state: State{Available: true, CapsLock: false},
|
||||
subscribers: make(map[string]chan State),
|
||||
closeChan: make(chan struct{}),
|
||||
}
|
||||
|
||||
ch1 := m.Subscribe("client1")
|
||||
@@ -197,8 +208,8 @@ func TestIsKeyboard_ErrorHandling(t *testing.T) {
|
||||
assert.False(t, result)
|
||||
}
|
||||
|
||||
func TestManager_MonitorCapsLock(t *testing.T) {
|
||||
t.Run("caps lock key press toggles state", func(t *testing.T) {
|
||||
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{
|
||||
@@ -207,12 +218,15 @@ func TestManager_MonitorCapsLock(t *testing.T) {
|
||||
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{
|
||||
device: mockDevice,
|
||||
devices: []EvdevDevice{mockDevice},
|
||||
state: State{Available: true, CapsLock: false},
|
||||
subscribers: make(map[string]chan State),
|
||||
closeChan: make(chan struct{}),
|
||||
@@ -220,7 +234,7 @@ func TestManager_MonitorCapsLock(t *testing.T) {
|
||||
|
||||
ch := m.Subscribe("test")
|
||||
|
||||
go m.monitorCapsLock()
|
||||
go m.monitorDevice(mockDevice, 0)
|
||||
|
||||
state := <-ch
|
||||
assert.True(t, state.CapsLock)
|
||||
@@ -255,10 +269,11 @@ func TestNotifySubscribers(t *testing.T) {
|
||||
mockDevice.EXPECT().Close().Return(nil).Maybe()
|
||||
|
||||
m := &Manager{
|
||||
device: mockDevice,
|
||||
state: State{Available: true, CapsLock: false},
|
||||
subscribers: make(map[string]chan State),
|
||||
closeChan: make(chan struct{}),
|
||||
devices: []EvdevDevice{mockDevice},
|
||||
monitoredPaths: make(map[string]bool),
|
||||
state: State{Available: true, CapsLock: false},
|
||||
subscribers: make(map[string]chan State),
|
||||
closeChan: make(chan struct{}),
|
||||
}
|
||||
|
||||
ch1 := m.Subscribe("client1")
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
func NewManager(display *wlclient.Display) (*Manager, error) {
|
||||
m := &Manager{
|
||||
display: display,
|
||||
ctx: display.Context(),
|
||||
outputs: make(map[uint32]*wlclient.Output),
|
||||
outputNames: make(map[uint32]string),
|
||||
groups: make(map[uint32]*workspaceGroupState),
|
||||
@@ -62,7 +63,6 @@ func (m *Manager) waylandActor() {
|
||||
|
||||
func (m *Manager) setupRegistry() error {
|
||||
log.Info("ExtWorkspace: starting registry setup")
|
||||
ctx := m.display.Context()
|
||||
|
||||
registry, err := m.display.GetRegistry()
|
||||
if err != nil {
|
||||
@@ -72,7 +72,7 @@ func (m *Manager) setupRegistry() error {
|
||||
|
||||
registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) {
|
||||
if e.Interface == "wl_output" {
|
||||
output := wlclient.NewOutput(ctx)
|
||||
output := wlclient.NewOutput(m.ctx)
|
||||
if err := registry.Bind(e.Name, e.Interface, 4, output); err == nil {
|
||||
outputID := output.ID()
|
||||
|
||||
@@ -88,7 +88,7 @@ func (m *Manager) setupRegistry() error {
|
||||
|
||||
if e.Interface == ext_workspace.ExtWorkspaceManagerV1InterfaceName {
|
||||
log.Infof("ExtWorkspace: found %s", ext_workspace.ExtWorkspaceManagerV1InterfaceName)
|
||||
manager := ext_workspace.NewExtWorkspaceManagerV1(ctx)
|
||||
manager := ext_workspace.NewExtWorkspaceManagerV1(m.ctx)
|
||||
version := e.Version
|
||||
if version > 1 {
|
||||
version = 1
|
||||
|
||||
@@ -33,6 +33,7 @@ type cmd struct {
|
||||
|
||||
type Manager struct {
|
||||
display *wlclient.Display
|
||||
ctx *wlclient.Context
|
||||
registry *wlclient.Registry
|
||||
manager *ext_workspace.ExtWorkspaceManagerV1
|
||||
|
||||
|
||||
@@ -1259,6 +1259,12 @@ func Start(printDocs bool) error {
|
||||
fatalErrChan <- fmt.Errorf("WlrOutput fatal error: %w", err)
|
||||
}()
|
||||
}
|
||||
if wlContext != nil {
|
||||
go func() {
|
||||
err := <-wlContext.FatalError()
|
||||
fatalErrChan <- fmt.Errorf("Wayland context fatal error: %w", err)
|
||||
}()
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := InitializeBrightnessManager(); err != nil {
|
||||
|
||||
@@ -25,6 +25,7 @@ func NewManager(display *wlclient.Display, config Config) (*Manager, error) {
|
||||
m := &Manager{
|
||||
config: config,
|
||||
display: display,
|
||||
ctx: display.Context(),
|
||||
outputs: make(map[uint32]*outputState),
|
||||
cmdq: make(chan cmd, 128),
|
||||
stopChan: make(chan struct{}),
|
||||
@@ -148,7 +149,6 @@ func (m *Manager) setupDBusMonitor() error {
|
||||
|
||||
func (m *Manager) setupRegistry() error {
|
||||
log.Info("setupRegistry: starting registry setup")
|
||||
ctx := m.display.Context()
|
||||
|
||||
registry, err := m.display.GetRegistry()
|
||||
if err != nil {
|
||||
@@ -165,7 +165,7 @@ func (m *Manager) setupRegistry() error {
|
||||
switch e.Interface {
|
||||
case wlr_gamma_control.ZwlrGammaControlManagerV1InterfaceName:
|
||||
log.Infof("setupRegistry: found %s", wlr_gamma_control.ZwlrGammaControlManagerV1InterfaceName)
|
||||
manager := wlr_gamma_control.NewZwlrGammaControlManagerV1(ctx)
|
||||
manager := wlr_gamma_control.NewZwlrGammaControlManagerV1(m.ctx)
|
||||
version := e.Version
|
||||
if version > 1 {
|
||||
version = 1
|
||||
@@ -178,7 +178,7 @@ func (m *Manager) setupRegistry() error {
|
||||
}
|
||||
case "wl_output":
|
||||
log.Debugf("Global event: found wl_output (name=%d)", e.Name)
|
||||
output := wlclient.NewOutput(ctx)
|
||||
output := wlclient.NewOutput(m.ctx)
|
||||
version := e.Version
|
||||
if version > 4 {
|
||||
version = 4
|
||||
|
||||
@@ -44,6 +44,7 @@ type Manager struct {
|
||||
stateMutex sync.RWMutex
|
||||
|
||||
display *wlclient.Display
|
||||
ctx *wlclient.Context
|
||||
registry *wlclient.Registry
|
||||
gammaControl interface{}
|
||||
availableOutputs []*wlclient.Output
|
||||
|
||||
@@ -10,11 +10,12 @@ import (
|
||||
)
|
||||
|
||||
type SharedContext struct {
|
||||
display *wlclient.Display
|
||||
stopChan chan struct{}
|
||||
wg sync.WaitGroup
|
||||
mu sync.Mutex
|
||||
started bool
|
||||
display *wlclient.Display
|
||||
stopChan chan struct{}
|
||||
fatalError chan error
|
||||
wg sync.WaitGroup
|
||||
mu sync.Mutex
|
||||
started bool
|
||||
}
|
||||
|
||||
func New() (*SharedContext, error) {
|
||||
@@ -24,9 +25,10 @@ func New() (*SharedContext, error) {
|
||||
}
|
||||
|
||||
sc := &SharedContext{
|
||||
display: display,
|
||||
stopChan: make(chan struct{}),
|
||||
started: false,
|
||||
display: display,
|
||||
stopChan: make(chan struct{}),
|
||||
fatalError: make(chan error, 1),
|
||||
started: false,
|
||||
}
|
||||
|
||||
return sc, nil
|
||||
@@ -49,8 +51,22 @@ func (sc *SharedContext) Display() *wlclient.Display {
|
||||
return sc.display
|
||||
}
|
||||
|
||||
func (sc *SharedContext) FatalError() <-chan error {
|
||||
return sc.fatalError
|
||||
}
|
||||
|
||||
func (sc *SharedContext) eventDispatcher() {
|
||||
defer sc.wg.Done()
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err := fmt.Errorf("FATAL: Wayland event dispatcher panic: %v", r)
|
||||
log.Error(err)
|
||||
select {
|
||||
case sc.fatalError <- err:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}()
|
||||
ctx := sc.display.Context()
|
||||
|
||||
for {
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
func NewManager(display *wlclient.Display) (*Manager, error) {
|
||||
m := &Manager{
|
||||
display: display,
|
||||
ctx: display.Context(),
|
||||
heads: make(map[uint32]*headState),
|
||||
modes: make(map[uint32]*modeState),
|
||||
cmdq: make(chan cmd, 128),
|
||||
@@ -85,7 +86,6 @@ func (m *Manager) waylandActor() {
|
||||
|
||||
func (m *Manager) setupRegistry() error {
|
||||
log.Info("WlrOutput: starting registry setup")
|
||||
ctx := m.display.Context()
|
||||
|
||||
registry, err := m.display.GetRegistry()
|
||||
if err != nil {
|
||||
@@ -96,7 +96,7 @@ func (m *Manager) setupRegistry() error {
|
||||
registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) {
|
||||
if e.Interface == wlr_output_management.ZwlrOutputManagerV1InterfaceName {
|
||||
log.Infof("WlrOutput: found %s", wlr_output_management.ZwlrOutputManagerV1InterfaceName)
|
||||
manager := wlr_output_management.NewZwlrOutputManagerV1(ctx)
|
||||
manager := wlr_output_management.NewZwlrOutputManagerV1(m.ctx)
|
||||
version := e.Version
|
||||
if version > 4 {
|
||||
version = 4
|
||||
|
||||
@@ -45,6 +45,7 @@ type cmd struct {
|
||||
|
||||
type Manager struct {
|
||||
display *wlclient.Display
|
||||
ctx *wlclient.Context
|
||||
registry *wlclient.Registry
|
||||
manager *wlr_output_management.ZwlrOutputManagerV1
|
||||
|
||||
|
||||
@@ -193,12 +193,9 @@ DankModal {
|
||||
let targetX = parentBounds.x + (parentBounds.width - width) / 2
|
||||
let targetY = parentBounds.y + (parentBounds.height - height) / 2
|
||||
|
||||
const minX = margin
|
||||
const maxX = screenW - width - margin
|
||||
const minY = SettingsData.dankBarPosition === SettingsData.Position.Top ? barExclusionZone + margin : margin
|
||||
const maxY = SettingsData.dankBarPosition === SettingsData.Position.Bottom ? screenH - height - barExclusionZone - margin : screenH - height - margin
|
||||
|
||||
targetX = Math.max(minX, Math.min(maxX, targetX))
|
||||
targetY = Math.max(minY, Math.min(maxY, targetY))
|
||||
|
||||
return Qt.point(targetX, targetY)
|
||||
|
||||
Reference in New Issue
Block a user