1
0
mirror of https://github.com/LDDestroier/CC/ synced 2025-10-28 14:07:39 +00:00
Files
ldd-CC/keypress.lua
2025-10-21 13:34:18 -04:00

454 lines
10 KiB
Lua

-- Keypress API
-- by LDDestroier
local keypress = {}
local _DEMO = false
if select(1, ...) == "demo" then
_DEMO = true
end
local r_keys = {}
for k,v in pairs(keys) do
r_keys[v] = k
end
local keys_down = {}
local last_epoch, last_key = 0, 0
local last_evt
local delta
keypress.keys_down = keys_down
local nonprintable_keys = {
[keys.backspace] = true,
[keys.leftCtrl] = true,
[keys.rightCtrl] = true,
[keys.leftAlt] = true,
[keys.rightAlt] = true,
[keys.leftShift] = true,
[keys.rightShift] = true,
[keys.capsLock] = true,
[keys.enter] = true,
[keys.insert] = true,
[keys.delete] = true,
[keys.home] = true,
[keys["end"]] = true,
[keys.pageDown] = true,
[keys.pageUp] = true,
[keys.numLock] = true,
[keys.scrollLock] = true,
[keys.numPadEnter] = true,
[keys.up] = true,
[keys.down] = true,
[keys.left] = true,
[keys.right] = true,
}
for i = 1, 15 do
nonprintable_keys[keys["f" .. i]] = true
end
-- TODO: make these local variables as to not polute the keys table
keys.ctrl = 1001
keys.shift = 1002
keys.alt = 1003
for k,v in pairs(keys) do
keys_down[k] = false
end
local function modifier_keydowns()
keys_down[keys.ctrl] = keys_down[keys.leftCtrl] or keys_down[keys.rightCtrl]
keys_down[keys.shift] = keys_down[keys.leftShift] or keys_down[keys.rightShift]
keys_down[keys.alt] = keys_down[keys.leftAlt] or keys_down[keys.rightAlt]
end
local modfier_lookup = {
[ keys.leftCtrl ] = true,
[ keys.rightCtrl ] = true,
[ keys.ctrl ] = true,
[ keys.leftShift ] = true,
[ keys.rightShift ] = true,
[ keys.shift ] = true,
[ keys.leftAlt ] = true,
[ keys.rightAlt ] = true,
[ keys.alt ] = true,
ctrl = {
[ keys.leftCtrl ] = true,
[ keys.rightCtrl ] = true,
[ keys.ctrl ] = true,
},
shift = {
[ keys.leftShift ] = true,
[ keys.rightShift ] = true,
[ keys.shift ] = true,
},
alt = {
[ keys.leftAlt ] = true,
[ keys.rightAlt ] = true,
[ keys.alt ] = true,
}
}
function keypress.resume(...)
local evt = {...}
local output = {}
if evt[1] == "keypress" then
if _DEMO then
-- exit demo with CTRL-C
if evt[2].key == keys.c and evt[2].ctrl then
return "keypress_terminatedemo"
else
print("key = keys." .. (r_keys[evt[2] .key] or "???"))
if evt[2].char then
write("char = '" .. evt[2].char .. "'")
else
write("char = nil")
end
if evt[2].char_pressed then
print(" ('" .. evt[2].char_pressed .. "')")
else
print("")
end
print("note = " .. (evt[2].notation or "(NONE)"))
write("mods = ")
write(evt[2].ctrl and "ctrl " or "")
write(evt[2].alt and "alt " or "")
print(evt[2].shift and "shift" or "")
print("")
end
end
-- keypress events should die when fed back into keypress.resume()
return
elseif evt[1] == "key" then
keys_down[evt[2]] = true
modifier_keydowns()
if nonprintable_keys[evt[2]] or (keys_down[keys.ctrl] or keys_down[keys.alt]) then
output[1] = "keypress"
output[2] = {
key = evt[2],
char = nil, -- represents a printable character -- use this if you're using keypress API for text input
char_pressed = nil, -- represents the character pressed regardless of if it should print
time = os.epoch(),
ctrl = keys_down[keys.ctrl],
shift = keys_down[keys.shift],
alt = keys_down[keys.alt]
}
output[2].notation = keypress.to_vim_notation(output[2])
else
last_epoch = os.epoch()
last_key = evt[2]
end
elseif evt[1] == "key_up" then
keys_down[evt[2]] = false
modifier_keydowns()
elseif evt[1] == "char" and last_evt == "key" then
delta = os.epoch() - last_epoch
if delta <= 90 then
output[1] = "keypress"
output[2] = {
key = last_key,
char = evt[2],
char_pressed = evt[2],
time = os.epoch(),
ctrl = keys_down[keys.ctrl],
shift = keys_down[keys.shift],
alt = keys_down[keys.alt]
}
output[2].notation = keypress.to_vim_notation(output[2])
end
else
last_key = nil
end
if #output == 0 then
output = evt
end
last_evt = evt[1]
return table.unpack(output)
end
-- convert some key codes to characters
local keys_printable_lookup = {
[ keys.one ] = "1",
[ keys.two ] = "2",
[ keys.three ] = "3",
[ keys.four ] = "4",
[ keys.five ] = "5",
[ keys.six ] = "6",
[ keys.seven ] = "7",
[ keys.eight ] = "8",
[ keys.nine ] = "9",
[ keys.zero ] = "0",
[ keys.grave ] = "`",
[ keys.equals ] = "=",
[ keys.minus ] = "-",
[ keys.underscore ] = "_",
[ keys.leftBracket ] = "[",
[ keys.rightBracket ] = "]",
[ keys.apostrophe ] = "'",
[ keys.colon ] = ":",
[ keys.semiColon ] = ";",
[ keys.period ] = ".",
[ keys.comma ] = ",",
[ keys.slash ] = "/",
[ keys.backslash ] = "\\",
}
local alphabet = "abcdefghijklmnopqrstuvwxyz"
for i = 1, #alphabet do
keys_printable_lookup[ keys[alphabet:sub(i, i)] ] = alphabet:sub(i, i)
end
-- lookup table to turn keypress events into vim notation
local vim_notation_lookup = {
[ keys.home ] = "Home",
[ keys["end"] ] = "End",
[ keys.pageUp ] = "PageUp",
[ keys.pageDown ] = "PageDown",
[ keys.insert ] = "Insert",
[ keys.delete ] = "Del",
[ keys.space ] = "Space",
[ keys.tab ] = "Tab",
[ keys.enter ] = "Enter",
[ keys.backspace ] = "BS",
[ "<" ] = "lt",
[ "|" ] = "Bar",
[ "\\" ] = "Bslash",
[ "\000" ] = "Nul",
[ keys.left ] = "Left",
[ keys.right ] = "Right",
[ keys.up ] = "Up",
[ keys.down ] = "Down",
-- numpad keys that do not change if numlock is on or off
[ keys.numPadAdd ] = "kPlus",
[ keys.numPadSubtract ] = "kMinus",
[ keys.numPadDivide ] = "kDivide",
[ keys.multiply ] = "kMultiply",
[ keys.numPadComma ] = "kComma",
[ keys.numPadEnter ] = "kEnter",
[ keys.numPadEquals ] = "kEqual",
-- NOTE: unsure of actual Vim notation (if any), since I don't own a keyboard with these keys
[ keys.kanji ] = "Kanji",
[ keys.kana ] = "Kana",
[ keys.ax ] = "Ax",
[ keys.yen ] = "Yen",
[ keys.stop ] = "Stop",
[ keys.convert ] = "Convert",
[ keys.noconvert ] = "NoConvert",
-- NOTE: I am quite sure these keys are not recognized in Vim, but they *are* in CraftOS
[ keys.capsLock ] = "CapsLock",
[ keys.scollLock ] = "ScrollLock", -- 'scollLock' misspelled in CraftOS
[ keys.numLock ] = "NumLock",
[ keys.pause ] = "Pause",
}
-- function keys
for i = 1, 15 do
vim_notation_lookup[ keys["f" .. i] ] = "F" .. i
end
-- treated as though numlock is OFF
local vim_notation_nonumlock = {
[ keys.numPadDecimal ] = "kDel",
[ keys.numPad0 ] = "Insert",
[ keys.numPad1 ] = "kEnd",
[ keys.numPad2 ] = "kDown",
[ keys.numPad3 ] = "kPageDown",
[ keys.numPad4 ] = "kLeft",
[ keys.numPad5 ] = "kOrigin",
[ keys.numPad6 ] = "kRight",
[ keys.numPad7 ] = "kHome",
[ keys.numPad8 ] = "kUp",
[ keys.numPad9 ] = "kPageUp",
}
-- what to register if numlock is ON
local vim_notation_numlock = {
[ keys.numPadDecimal ] = "kPoint",
[ keys.numPad0 ] = "k0",
[ keys.numPad1 ] = "k1",
[ keys.numPad2 ] = "k2",
[ keys.numPad3 ] = "k3",
[ keys.numPad4 ] = "k4",
[ keys.numPad5 ] = "k5",
[ keys.numPad6 ] = "k6",
[ keys.numPad7 ] = "k7",
[ keys.numPad8 ] = "k8",
[ keys.numPad9 ] = "k9",
}
-- aliases for vim notation into other vim notation
local vim_notation_alias = {
[ "kDel" ] = "Del",
[ "kEnd" ] = "End",
[ "kDown" ] = "Down",
[ "kPageDown" ] = "PageDown",
[ "kLeft" ] = "Left",
[ "kRight" ] = "Right",
[ "kHome" ] = "Home",
[ "kUp" ] = "Up",
[ "kPageUp" ] = "PageUp",
}
-- lookup table for shift-modified characters
-- might not be representative of keyboards other than my own
local shifted_keys = {
['1'] = '!',
['2'] = '@',
['3'] = '#',
['4'] = '$',
['5'] = '%',
['6'] = '^',
['7'] = '&',
['8'] = '*',
['9'] = '(',
['0'] = ')',
['-'] = '_',
['='] = '+',
['`'] = '~',
['['] = '{',
[']'] = '}',
['\\'] = '|',
[';'] = ':',
['\''] = '\"',
[','] = "<",
['.'] = ">",
['/'] = "?",
}
local function uppersize(char)
return shifted_keys[char] or char:upper()
end
function keypress.to_vim_notation( kp )
if (not kp) or type(kp) ~= "table" then return "", false end
if not kp.key then return "", false end
local output = {"<", "", "", "", "", ">"}
-- output[2] is "M" (alt)
-- output[3] is "C" (ctrl)
-- output[4] is "S" (shift)
-- output[5] is the key code
-- if the keypress has a printable character, omit the "S" notation
local do_omit_s = false
-- check if key is numlock-modifiable
if vim_notation_numlock[ kp.key ] then
-- if a character event was queued, that means numlock must have been on!
if kp.char then
output[5] = vim_notation_numlock[ kp.key ]
else
output[5] = vim_notation_nonumlock[ kp.key ]
end
else
if vim_notation_lookup[ kp.char ] then
output[5] = vim_notation_lookup[ kp.char ]
elseif (kp.ctrl or kp.alt) and keys_printable_lookup[ kp.key ] then
output[5] = keys_printable_lookup[ kp.key ]
if kp.shift then
output[5] = uppersize(output[5])
end
kp.char_pressed = output[5]
do_omit_s = true
output[5] = vim_notation_lookup[ output[5] ] or output[5]
elseif vim_notation_lookup[ kp.key ] then
output[5] = vim_notation_lookup[ kp.key ]
end
end
if kp.char then
do_omit_s = true
end
kp.char_pressed = kp.char_pressed or kp.char
-- tack on modifier codes
if kp.alt and not modfier_lookup.alt[ kp.key ] then
output[2] = "M-"
end
if kp.ctrl and not modfier_lookup.ctrl[ kp.key ] then
output[3] = "C-"
end
if kp.shift and (not do_omit_s) and not modfier_lookup.shift[ kp.key ] then
output[4] = "S-"
end
-- enforce notation aliases
if vim_notation_alias[ output[5] ] then
output[5] = vim_notation_alias[ output[5] ]
end
-- for keys without notation, remove the chevrons and use the printed character
if output[5] == "" then
if not (kp.ctrl or kp.alt) then
output[1] = ""
output[6] = ""
end
output[5] = kp.char
end
if output[5] then
return table.concat(output)
else
return nil
end
end
function keypress.process()
if _DEMO then
print("Keypress API Demo")
print("Press CTRL-C to exit.")
end
while true do
local evt, kp = keypress.resume( os.pullEvent() )
if evt == "keypress" then
os.queueEvent(evt, kp)
elseif _DEMO and evt == "keypress_terminatedemo" then
print("Demo ended.")
return
end
end
end
if _DEMO then
keypress.process()
end
return keypress