Files

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