115 lines
2.8 KiB
Go
115 lines
2.8 KiB
Go
package xorg
|
|
|
|
/*
|
|
#cgo pkg-config: x11 xtst
|
|
#include "commands_native.h"
|
|
#include <X11/XKBlib.h>
|
|
*/
|
|
import "C"
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"runtime"
|
|
"time"
|
|
|
|
"git.stormux.org/storm/cthulhu/internal/input"
|
|
)
|
|
|
|
const xtermFocusCheckInterval = 100 * time.Millisecond
|
|
|
|
func (listener *CommandListener) Start(ctx context.Context) error {
|
|
if err := ctx.Err(); err != nil {
|
|
return err
|
|
}
|
|
listener.mutex.Lock()
|
|
defer listener.mutex.Unlock()
|
|
if listener.done != nil {
|
|
return errors.New("command listener already started")
|
|
}
|
|
symbols, err := command_modifier_symbols(listener.modifiers)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var useInsert, useKeypad, useCaps C.int
|
|
for _, symbol := range symbols {
|
|
switch symbol {
|
|
case 0xff63:
|
|
useInsert = 1
|
|
case 0xff9e:
|
|
useKeypad = 1
|
|
case 0xffe5:
|
|
useCaps = 1
|
|
}
|
|
}
|
|
workerContext, cancel := context.WithCancel(ctx)
|
|
listener.cancel = cancel
|
|
listener.done = make(chan struct{})
|
|
setup := make(chan error, 1)
|
|
go func() {
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
defer close(listener.done)
|
|
defer listener.grabActive.Store(false)
|
|
defer listener.learnMode.Store(false)
|
|
defer listener.learnEcho.Store(false)
|
|
defer listener.uiSession.Store(0)
|
|
defer listener.uiEcho.Store(false)
|
|
var nativeError C.int
|
|
listener.commandsEnabled.Store(true)
|
|
device := C.open_commands(useInsert, useKeypad, useCaps, &nativeError)
|
|
if device == nil {
|
|
setup <- fmt.Errorf("open X11 command listener: native error %d (a conflicting command-key grab may already exist)", int(nativeError))
|
|
return
|
|
}
|
|
defer C.close_commands(device)
|
|
defer listener.browseApplied.Store(false)
|
|
defer listener.ocrApplied.Store(false)
|
|
defer listener.commandsApplied.Store(false)
|
|
defer listener.bypassApplied.Store(false)
|
|
listener.commandsApplied.Store(true)
|
|
layout := listener.layout
|
|
modifiers := append([]string(nil), listener.modifiers...)
|
|
core := input.CloneBindingOverrides(listener.bindingOverrides)
|
|
tentacles := input.CloneBindingOverrides(listener.tentacleBindings)
|
|
overrides := input.MergeTentacleBindings(core, tentacles)
|
|
if err := configureNativeBindings(device, layout, modifiers, overrides); err != nil {
|
|
setup <- err
|
|
return
|
|
}
|
|
listener.publishTentacleBindings(overrides)
|
|
defer listener.publishTentacleBindings(nil)
|
|
setup <- nil
|
|
listener.runCommandLoop(workerContext, device, layout, modifiers, core, tentacles, symbols)
|
|
}()
|
|
select {
|
|
case err := <-setup:
|
|
if err != nil {
|
|
cancel()
|
|
<-listener.done
|
|
}
|
|
return err
|
|
case <-ctx.Done():
|
|
cancel()
|
|
<-listener.done
|
|
return ctx.Err()
|
|
}
|
|
}
|
|
|
|
func (listener *CommandListener) Stop(ctx context.Context) error {
|
|
listener.mutex.Lock()
|
|
cancel, done := listener.cancel, listener.done
|
|
listener.mutex.Unlock()
|
|
if done == nil {
|
|
return nil
|
|
}
|
|
cancel()
|
|
select {
|
|
case <-done:
|
|
return nil
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
}
|
|
}
|