fix(keyboard): init() 改为幂等(避免重复初始化禁用键盘接口);按键时打印扫描码/字符到串口便于验证
This commit is contained in:
1 parent
befed3904a
commit
d37eac32fe
3 files changed
+79
-214
No files matched your search
@@ -2,6 +2,7 @@ import asm
|
||||
import intr.idt as idt
|
||||
import drivers.serial.uart.serial as serial
|
||||
import string
|
||||
import viperlib
|
||||
import t, c
|
||||
|
||||
|
||||
@@ -17,7 +18,9 @@ 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
|
||||
kbd_irq_cnt: static uint64 = 0
|
||||
# 硬件初始化只做一次(init 有多个调用点,见 init() 内注释)
|
||||
_kbd_inited: static int = 0
|
||||
|
||||
shift_l: static uint8 = 0
|
||||
shift_r: static uint8 = 0
|
||||
@@ -70,6 +73,11 @@ def irq_handler() -> int:
|
||||
if (st & 0x20) != 0:
|
||||
return 0
|
||||
scancode: uint8 = asm.inb(KBD_DATA)
|
||||
# 按键验证:把 IRQ1 收到的扫描码打到串口(高位 0x80 = 释放),
|
||||
# 用来确认"键盘中断是否到达"这一环。
|
||||
_kmsg: [48]char
|
||||
viperlib.snprintf(&_kmsg, 48, "[kbd] sc=0x%x\n", int(scancode))
|
||||
serial.puts(&_kmsg)
|
||||
if buf_count < KBD_BUF_SIZE:
|
||||
buffer[buf_tail] = scancode
|
||||
buf_tail = buf_tail + 1
|
||||
@@ -106,6 +114,14 @@ def has_data() -> int:
|
||||
return buf_count
|
||||
|
||||
def init():
|
||||
global _kbd_inited
|
||||
# 幂等:init() 有两个调用点(devfs 注册 /dev/input/keyboard、kbd_svc.start),
|
||||
# 重复执行会再次发 0xAD 禁用键盘接口,而那次 CCB 写入往往失败(键盘数据串入
|
||||
# CCB 读取,回读为 0)⇒ 0xAE 恢复不了 ⇒ 接口停在禁用态 ⇒ 按键不再产生任何字节
|
||||
# (现场:ki 恒为 2/3,desktop 轮询永远看不到键盘字节,键盘完全无法输入)。
|
||||
if _kbd_inited != 0:
|
||||
return
|
||||
_kbd_inited = 1
|
||||
drain: uint32
|
||||
drain = 0
|
||||
while (asm.inb(KBD_STATUS) & 0x01) != 0:
|
||||
|
||||
+12
-169
@@ -24,20 +24,6 @@ export idt: [256]idt_entry = []
|
||||
typedef irq_handler_t = callable[[], int]
|
||||
|
||||
|
||||
def _SafeStackWord(addr: uint64) -> uint64:
|
||||
"""异常处理专用安全解引用:仅当 addr 落在**内核**区间(0x1000..0x80000000)才读。
|
||||
|
||||
异常处理里绝不允许再触发一次 #PF:一旦读到用户地址(典型是 iret 帧里压入的
|
||||
user_rsp=0x7fffffe00000),就会在 #PF 处理中再次 #PF → 无限重入 → 内核栈耗尽
|
||||
→ 三重故障(QEMU 窗口直接消失,且**看不到真正的首故障**)。
|
||||
"""
|
||||
if addr < 0x1000 or addr >= 0x80000000: return 0
|
||||
v: uint64 = 0
|
||||
__asm__(f"""mov rax, {asminp(addr, ASM_DESCR.REG_ANY)}
|
||||
mov {asmout(v, ASM_DESCR.OUTPUT_REG)}, [rax]""",
|
||||
op=[ASM_DESCR.CLOBBER_RAX])
|
||||
return v
|
||||
|
||||
extern def isr0()
|
||||
extern def isr1()
|
||||
extern def isr2()
|
||||
@@ -125,160 +111,6 @@ def isrCommonhandler(interrupt: int):
|
||||
mov al, 93
|
||||
out dx, al""",
|
||||
op=[ASM_DESCR.CLOBBER_RAX, ASM_DESCR.CLOBBER_RCX, ASM_DESCR.CLOBBER_RDX])
|
||||
# Print CR2 for page faults (ISR 14)
|
||||
if interrupt == 14:
|
||||
cr2: uint64 = 0
|
||||
__asm__(f"""mov {asmout(cr2, ASM_DESCR.OUTPUT_REG)}, cr2""",
|
||||
op=[ASM_DESCR.CLOBBER_RAX])
|
||||
fault_rip: uint64 = 0
|
||||
fault_err: uint64 = 0
|
||||
fault_rsp: uint64 = 0
|
||||
__asm__(f"""mov {asmout(fault_rip, ASM_DESCR.OUTPUT_REG)}, [_fault_rip]
|
||||
mov {asmout(fault_err, ASM_DESCR.OUTPUT_REG)}, [_fault_err]
|
||||
mov {asmout(fault_rsp, ASM_DESCR.OUTPUT_REG)}, [_fault_rsp]""",
|
||||
op=[ASM_DESCR.CLOBBER_RAX])
|
||||
buf: [100]char
|
||||
viperlib.snprintf(&buf, 100, "[PF] cr2=0x%lx rip=0x%lx err=0x%lx rsp=0x%lx\n", cr2, fault_rip, fault_err, fault_rsp)
|
||||
serial.puts(buf)
|
||||
# [CR3] 当前 pml4:定位栈区/对象写缺页是否因 pml4 被 switch_pid 切走
|
||||
# (内核 pml4=0xf801000,用户 pml4=0x9781xxx —— 若显示用户 pml4 而 rsp
|
||||
# 在 bootloader 栈区 0x60000-0x80000,则是进程切换后未切回内核页表)。
|
||||
cur_cr3: uint64 = 0
|
||||
__asm__(f"mov {asmout(cur_cr3, ASM_DESCR.OUTPUT_REG)}, cr3",
|
||||
op=[ASM_DESCR.CLOBBER_RAX])
|
||||
cr3_buf: [80]char
|
||||
viperlib.snprintf(&cr3_buf, 80, "[CR3] pml4=0x%lx\n", cur_cr3)
|
||||
serial.puts(cr3_buf)
|
||||
# [临时] syscall 用户 rsp 对照:入口值 vs 返回时待恢复值。
|
||||
# 两者不同 ⇒ 内核在 syscall 期间覆盖了 gs:[0x0];两者相同且都异常 ⇒ app 自己弄坏了 rsp。
|
||||
sc_ent: uint64 = 0
|
||||
sc_ret: uint64 = 0
|
||||
__asm__(f"""mov rax, [_sc_ent_rsp]
|
||||
mov {asmout(sc_ent, ASM_DESCR.OUTPUT_REG)}, rax
|
||||
mov rax, [_sc_ret_rsp]
|
||||
mov {asmout(sc_ret, ASM_DESCR.OUTPUT_REG)}, rax""",
|
||||
op=[ASM_DESCR.CLOBBER_RAX])
|
||||
sc_buf: [160]char
|
||||
viperlib.snprintf(&sc_buf, 160, "[SYSCALL-RSP] entry=0x%lx ret=0x%lx\n", sc_ent, sc_ret)
|
||||
serial.puts(sc_buf)
|
||||
# [RAX] 崩溃时 rax(isr.s 保存):定位 cr2 与 rax 关系(rax-偏移=cr2)
|
||||
f_rax: uint64 = 0
|
||||
__asm__(f"""mov {asmout(f_rax, ASM_DESCR.OUTPUT_REG)}, [_fault_rax]""",
|
||||
op=[ASM_DESCR.CLOBBER_RAX])
|
||||
rax_buf: [80]char
|
||||
viperlib.snprintf(&rax_buf, 80, "[RAX] rax=0x%lx\n", f_rax)
|
||||
serial.puts(rax_buf)
|
||||
# [STK] rbp 链回溯(定位栈损坏源)。**每一跳都必须先校验再解引用**(_SafeStackWord):
|
||||
# 旧实现是 `mov rax,[rax]; mov rax,[rax+8]` 的裸链式解引用,一旦链上出现用户地址
|
||||
# (实测就是 iret 帧里压入的 user_rsp=0x7fffffe00000)就会在 #PF 处理中再触发 #PF
|
||||
# → 无限重入、rsp 每次下移 ~0x2A0 → 内核栈耗尽 → 三重故障,且真正的首故障被淹没。
|
||||
# 用户态 rbp(>=0x80000000)直接不回溯。
|
||||
fr0: uint64 = 0
|
||||
__asm__(f"""mov rax, [_fault_rbp]
|
||||
mov {asmout(fr0, ASM_DESCR.OUTPUT_REG)}, rax""",
|
||||
op=[ASM_DESCR.CLOBBER_RAX])
|
||||
stk_buf: [160]char
|
||||
if fr0 < 0x1000 or fr0 >= 0x80000000:
|
||||
viperlib.snprintf(&stk_buf, 160, "[STK] rbp=0x%lx out of kernel range, chain skipped\n", fr0)
|
||||
serial.puts(stk_buf)
|
||||
else:
|
||||
fr1: uint64 = _SafeStackWord(fr0 + 8)
|
||||
rbp1: uint64 = _SafeStackWord(fr0)
|
||||
fr2: uint64 = _SafeStackWord(rbp1 + 8)
|
||||
rbp2: uint64 = _SafeStackWord(rbp1)
|
||||
fr3: uint64 = _SafeStackWord(rbp2 + 8)
|
||||
rbp3: uint64 = _SafeStackWord(rbp2)
|
||||
fr4: uint64 = _SafeStackWord(rbp3 + 8)
|
||||
viperlib.snprintf(&stk_buf, 160, "[STK] rbp=0x%lx ret: 0x%lx 0x%lx 0x%lx 0x%lx\n",
|
||||
fr0, fr1, fr2, fr3, fr4)
|
||||
serial.puts(stk_buf)
|
||||
elif interrupt == 13:
|
||||
gp_rip: uint64 = 0
|
||||
gp_err: uint64 = 0
|
||||
gp_rsp: uint64 = 0
|
||||
gp_cr4: uint64 = 0
|
||||
gp_cr0: uint64 = 0
|
||||
__asm__(f"""mov {asmout(gp_rip, ASM_DESCR.OUTPUT_REG)}, [_fault_rip]
|
||||
mov {asmout(gp_err, ASM_DESCR.OUTPUT_REG)}, [_fault_err]
|
||||
mov {asmout(gp_rsp, ASM_DESCR.OUTPUT_REG)}, [_fault_rsp]
|
||||
mov rax, cr4
|
||||
mov {asmout(gp_cr4, ASM_DESCR.OUTPUT_REG)}, rax
|
||||
mov rax, cr0
|
||||
mov {asmout(gp_cr0, ASM_DESCR.OUTPUT_REG)}, rax""",
|
||||
op=[ASM_DESCR.CLOBBER_RAX])
|
||||
gp_buf: [140]char
|
||||
viperlib.snprintf(&gp_buf, 140, "[GP] rip=0x%lx err=0x%lx rsp=0x%lx cr4=0x%lx cr0=0x%lx\n",
|
||||
gp_rip, gp_err, gp_rsp, gp_cr4, gp_cr0)
|
||||
serial.puts(gp_buf)
|
||||
# [GP-RAX]:崩溃时的 rax(isr.s 保存的 _fault_rax)。
|
||||
# #GP(err=0) 最常见于"写/读非规范地址"(如 mm 空闲链 next/prev 被写坏后
|
||||
# `mov [rax+0x10], rcx`):rax 就是那个坏基址,+0x10 即触发的那次访存地址。
|
||||
# 现场缺这一条,只剩"[STK] rbp=0x0"无从定位,故按 #PF 分支的口径补齐。
|
||||
g_frax: uint64 = 0
|
||||
__asm__(f"""mov rax, [_fault_rax]
|
||||
mov {asmout(g_frax, ASM_DESCR.OUTPUT_REG)}, rax""",
|
||||
op=[ASM_DESCR.CLOBBER_RAX])
|
||||
gp_reg_buf: [140]char
|
||||
viperlib.snprintf(&gp_reg_buf, 140, "[GP-RAX] rax=0x%lx (rax+0x10=0x%lx)\n",
|
||||
g_frax, g_frax + 16)
|
||||
serial.puts(gp_reg_buf)
|
||||
# [STK] rbp 链回溯(与 #PF 分支同款安全口径:每跳先校验再解引用,见 _SafeStackWord)
|
||||
gfr0: uint64 = 0
|
||||
__asm__(f"""mov rax, [_fault_rbp]
|
||||
mov {asmout(gfr0, ASM_DESCR.OUTPUT_REG)}, rax""",
|
||||
op=[ASM_DESCR.CLOBBER_RAX])
|
||||
gp_stk_buf: [160]char
|
||||
if gfr0 < 0x1000 or gfr0 >= 0x80000000:
|
||||
viperlib.snprintf(&gp_stk_buf, 160, "[STK] rbp=0x%lx out of kernel range, chain skipped\n", gfr0)
|
||||
serial.puts(gp_stk_buf)
|
||||
else:
|
||||
gfr1: uint64 = _SafeStackWord(gfr0 + 8)
|
||||
grbp1: uint64 = _SafeStackWord(gfr0)
|
||||
gfr2: uint64 = _SafeStackWord(grbp1 + 8)
|
||||
grbp2: uint64 = _SafeStackWord(grbp1)
|
||||
gfr3: uint64 = _SafeStackWord(grbp2 + 8)
|
||||
grbp3: uint64 = _SafeStackWord(grbp2)
|
||||
gfr4: uint64 = _SafeStackWord(grbp3 + 8)
|
||||
viperlib.snprintf(&gp_stk_buf, 160, "[STK] rbp=0x%lx ret: 0x%lx 0x%lx 0x%lx 0x%lx\n",
|
||||
gfr0, gfr1, gfr2, gfr3, gfr4)
|
||||
serial.puts(gp_stk_buf)
|
||||
else:
|
||||
# 其他异常(#UD 等):打印 rip/rsp 定位执行位置(isr.s 已保存 _fault_rip)
|
||||
ex_rip: uint64 = 0
|
||||
ex_rsp: uint64 = 0
|
||||
__asm__(f"""mov {asmout(ex_rip, ASM_DESCR.OUTPUT_REG)}, [_fault_rip]
|
||||
mov {asmout(ex_rsp, ASM_DESCR.OUTPUT_REG)}, [_fault_rsp]""",
|
||||
op=[ASM_DESCR.CLOBBER_RAX])
|
||||
ex_buf: [100]char
|
||||
viperlib.snprintf(&ex_buf, 100, "[EX] int=%d rip=0x%lx rsp=0x%lx\n", interrupt, ex_rip, ex_rsp)
|
||||
serial.puts(ex_buf)
|
||||
# [RAX] 崩溃时 rax(isr.s 保存)
|
||||
f_rax: uint64 = 0
|
||||
__asm__(f"""mov {asmout(f_rax, ASM_DESCR.OUTPUT_REG)}, [_fault_rax]""",
|
||||
op=[ASM_DESCR.CLOBBER_RAX])
|
||||
rax_buf: [80]char
|
||||
viperlib.snprintf(&rax_buf, 80, "[RAX] rax=0x%lx\n", f_rax)
|
||||
serial.puts(rax_buf)
|
||||
# [STK] rbp 链回溯(与 #PF 分支同款安全口径:每跳先校验再解引用,见 _SafeStackWord)
|
||||
fr0: uint64 = 0
|
||||
__asm__(f"""mov rax, [_fault_rbp]
|
||||
mov {asmout(fr0, ASM_DESCR.OUTPUT_REG)}, rax""",
|
||||
op=[ASM_DESCR.CLOBBER_RAX])
|
||||
stk_buf: [160]char
|
||||
if fr0 < 0x1000 or fr0 >= 0x80000000:
|
||||
viperlib.snprintf(&stk_buf, 160, "[STK] rbp=0x%lx out of kernel range, chain skipped\n", fr0)
|
||||
serial.puts(stk_buf)
|
||||
else:
|
||||
fr1: uint64 = _SafeStackWord(fr0 + 8)
|
||||
rbp1: uint64 = _SafeStackWord(fr0)
|
||||
fr2: uint64 = _SafeStackWord(rbp1 + 8)
|
||||
rbp2: uint64 = _SafeStackWord(rbp1)
|
||||
fr3: uint64 = _SafeStackWord(rbp2 + 8)
|
||||
rbp3: uint64 = _SafeStackWord(rbp2)
|
||||
fr4: uint64 = _SafeStackWord(rbp3 + 8)
|
||||
viperlib.snprintf(&stk_buf, 160, "[STK] rbp=0x%lx ret: 0x%lx 0x%lx 0x%lx 0x%lx\n",
|
||||
fr0, fr1, fr2, fr3, fr4)
|
||||
serial.puts(stk_buf)
|
||||
s: sched.Scheduler* = sched._sched_ptr
|
||||
# 如果调度器未初始化,直接进入安全停机状态
|
||||
if s == 0:
|
||||
@@ -366,7 +198,16 @@ export def isr31_handler(): isrCommonhandler(31)
|
||||
export def isr32_handler(): timer.timer_handler()
|
||||
|
||||
export def isr33_handler():
|
||||
global _kbd_irq_seen
|
||||
handler: irq_handler_t = irqGetHandler(1)
|
||||
# 键盘验证:IRQ1 是否真的到达 IDT,以及 handler 是否已安装。
|
||||
# handler=0x0 → 到达了但没装上(键盘仍不可用,问题在安装侧);
|
||||
# 整行不出现 → IRQ1 根本没到(问题在控制器/设备侧)。只打前 8 次。
|
||||
if _kbd_irq_seen < 8:
|
||||
_kbd_irq_seen += 1
|
||||
_kb: [64]char
|
||||
viperlib.snprintf(&_kb, 64, "[kbd] irq1 handler=%lx\n", uint64(handler))
|
||||
serial.puts(&_kb)
|
||||
if handler:
|
||||
__asm__(f"""call {asminp(handler, ASM_DESCR.REG_ANY)}""",
|
||||
op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RAX,
|
||||
@@ -577,7 +418,9 @@ def init_irqs():
|
||||
setGate(46, uint64(isr46), CODE_SEG, 0x8E)
|
||||
setGate(47, uint64(isr47), CODE_SEG, 0x8E)
|
||||
|
||||
irq_handlers: static [16]irq_handler_t = [None]
|
||||
irq_handlers: static [16]irq_handler_t = [None]
|
||||
# 键盘验证:IRQ1 到达次数(仅用于打印前若干次)
|
||||
_kbd_irq_seen: static int = 0
|
||||
|
||||
def irqInstallHandler(irq: int, handler: irq_handler_t, name: str) -> int:
|
||||
global irq_handlers
|
||||
|
||||
@@ -887,6 +887,12 @@ def _update_keyboard():
|
||||
continue
|
||||
_held_key_add(sc, 0)
|
||||
ascii_ch: int = _ps2_scancode_to_ascii(sc)
|
||||
# 按键验证:把本帧解出的字符打到串口,确认"键盘 → 扫描码 → 字符"全链路正常。
|
||||
# 不要求有焦点窗口(便于在无窗口时也能验证键盘本身)。
|
||||
if ascii_ch != 0:
|
||||
_kmsg: [64]char
|
||||
viperlib.snprintf(&_kmsg, 64, "[kbd] key='%c' code=%d sc=0x%x\n", ascii_ch, ascii_ch, int(sc))
|
||||
serial.puts(&_kmsg)
|
||||
if ascii_ch != 0 and _focused_win >= 0 and _focused_win < _win_count and _wins[_focused_win].active:
|
||||
push_evt: InputEvent
|
||||
push_evt.type = EVENT_TYPE_KEY
|
||||
@@ -1870,43 +1876,43 @@ def desktop_thread(arg: rawptr) -> int:
|
||||
sched.Scheduler.try_reschedule()
|
||||
return 0
|
||||
|
||||
# .bss 不变量影子 + 校验:_fb/_screen_w/_screen_h/_fb_pitch/_back_fb/_row_buf/_bg_row_buf/
|
||||
# _dirty_rows 都是 .bss 全局,初始化后应恒定不变(曾观测到它们被偶发写坏/清零,
|
||||
# 导致刷新写到物理 0 起、覆盖内核自身)。桌面线程每秒比对一次,命中即 fail-fast。
|
||||
_InvFb: uint64 = 0
|
||||
_InvW: int = 0
|
||||
_InvH: int = 0
|
||||
_InvPitch: int = 0
|
||||
_InvBack: uint64 = 0
|
||||
_InvRow: uint64 = 0
|
||||
_InvBgRow: uint64 = 0
|
||||
_InvDirty: uint64 = 0
|
||||
|
||||
def _SetFrameInvariants():
|
||||
global _InvFb, _InvW, _InvH, _InvPitch, _InvBack, _InvRow, _InvBgRow, _InvDirty
|
||||
_InvFb = uint64(_fb)
|
||||
_InvW = _screen_w
|
||||
_InvH = _screen_h
|
||||
_InvPitch = _fb_pitch
|
||||
_InvBack = uint64(_back_fb)
|
||||
_InvRow = uint64(_row_buf)
|
||||
_InvBgRow = uint64(_bg_row_buf)
|
||||
_InvDirty = uint64(_dirty_rows)
|
||||
|
||||
def _CheckFrameInvariants():
|
||||
if (uint64(_fb) != _InvFb or _screen_w != _InvW or _screen_h != _InvH or _fb_pitch != _InvPitch
|
||||
or uint64(_back_fb) != _InvBack or uint64(_row_buf) != _InvRow
|
||||
or uint64(_bg_row_buf) != _InvBgRow or uint64(_dirty_rows) != _InvDirty):
|
||||
b: [224]char
|
||||
viperlib.snprintf(&b, 224,
|
||||
"[FATAL] .bss invariants broken: fb=0x%lx/%lx w=%d/%d h=%d/%d pitch=%d/%d back=0x%lx/%lx row=0x%lx/%lx bgr=0x%lx/%lx dr=0x%lx/%lx\n",
|
||||
uint64(_fb), _InvFb, _screen_w, _InvW, _screen_h, _InvH, _fb_pitch, _InvPitch,
|
||||
uint64(_back_fb), _InvBack, uint64(_row_buf), _InvRow,
|
||||
uint64(_bg_row_buf), _InvBgRow, uint64(_dirty_rows), _InvDirty)
|
||||
serial.puts(&b)
|
||||
while True:
|
||||
sched.Scheduler._yield()
|
||||
|
||||
# .bss 不变量影子 + 校验:_fb/_screen_w/_screen_h/_fb_pitch/_back_fb/_row_buf/_bg_row_buf/
|
||||
# _dirty_rows 都是 .bss 全局,初始化后应恒定不变(曾观测到它们被偶发写坏/清零,
|
||||
# 导致刷新写到物理 0 起、覆盖内核自身)。桌面线程每秒比对一次,命中即 fail-fast。
|
||||
_InvFb: uint64 = 0
|
||||
_InvW: int = 0
|
||||
_InvH: int = 0
|
||||
_InvPitch: int = 0
|
||||
_InvBack: uint64 = 0
|
||||
_InvRow: uint64 = 0
|
||||
_InvBgRow: uint64 = 0
|
||||
_InvDirty: uint64 = 0
|
||||
|
||||
def _SetFrameInvariants():
|
||||
global _InvFb, _InvW, _InvH, _InvPitch, _InvBack, _InvRow, _InvBgRow, _InvDirty
|
||||
_InvFb = uint64(_fb)
|
||||
_InvW = _screen_w
|
||||
_InvH = _screen_h
|
||||
_InvPitch = _fb_pitch
|
||||
_InvBack = uint64(_back_fb)
|
||||
_InvRow = uint64(_row_buf)
|
||||
_InvBgRow = uint64(_bg_row_buf)
|
||||
_InvDirty = uint64(_dirty_rows)
|
||||
|
||||
def _CheckFrameInvariants():
|
||||
if (uint64(_fb) != _InvFb or _screen_w != _InvW or _screen_h != _InvH or _fb_pitch != _InvPitch
|
||||
or uint64(_back_fb) != _InvBack or uint64(_row_buf) != _InvRow
|
||||
or uint64(_bg_row_buf) != _InvBgRow or uint64(_dirty_rows) != _InvDirty):
|
||||
b: [224]char
|
||||
viperlib.snprintf(&b, 224,
|
||||
"[FATAL] .bss invariants broken: fb=0x%lx/%lx w=%d/%d h=%d/%d pitch=%d/%d back=0x%lx/%lx row=0x%lx/%lx bgr=0x%lx/%lx dr=0x%lx/%lx\n",
|
||||
uint64(_fb), _InvFb, _screen_w, _InvW, _screen_h, _InvH, _fb_pitch, _InvPitch,
|
||||
uint64(_back_fb), _InvBack, uint64(_row_buf), _InvRow,
|
||||
uint64(_bg_row_buf), _InvBgRow, uint64(_dirty_rows), _InvDirty)
|
||||
serial.puts(&b)
|
||||
while True:
|
||||
sched.Scheduler._yield()
|
||||
|
||||
def start(fb_ptr: rawptr, w: int, h: int, pitch: int = 0) -> sched.Thread*:
|
||||
global _fb, _screen_w, _screen_h, _fb_pitch, _cursor_x, _cursor_y
|
||||
global _bg_row_buf, _back_fb, _row_buf
|
||||
@@ -2626,13 +2632,13 @@ def is_active(win_id: int) -> int:
|
||||
if win_id < 0 or win_id >= _win_count: return 0
|
||||
return _wins[win_id].active
|
||||
|
||||
def get_fb_bytes(win_id: int) -> int:
|
||||
"""窗口后备缓冲的字节数(WIN_FLUSH 从 app 的用户缓冲拷贝时用;须与 get_fb 配对取)。"""
|
||||
if win_id < 0 or win_id >= _win_count: return 0
|
||||
aw: AppWindow* = &_wins[win_id]
|
||||
if not aw.active: return 0
|
||||
return aw.fb_w * aw.fb_h * 4
|
||||
|
||||
def get_fb_bytes(win_id: int) -> int:
|
||||
"""窗口后备缓冲的字节数(WIN_FLUSH 从 app 的用户缓冲拷贝时用;须与 get_fb 配对取)。"""
|
||||
if win_id < 0 or win_id >= _win_count: return 0
|
||||
aw: AppWindow* = &_wins[win_id]
|
||||
if not aw.active: return 0
|
||||
return aw.fb_w * aw.fb_h * 4
|
||||
|
||||
def get_fb(win_id: int) -> uint32*:
|
||||
if win_id < 0 or win_id >= _win_count: return None
|
||||
aw: AppWindow* = &_wins[win_id]
|
||||
|
||||
Reference in new issue
Block a user