168 lines
6.0 KiB
Go
168 lines
6.0 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package input
|
|
|
|
import (
|
|
"errors"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
// RemoteRepeatReleaseDelay is the initial bound for holding a release
|
|
// while waiting for its classic X11 autorepeat press. It is a tunable
|
|
// latency limit for a private capture owner, not a measured hardware value.
|
|
RemoteRepeatReleaseDelay = 25 * time.Millisecond
|
|
RemoteRepeatReleaseDelayMax = 250 * time.Millisecond
|
|
remoteRepeatMinKeycode = 8
|
|
)
|
|
|
|
var ErrRemoteRepeatDelay = errors.New("remote repeat release delay is invalid")
|
|
|
|
// RemoteRepeatTransition is one normalized source transition. Repeat is set
|
|
// only on a press for a key that is already owned; no synthetic release is
|
|
// emitted between repeats.
|
|
type RemoteRepeatTransition struct {
|
|
Keycode uint8
|
|
Pressed bool
|
|
Repeat bool
|
|
}
|
|
|
|
// RemoteRepeatNormalizer converts native key observations into the stream
|
|
// required by Remote's outgoing encoder: one fresh down, repeated downs and
|
|
// one final up. Classic X11 autorepeat reports release/press pairs with the
|
|
// same server timestamp; a nonzero-timestamp release is therefore held until
|
|
// the next observation or its deadline. Detectable autorepeat already reports
|
|
// repeated downs and passes through unchanged.
|
|
//
|
|
// One instance belongs to one capture lifetime and one serial native owner.
|
|
// It owns only keys whose downs it observed: releases for keys held before
|
|
// capture are suppressed and never reported as Remote-owned. Admission stops
|
|
// on an invalid keycode or more than RemoteHeldKeyLimit held keys; Retire still
|
|
// returns releases for every owned key. The normalizer performs no I/O, grants
|
|
// no consent and does not establish physical-source provenance.
|
|
type RemoteRepeatNormalizer struct {
|
|
delay time.Duration
|
|
held [256]bool
|
|
order []uint8
|
|
pending bool
|
|
pendingCode uint8
|
|
pendingStamp uint32
|
|
pendingDeadline time.Time
|
|
stopped bool
|
|
retired bool
|
|
}
|
|
|
|
func NewRemoteRepeatNormalizer(delay time.Duration) (*RemoteRepeatNormalizer, error) {
|
|
if delay <= 0 || delay > RemoteRepeatReleaseDelayMax {
|
|
return nil, ErrRemoteRepeatDelay
|
|
}
|
|
return &RemoteRepeatNormalizer{delay: delay, order: make([]uint8, 0, RemoteHeldKeyLimit)}, nil
|
|
}
|
|
|
|
// Observe admits one native transition. A successful call can return zero,
|
|
// one or two transitions; a returned error stops admission permanently while
|
|
// retaining ownership for Retire. The caller must not drop returned work.
|
|
func (normalizer *RemoteRepeatNormalizer) Observe(event Event, now time.Time) ([]RemoteRepeatTransition, error) {
|
|
if normalizer == nil || normalizer.stopped {
|
|
return nil, ErrRemoteKeyState
|
|
}
|
|
if event.Keycode < remoteRepeatMinKeycode {
|
|
normalizer.stopped = true
|
|
return nil, ErrRemoteKeyUnavailable
|
|
}
|
|
var out []RemoteRepeatTransition
|
|
if normalizer.pending {
|
|
if event.Pressed && event.Keycode == normalizer.pendingCode &&
|
|
event.Timestamp == normalizer.pendingStamp {
|
|
normalizer.pending = false
|
|
return []RemoteRepeatTransition{{Keycode: event.Keycode, Pressed: true, Repeat: true}}, nil
|
|
}
|
|
out = append(out, normalizer.releasePending())
|
|
}
|
|
code := event.Keycode
|
|
if event.Pressed {
|
|
if normalizer.held[code] {
|
|
return append(out, RemoteRepeatTransition{Keycode: code, Pressed: true, Repeat: true}), nil
|
|
}
|
|
if len(normalizer.order) >= RemoteHeldKeyLimit {
|
|
normalizer.stopped = true
|
|
return out, ErrRemoteKeyLimit
|
|
}
|
|
normalizer.held[code] = true
|
|
normalizer.order = append(normalizer.order, code)
|
|
return append(out, RemoteRepeatTransition{Keycode: code, Pressed: true}), nil
|
|
}
|
|
if !normalizer.held[code] {
|
|
return out, nil
|
|
}
|
|
if event.Timestamp == 0 {
|
|
// Unknown timestamps cannot prove a classic pair; release now.
|
|
normalizer.forget(code)
|
|
return append(out, RemoteRepeatTransition{Keycode: code}), nil
|
|
}
|
|
normalizer.pending = true
|
|
normalizer.pendingCode = code
|
|
normalizer.pendingStamp = event.Timestamp
|
|
normalizer.pendingDeadline = now.Add(normalizer.delay)
|
|
return out, nil
|
|
}
|
|
|
|
// Deadline reports when a held release must be flushed. The owner should arm
|
|
// a timer for it rather than poll.
|
|
func (normalizer *RemoteRepeatNormalizer) Deadline() (time.Time, bool) {
|
|
if normalizer == nil || normalizer.retired || !normalizer.pending {
|
|
return time.Time{}, false
|
|
}
|
|
return normalizer.pendingDeadline, true
|
|
}
|
|
|
|
// Flush emits a held release once its deadline has been reached. Pairing is
|
|
// decided by event content, not by timer accuracy: a late flush only adds
|
|
// latency, and a press arriving after a flush is a fresh down.
|
|
func (normalizer *RemoteRepeatNormalizer) Flush(now time.Time) []RemoteRepeatTransition {
|
|
if normalizer == nil || normalizer.retired || !normalizer.pending ||
|
|
now.Before(normalizer.pendingDeadline) {
|
|
return nil
|
|
}
|
|
return []RemoteRepeatTransition{normalizer.releasePending()}
|
|
}
|
|
|
|
// Retire stops the normalizer and transfers releases for every owned key in
|
|
// reverse down order, including a held pending release. It returns the plan
|
|
// once; repeated retirement returns nothing.
|
|
func (normalizer *RemoteRepeatNormalizer) Retire() []RemoteRepeatTransition {
|
|
if normalizer == nil || normalizer.retired {
|
|
return nil
|
|
}
|
|
normalizer.stopped = true
|
|
normalizer.retired = true
|
|
normalizer.pending = false
|
|
out := make([]RemoteRepeatTransition, 0, len(normalizer.order))
|
|
for index := len(normalizer.order) - 1; index >= 0; index-- {
|
|
out = append(out, RemoteRepeatTransition{Keycode: normalizer.order[index]})
|
|
}
|
|
normalizer.held = [256]bool{}
|
|
normalizer.order = normalizer.order[:0]
|
|
return out
|
|
}
|
|
|
|
func (normalizer *RemoteRepeatNormalizer) releasePending() RemoteRepeatTransition {
|
|
code := normalizer.pendingCode
|
|
normalizer.pending = false
|
|
normalizer.forget(code)
|
|
return RemoteRepeatTransition{Keycode: code}
|
|
}
|
|
|
|
func (normalizer *RemoteRepeatNormalizer) forget(code uint8) {
|
|
normalizer.held[code] = false
|
|
for index, held := range normalizer.order {
|
|
if held == code {
|
|
copy(normalizer.order[index:], normalizer.order[index+1:])
|
|
normalizer.order = normalizer.order[:len(normalizer.order)-1]
|
|
return
|
|
}
|
|
}
|
|
}
|