marketplace/accessibility

Sticky Keys

v0.1.0

@rebindfree

Tap Shift, Ctrl or Alt once to lock it and again to release it, with a notice either way.

Gallery

Sticky Keys preview

Readme

Holding two keys at once is hard with one hand or a weak grip. Tap Shift, Ctrl, or Alt on its own and it stays held, with a notice so you know. Tap it again to release it. Lock one, two, or all three to build combinations like Ctrl and Shift.

Holding a lock key while you press another key works as usual: the key is released when you let go.

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

Settings

  • First / Second / Third lock key: defaults Left Shift, Left Ctrl, Left Alt. Any key can be a lock key. Restart the package after changing them.

Limits

  • The "locked" and "released" notices appear in Rebind. To see them on the desktop, turn on Desktop notifications in Rebind's settings; two notices within a second show only the first there. Linux shows them only inside Rebind.
  • On macOS, whether letters you type pick up a locked Shift is unverified.
  • Live use remains unverified on Windows, macOS, and Linux.

Stop

Stopping the package releases every locked key. Stop it 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

-- Sticky Keys: tap a lock key on its own and it stays held; tap it again to
-- let it go. Holding it while you press another key works as usual.

local cfg = UI.Schema({
  shift = UI.Keybind("LShift", { label = "First lock key" }),
  ctrl = UI.Keybind("LCtrl", { label = "Second lock key" }),
  alt = UI.Keybind("LAlt", { label = "Third lock key" }),
})

local NAMES = {
  LShift = "Shift",
  RShift = "Shift",
  LCtrl = "Ctrl",
  RCtrl = "Ctrl",
  LAlt = "Alt",
  RAlt = "Alt",
  LWin = "Win",
  RWin = "Win",
}

-- per lock key: held by the finger, used in a chord, latched, being released
local keys = {}
for _, id in { "shift", "ctrl", "alt" } do
  keys[cfg[id]] = { pressed = false, used = false, latched = false, releasing = false }
end

local function name(key)
  return NAMES[key] or key
end

function OnDown(key)
  local k = keys[key]
  if not k then
    for _, other in keys do
      if other.pressed then
        other.used = true
      end
    end
    return true
  end
  if k.latched then
    k.latched, k.releasing = false, true
    HID.Up(key)
    UI.Notify(name(key) .. " released", "info")
  elseif not k.pressed then
    k.pressed, k.used = true, false
    HID.Down(key)
  end
  return false
end

function OnUp(key)
  local k = keys[key]
  if not k then
    return true
  end
  if k.releasing then
    k.releasing = false
  elseif k.pressed then
    k.pressed = false
    if k.used then
      HID.Up(key)
    else
      k.latched = true
      UI.Notify(name(key) .. " locked", "info")
    end
  end
  return false
end

function OnStop()
  for key, k in keys do
    if k.latched or k.pressed then
      HID.Up(key)
    end
  end
end