115 lines
4.0 KiB
Go
115 lines
4.0 KiB
Go
package xorg
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"sync/atomic"
|
|
|
|
"git.stormux.org/storm/cthulhu/internal/input"
|
|
)
|
|
|
|
type CommandListener struct {
|
|
secureMutex sync.Mutex
|
|
secure atomic.Bool
|
|
ocrApplied atomic.Bool
|
|
ocrPending atomic.Bool
|
|
browse atomic.Bool
|
|
browseApplied atomic.Bool
|
|
nativeButtonFocus atomic.Bool
|
|
nativeButtonApplied atomic.Bool
|
|
grabActive atomic.Bool
|
|
commandsEnabled atomic.Bool
|
|
commandsApplied atomic.Bool
|
|
learnMode atomic.Bool
|
|
learnEcho atomic.Bool
|
|
uiSession atomic.Uint64
|
|
uiSequence atomic.Uint64
|
|
uiClose atomic.Uint64
|
|
uiEcho atomic.Bool
|
|
uiUpdates chan uiUpdate
|
|
focusChanges chan FocusState
|
|
modifiers []string
|
|
layout input.KeyboardLayout
|
|
bindingOverrides []input.BindingOverride
|
|
tentacleBindings []input.BindingOverride
|
|
registrationMutex sync.RWMutex
|
|
appliedTentacles []input.BindingOverride
|
|
bindingUpdates chan bindingUpdate
|
|
// Bounded FIFO; overflow fails the listener rather than blocking key releases.
|
|
commands chan input.CommandEvent
|
|
lockStates chan input.LockState
|
|
failures chan error
|
|
done chan struct{}
|
|
cancel context.CancelFunc
|
|
mutex sync.Mutex
|
|
}
|
|
|
|
type FocusState struct {
|
|
Known bool
|
|
XTerm bool
|
|
}
|
|
|
|
func (listener *CommandListener) publishFocusState(state FocusState) {
|
|
select {
|
|
case listener.focusChanges <- state:
|
|
default:
|
|
select {
|
|
case <-listener.focusChanges:
|
|
default:
|
|
}
|
|
listener.focusChanges <- state
|
|
}
|
|
}
|
|
|
|
func NewCommands(modifiers []string, layout input.KeyboardLayout) *CommandListener {
|
|
return &CommandListener{
|
|
modifiers: append([]string(nil), modifiers...),
|
|
bindingUpdates: make(chan bindingUpdate),
|
|
uiUpdates: make(chan uiUpdate),
|
|
layout: layout,
|
|
commands: make(chan input.CommandEvent, 16),
|
|
lockStates: make(chan input.LockState, 16),
|
|
failures: make(chan error, 1),
|
|
focusChanges: make(chan FocusState, 1),
|
|
}
|
|
}
|
|
|
|
// SetBrowseNavigation publishes policy; the X11 owner changes grabs between
|
|
// complete chords, preserving every key release during a mode transition.
|
|
func (listener *CommandListener) SetBrowseNavigation(enabled bool) { listener.browse.Store(enabled) }
|
|
|
|
// SetNativeButtonFocus leaves Return to a standard button with real DOM focus.
|
|
// Virtual browse targets elsewhere still use Cthulhu's document activation.
|
|
func (listener *CommandListener) SetNativeButtonFocus(focused bool) {
|
|
listener.nativeButtonFocus.Store(focused)
|
|
}
|
|
|
|
func (listener *CommandListener) SetCommandGrabs(enabled bool) {
|
|
listener.commandsEnabled.Store(enabled)
|
|
}
|
|
|
|
// BrowseNavigationEnabled reports whether requested browse grabs have reached X11.
|
|
func (listener *CommandListener) BrowseNavigationEnabled() bool {
|
|
return !listener.ocrApplied.Load() && !listener.secure.Load() && listener.browse.Load() && listener.browseApplied.Load() && listener.commandsApplied.Load()
|
|
}
|
|
|
|
func (listener *CommandListener) CommandGrabsEnabled() bool {
|
|
return (listener.commandsEnabled.Load() && !listener.secure.Load()) && listener.commandsApplied.Load()
|
|
}
|
|
|
|
// CommandGrabActive reports whether the listener currently owns an explicit
|
|
// keyboard grab for a possible command chord. This becomes true on the first
|
|
// grabbed press, before the complete chord can be resolved and dispatched.
|
|
func (listener *CommandListener) CommandGrabActive() bool { return listener.grabActive.Load() }
|
|
|
|
func (listener *CommandListener) LearnModeActive() bool { return listener.learnMode.Load() }
|
|
func (listener *CommandListener) LearnModeEchoActive() bool { return listener.learnEcho.Load() }
|
|
|
|
func (listener *CommandListener) Commands() <-chan input.CommandEvent { return listener.commands }
|
|
func (listener *CommandListener) LockStates() <-chan input.LockState { return listener.lockStates }
|
|
|
|
func (listener *CommandListener) Failures() <-chan error { return listener.failures }
|
|
func (listener *CommandListener) FocusChanges() <-chan FocusState {
|
|
return listener.focusChanges
|
|
}
|