feat(keyboard): 恢复 Shift/CapsLock 与方向键等特殊键
旧内核桌面 desktop.vp 里有完整的键盘解码(修饰键状态、特殊键映射、重复),S3e 下沉后丢了,只剩一个简化 scancode_to_ascii(shift/caps 字段从未赋值、方向键返回 0 被丢)。① 内核 keyboard.vp:新增解码层 _decode_scancode + 输出队列,跟踪 Shift(0x2A/0x36)/CapsLock(0x3A)/Ctrl/Alt,Shift 输出大写与上档符号、Caps 只翻转字母区;E0 前缀的独立方向键/Home/End/Delete/PageUp/PageDown/Insert 输出 ANSI 序列(ESC[A/B/C/D、ESC[H/F、ESC[3~/5~/6~/2~);释放码只清状态不产字符。devfs 的 ASCII 字节流契约不变。② vpui:Tk 新增 _ansi_state/_ansi_param 与 _fold_ansi_key(),在 KEY 分发前折叠 ANSI 序列,映射到既有 VK_* 语义(Text/Entry 里那些分支不再是死代码)。Trim:Insert 无 VK_INSERT 定义、被吞掉;typematic 重复未恢复;USB 键盘未涉及。
This commit is contained in:
1 parent
66dc11364a
commit
266e6fbcfb
2 files changed
+182
-42
No files matched your search
@@ -10,23 +10,31 @@ define KBD_DATA = 0x60
|
||||
define KBD_STATUS = 0x64
|
||||
define KBD_COMMAND = 0x64
|
||||
|
||||
define KBD_BUF_SIZE = 1024
|
||||
|
||||
define KEY_RELEASED = 0x80
|
||||
|
||||
buffer: [KBD_BUF_SIZE]uint8
|
||||
buf_head: static uint32 = 0
|
||||
buf_tail: static uint32 = 0
|
||||
buf_count: static uint32 = 0
|
||||
define KBD_BUF_SIZE = 1024
|
||||
define KBD_OUT_SIZE = 1024
|
||||
|
||||
define KEY_RELEASED = 0x80
|
||||
|
||||
buffer: [KBD_BUF_SIZE]uint8
|
||||
buf_head: static uint32 = 0
|
||||
buf_tail: static uint32 = 0
|
||||
buf_count: static uint32 = 0
|
||||
kbd_irq_cnt: static uint64 = 0
|
||||
# 硬件初始化只做一次(init 有多个调用点,见 init() 内注释)
|
||||
_kbd_inited: static int = 0
|
||||
|
||||
shift_l: static uint8 = 0
|
||||
shift_r: static uint8 = 0
|
||||
ctrl_l: static uint8 = 0
|
||||
alt_l: static uint8 = 0
|
||||
caps_lock: static uint8 = 0
|
||||
_kbd_inited: static int = 0
|
||||
|
||||
# 解码后的 ASCII / ANSI 转义字节队列(read_scancode 从原始缓冲解码后填入)
|
||||
out_buf: [KBD_OUT_SIZE]uint8
|
||||
out_head: static uint32 = 0
|
||||
out_tail: static uint32 = 0
|
||||
out_count: static uint32 = 0
|
||||
|
||||
shift_l: static uint8 = 0
|
||||
shift_r: static uint8 = 0
|
||||
ctrl_l: static uint8 = 0
|
||||
alt_l: static uint8 = 0
|
||||
caps_lock: static uint8 = 0
|
||||
_e0_pending: static uint8 = 0
|
||||
|
||||
scancode_set1: [128]char = [
|
||||
'\0', '\x1b', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',
|
||||
@@ -89,35 +97,124 @@ def irq_handler() -> int:
|
||||
buf_count += 1
|
||||
return 0
|
||||
|
||||
def _out_push(b: uint8):
|
||||
global out_tail, out_count
|
||||
if out_count >= KBD_OUT_SIZE: return
|
||||
out_buf[out_tail] = b
|
||||
out_tail = out_tail + 1
|
||||
if out_tail >= KBD_OUT_SIZE:
|
||||
out_tail = 0
|
||||
out_count += 1
|
||||
|
||||
def _emit_csi(final: uint8):
|
||||
_out_push(uint8(0x1B))
|
||||
_out_push(uint8(0x5B))
|
||||
_out_push(final)
|
||||
|
||||
def _emit_csi_tilde(digit: uint8):
|
||||
_out_push(uint8(0x1B))
|
||||
_out_push(uint8(0x5B))
|
||||
_out_push(digit)
|
||||
_out_push(uint8(0x7E))
|
||||
|
||||
# CapsLock 只翻转字母大小写(符号仍由 Shift 决定)
|
||||
def _is_letter_code(code: uint8) -> bool:
|
||||
if code >= uint8(0x10) and code <= uint8(0x19): return True
|
||||
if code >= uint8(0x1E) and code <= uint8(0x26): return True
|
||||
if code >= uint8(0x2C) and code <= uint8(0x32): return True
|
||||
return False
|
||||
|
||||
# 扫描码 -> ASCII / ANSI 转义字节,追加到输出队列
|
||||
def _decode_scancode(sc: uint8):
|
||||
global shift_l, shift_r, ctrl_l, alt_l, caps_lock, _e0_pending
|
||||
if sc == uint8(0xE0):
|
||||
_e0_pending = 1
|
||||
return
|
||||
if sc == uint8(0xE1):
|
||||
_e0_pending = 0
|
||||
return
|
||||
released: bool = (sc & uint8(KEY_RELEASED)) != 0
|
||||
code: uint8 = uint8(sc & ~uint8(KEY_RELEASED))
|
||||
if _e0_pending != 0:
|
||||
_e0_pending = 0
|
||||
# E0 1D / 38 = 右 Ctrl / 右 Alt
|
||||
if code == uint8(0x1D):
|
||||
ctrl_l = 0
|
||||
if not released: ctrl_l = 1
|
||||
return
|
||||
if code == uint8(0x38):
|
||||
alt_l = 0
|
||||
if not released: alt_l = 1
|
||||
return
|
||||
if released: return
|
||||
if code == uint8(0x48): _emit_csi(uint8(0x41)) # Up
|
||||
elif code == uint8(0x50): _emit_csi(uint8(0x42)) # Down
|
||||
elif code == uint8(0x4D): _emit_csi(uint8(0x43)) # Right
|
||||
elif code == uint8(0x4B): _emit_csi(uint8(0x44)) # Left
|
||||
elif code == uint8(0x47): _emit_csi(uint8(0x48)) # Home
|
||||
elif code == uint8(0x4F): _emit_csi(uint8(0x46)) # End
|
||||
elif code == uint8(0x53): _emit_csi_tilde(uint8(0x33)) # Delete
|
||||
elif code == uint8(0x49): _emit_csi_tilde(uint8(0x35)) # PageUp
|
||||
elif code == uint8(0x51): _emit_csi_tilde(uint8(0x36)) # PageDown
|
||||
elif code == uint8(0x52): _emit_csi_tilde(uint8(0x32)) # Insert
|
||||
return
|
||||
if code == uint8(0x2A):
|
||||
shift_l = 0
|
||||
if not released: shift_l = 1
|
||||
return
|
||||
if code == uint8(0x36):
|
||||
shift_r = 0
|
||||
if not released: shift_r = 1
|
||||
return
|
||||
if code == uint8(0x1D):
|
||||
ctrl_l = 0
|
||||
if not released: ctrl_l = 1
|
||||
return
|
||||
if code == uint8(0x38):
|
||||
alt_l = 0
|
||||
if not released: alt_l = 1
|
||||
return
|
||||
if code == uint8(0x3A):
|
||||
if not released: caps_lock = caps_lock ^ uint8(1)
|
||||
return
|
||||
if released: return
|
||||
shifted: bool = (shift_l or shift_r) != 0
|
||||
if caps_lock and _is_letter_code(code):
|
||||
shifted = not shifted
|
||||
ch: char = scancode_set1[code]
|
||||
if shifted:
|
||||
ch = scancode_set1_shift[code]
|
||||
if ch != '\0':
|
||||
_out_push(uint8(ch))
|
||||
|
||||
def read_scancode() -> uint8:
|
||||
global buf_head, buf_count
|
||||
global buf_head, buf_count, out_head, out_count
|
||||
asm.cli()
|
||||
if buf_count == 0:
|
||||
# 原始扫描码持续解码,直到产出至少一个可读字节
|
||||
while out_count == 0 and buf_count > 0:
|
||||
raw: uint8 = buffer[buf_head]
|
||||
buf_head = buf_head + 1
|
||||
if buf_head >= KBD_BUF_SIZE:
|
||||
buf_head = 0
|
||||
buf_count -= 1
|
||||
_decode_scancode(raw)
|
||||
if out_count == 0:
|
||||
asm.sti()
|
||||
return 0
|
||||
code: uint8 = buffer[buf_head]
|
||||
buf_head = buf_head + 1
|
||||
if buf_head >= KBD_BUF_SIZE:
|
||||
buf_head = 0
|
||||
buf_count -= 1
|
||||
code: uint8 = out_buf[out_head]
|
||||
out_head = out_head + 1
|
||||
if out_head >= KBD_OUT_SIZE:
|
||||
out_head = 0
|
||||
out_count -= 1
|
||||
asm.sti()
|
||||
return code
|
||||
|
||||
def scancode_to_ascii(scancode: uint8) -> char:
|
||||
# 断码(bit7 = 按键释放)不产出字符:剥掉 bit7 再查表会把释放事件也翻译成同一个
|
||||
# ASCII,导致每个键输出两个字符(打 ls 得到 llss)。
|
||||
if (scancode & uint8(KEY_RELEASED)) != 0: return '\0'
|
||||
code: uint8 = uint8(scancode & ~uint8(KEY_RELEASED))
|
||||
if code >= 128: return '\0'
|
||||
shifted: bool = (shift_l or shift_r) != 0
|
||||
if caps_lock and code >= 0x10 and code <= 0x32:
|
||||
shifted = not shifted
|
||||
if shifted:
|
||||
return scancode_set1_shift[code]
|
||||
return scancode_set1[code]
|
||||
# read_scancode() 已解码为 ASCII/转义字节,这里只做透传
|
||||
return char(scancode)
|
||||
|
||||
def has_data() -> int:
|
||||
return buf_count
|
||||
return int(buf_count) + int(out_count)
|
||||
|
||||
def init():
|
||||
global _kbd_inited
|
||||
|
||||
+51
-8
@@ -1518,6 +1518,8 @@ virtual class Tk(Wm, Misc):
|
||||
_grid_cols: int
|
||||
_bb: uint32* # 后备缓冲:整帧画完再一次性拷进共享 arena,避免合成器读到半成品帧
|
||||
_bb_px: int # 后备缓冲容量(像素数),窗口变大时才重新分配
|
||||
_ansi_state: int # ESC 序列解析状态:0=普通 1=已收 ESC 2=已收 ESC[
|
||||
_ansi_param: int # CSI 数值参数(如 ESC[5~ 的 5)
|
||||
|
||||
def __init__(self):
|
||||
global _g_layout_init
|
||||
@@ -1551,6 +1553,8 @@ virtual class Tk(Wm, Misc):
|
||||
_g_layout_init = 1
|
||||
self._bb = None
|
||||
self._bb_px = 0
|
||||
self._ansi_state = 0
|
||||
self._ansi_param = 0
|
||||
for j in range(32):
|
||||
self._title[j] = 0
|
||||
|
||||
@@ -1785,6 +1789,41 @@ virtual class Tk(Wm, Misc):
|
||||
return w
|
||||
return None
|
||||
|
||||
# 逐字节投递的按键流 -> ANSI CSI 状态机;返回 0=本字节被吞掉,1=可分发
|
||||
def _fold_ansi_key(self, evt: window.InputEvent*) -> int:
|
||||
ch: int = evt.x
|
||||
if self._ansi_state == 0:
|
||||
if ch == 27:
|
||||
self._ansi_state = 1
|
||||
self._ansi_param = 0
|
||||
return 0
|
||||
return 1
|
||||
if self._ansi_state == 1:
|
||||
if ch == 91:
|
||||
self._ansi_state = 2
|
||||
return 0
|
||||
self._ansi_state = 0
|
||||
evt.x = 27
|
||||
return 1
|
||||
if ch >= 48 and ch <= 57:
|
||||
self._ansi_param = self._ansi_param * 10 + (ch - 48)
|
||||
return 0
|
||||
self._ansi_state = 0
|
||||
vk: int = 0
|
||||
if ch == 65: vk = window.VK_UP
|
||||
elif ch == 66: vk = window.VK_DOWN
|
||||
elif ch == 67: vk = window.VK_RIGHT
|
||||
elif ch == 68: vk = window.VK_LEFT
|
||||
elif ch == 72: vk = window.VK_HOME
|
||||
elif ch == 70: vk = window.VK_END
|
||||
elif ch == 126:
|
||||
if self._ansi_param == 3: vk = window.VK_DELETE
|
||||
elif self._ansi_param == 5: vk = window.VK_PRIOR
|
||||
elif self._ansi_param == 6: vk = window.VK_NEXT
|
||||
if vk == 0: return 0
|
||||
evt.x = vk
|
||||
return 1
|
||||
|
||||
def mainloop(self):
|
||||
self.redraw()
|
||||
evt: window.InputEvent
|
||||
@@ -1803,15 +1842,19 @@ virtual class Tk(Wm, Misc):
|
||||
self.bh = sz & 0xFFFF
|
||||
need_redraw = 1
|
||||
elif etype == window.EVENT_TYPE_KEY or etype == window.EVENT_TYPE_KEY_RELEASE:
|
||||
fw: Widget* = self.focused_widget
|
||||
if fw is None:
|
||||
fw = self._first_text_widget()
|
||||
folded: int = 1
|
||||
if etype == window.EVENT_TYPE_KEY:
|
||||
folded = self._fold_ansi_key(&evt)
|
||||
if folded != 0:
|
||||
fw: Widget* = self.focused_widget
|
||||
if fw is None:
|
||||
fw = self._first_text_widget()
|
||||
if fw is not None:
|
||||
fw.focused = 1
|
||||
self.focused_widget = fw
|
||||
if fw is not None:
|
||||
fw.focused = 1
|
||||
self.focused_widget = fw
|
||||
if fw is not None:
|
||||
handled: int = fw.dispatch_event(&evt)
|
||||
if handled: need_redraw = 1
|
||||
handled: int = fw.dispatch_event(&evt)
|
||||
if handled: need_redraw = 1
|
||||
else:
|
||||
if etype == window.EVENT_TYPE_HOVER:
|
||||
i: int = self.widget_count - 1
|
||||
|
||||
Reference in new issue
Block a user