More work on tentacles.

This commit is contained in:
Storm Dragon committed 2026-11-03 23:48:31 -05:00
1 parent 573d15d7b4
commit 4cf3335017
29 files changed
+1194 -66

No files matched your search

+26 -7
View File
@@ -6,6 +6,7 @@ package xorg
/*
#cgo pkg-config: xkbcommon
#cgo pkg-config: xi
#include "commands_native.h"
*/
import "C"
@@ -26,6 +27,7 @@ type uiUpdate struct {
ctx context.Context
closeSession uint64
result chan uiReply
localOnly bool
}
func (listener *CommandListener) InternalUISession() uint64 { return listener.uiSession.Load() }
@@ -35,7 +37,14 @@ func (listener *CommandListener) InternalUIEchoActive() bool { return listener.u
// BeginInternalUI acknowledges exclusive capture without changing X input
// focus. EndInternalUI waits for physical releases, and cannot close a newer UI.
func (listener *CommandListener) BeginInternalUI(ctx context.Context) (uint64, error) {
return listener.update_internal_ui(ctx, 0)
return listener.update_internal_ui(ctx, 0, false)
}
// BeginLocalUI admits only device-backed XI2 events and excludes XTEST. This
// is a core API, never a Tentacle-controlled form flag. It fails on unsupported
// servers; ordinary forms keep their existing input path.
func (listener *CommandListener) BeginLocalUI(ctx context.Context) (uint64, error) {
return listener.update_internal_ui(ctx, 0, true)
}
func (listener *CommandListener) EndInternalUI(ctx context.Context, session uint64) error {
if session == 0 {
@@ -50,17 +59,17 @@ func (listener *CommandListener) EndInternalUI(ctx context.Context, session uint
break
}
}
_, err := listener.update_internal_ui(ctx, session)
_, err := listener.update_internal_ui(ctx, session, false)
return err
}
func (listener *CommandListener) update_internal_ui(ctx context.Context, closeSession uint64) (uint64, error) {
func (listener *CommandListener) update_internal_ui(ctx context.Context, closeSession uint64, localOnly bool) (uint64, error) {
listener.mutex.Lock()
done := listener.done
listener.mutex.Unlock()
if done == nil {
return 0, errors.New("command listener is not running")
}
update := uiUpdate{ctx: ctx, closeSession: closeSession, result: make(chan uiReply, 1)}
update := uiUpdate{ctx: ctx, closeSession: closeSession, result: make(chan uiReply, 1), localOnly: localOnly}
select {
case listener.uiUpdates <- update:
case <-ctx.Done():
@@ -76,7 +85,7 @@ func (listener *CommandListener) update_internal_ui(ctx context.Context, closeSe
}
}
func (owner *commandOwner) begin_internal_ui() (uint64, error) {
func (owner *commandOwner) begin_internal_ui_mode(localOnly bool) (uint64, error) {
if !owner.commandGrabsAllowed() || owner.learnMode || owner.ocrReview || owner.uiSession != 0 {
return 0, errors.New("internal UI input unavailable")
}
@@ -86,7 +95,13 @@ func (owner *commandOwner) begin_internal_ui() (uint64, error) {
return 0, errors.New("no focused application")
}
owner.uiWindow = C.read_foreground(owner.device.display).window
if status := C.acquire_internal_keyboard(owner.device); status != 0 {
var status C.int
if localOnly {
status = C.acquire_local_keyboard(owner.device)
} else {
status = C.acquire_internal_keyboard(owner.device)
}
if status != 0 {
return 0, fmt.Errorf("internal keyboard capture failed: %d", int(status))
}
owner.uiSession = owner.listener.uiSequence.Add(1)
@@ -96,6 +111,10 @@ func (owner *commandOwner) begin_internal_ui() (uint64, error) {
return owner.uiSession, nil
}
func (owner *commandOwner) begin_internal_ui() (uint64, error) {
return owner.begin_internal_ui_mode(false)
}
func (owner *commandOwner) dismiss_internal_ui() {
owner.uiSession = 0
owner.listener.uiSession.Store(0)
@@ -136,7 +155,7 @@ func (owner *commandOwner) sync_internal_ui() bool {
owner.dismiss_internal_ui()
}
} else {
result.session, result.err = owner.begin_internal_ui()
result.session, result.err = owner.begin_internal_ui_mode(update.localOnly)
}
}
update.result <- result