marketplace/accessibility

Key Timing Control

v0.1.0

@rebindfree

Ignore accidental double presses and make a held key type once. For tremor and lingering presses.

Gallery

Key Timing Control preview

Readme

When presses linger or bounce, the screen fills with extra letters. Ignore double presses drops a second press of the same key within the time you set, and One character per press makes a held key type once instead of repeating.

Needs Rebind 3.5.0 or newer on Windows, macOS, or Linux. No extra permissions.

Settings

  • Ignore double presses and Within: a second press of the same key sooner than 50 to 1000 ms after the first is ignored. Default 200 ms.
  • One character per press: each press types once, however long you hold it.

Changes apply right away.

Limits

  • Shift, Ctrl, Alt, Cmd/Win, the lock keys, and mouse buttons are never filtered, so shortcuts and clicks work as usual.
  • With One character per press on, each key is sent as a quick tap when you press it, so apps that act on a held key (such as holding a key to move in a game) see a tap instead. Turn it off for those.
  • Live use remains unverified on Windows, macOS, and Linux.

Stop

Stop the package from the editor toolbar, or press Left Ctrl + Left Alt + K to stop all scripts.

Reviews

No reviews yet. Be the first, from the app.

Source

Version 0.1.0 (current)

main.luau

-- Key Timing Control: ignore a second press of the same key that comes too
-- soon, and make a held key type once instead of repeating.

local cfg = UI.Schema({
  debounce = UI.Toggle(true, { label = "Ignore double presses" }),
  debounce_ms = UI.Slider(200, {
    min = 50,
    max = 1000,
    suffix = " ms",
    label = "Within",
  }),
  no_repeat = UI.Toggle(true, { label = "One character per press" }),
})

-- modifiers and lock keys pass through untouched so chords keep working
local PASS = {
  LCtrl = true,
  RCtrl = true,
  LShift = true,
  RShift = true,
  LAlt = true,
  RAlt = true,
  LWin = true,
  RWin = true,
  CapsLock = true,
  NumLock = true,
  ScrollLock = true,
}

local last = {}
-- keys whose press was taken over, so their release is taken over too
local taken = {}

function OnDown(key)
  if PASS[key] or key == "Unknown" or key:sub(1, 5) == "Mouse" then
    return true
  end
  if cfg.debounce then
    local now = os.clock() * 1000
    local previous = last[key]
    last[key] = now
    if previous and now - previous < cfg.debounce_ms then
      taken[key] = true
      return false
    end
  end
  if cfg.no_repeat then
    -- a press that never reaches the app cannot auto-repeat: send one tap
    taken[key] = true
    HID.Combo(key)
    return false
  end
  return true
end

function OnUp(key)
  if taken[key] then
    taken[key] = nil
    return false
  end
  return true
end