Files

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()
}
}