- 现场:四个 app 一画就用户态 #PF,cr2 全在内核堆区(~0xb7c1000/0x9f81000/0xb851000/0xb881000),err=0x7(present + 用户写),RAX 正是各自颜色常量(0x1E1E32/0xC0C0C/0xF0F19) - 根因:GET_WINBUF 把 desktop 的**内核**窗口后备缓冲地址直接交给 app(该页 U=0),app 当用户指针写 → 必然 #PF,四个 app 全中(tid=5 那条 cr2=0xffffffff52209000 是同一指针继续走坏) - 内核:WIN_FLUSH 增加源指针 `WIN_FLUSH(win_id, src)`:先 desktop.get_fb 取窗口缓冲、再 desktop.get_fb_bytes 取字节数(配对取,避免 resize 重建导致尺寸/指针不一致),memcpy 后 desktop.flush;syscall_handler 的 WIN_FLUSH 分支透传 arg2;desktop 新增 get_fb_bytes - app(vpsdk/window.vp):get_winbuf 改为按窗口分配**用户态**缓冲(stdlib.malloc → MMAP,落在 0x20000000 用户堆)并缓存;flush(win_id) 改为 WIN_FLUSH(win_id, 用户缓冲)
2807 lines
115 KiB
Plaintext
2807 lines
115 KiB
Plaintext
import drivers.video.vesafb.gfx as gfx
|
||
import drivers.video.vesafb.vga as vga
|
||
import drivers.input.mouse.mouse as mouse
|
||
import drivers.input.keyboard.keyboard as keyboard
|
||
import drivers.usb.hid.usb_mouse as usb_mouse
|
||
import drivers.usb.hid.usb_kbd as usb_kbd
|
||
import drivers.serial.uart.serial as serial
|
||
import platform.pch.pic as pic
|
||
import platform.pch.timer as timer
|
||
import platform.pch.rtc as rtc
|
||
import sched.sched as sched
|
||
import sched.process as process
|
||
import mm.mm as mm
|
||
import viperlib
|
||
import string
|
||
import asm
|
||
|
||
|
||
define TASKBAR_HEIGHT = 50
|
||
define CURSOR_SIZE = 16
|
||
define CURSOR_W = 16
|
||
define CURSOR_H = 16
|
||
define CURSOR_SAVE_SIZE = 256
|
||
define TITLE_HEIGHT_CLASSIC = 24
|
||
define TITLE_HEIGHT_WIN = 32
|
||
define MAX_APP_WINDOWS = 16
|
||
define CLOSE_BTN_SIZE = 16
|
||
define CLOSE_BTN_W_WIN = 46
|
||
define CLOSE_BTN_W7_W = 28
|
||
define CLOSE_BTN_W7_H = 20
|
||
define CLOSE_BTN_W7_R = 3
|
||
define CLOSE_BTN_XP_SZ = 21
|
||
define TITLE_BTN_W_WIN = 46
|
||
define TITLE_BTN_W7 = 28
|
||
define TITLE_BTN_XP = 21
|
||
define TASKBAR_ICON_SZ = 36
|
||
define TASKBAR_ICON_PAD = 6
|
||
define TASKBAR_LOGO_SZ = 32
|
||
define WIN_RADIUS = 8
|
||
define SHADOW_ENABLED = False
|
||
define SHADOW_LEFT = 8
|
||
define SHADOW_RIGHT = 12
|
||
define SHADOW_TOP = 4
|
||
define SHADOW_BOTTOM = 14
|
||
define SHADOW_ALPHA = 100
|
||
define CURSOR_SHADOW_ALPHA = 50
|
||
|
||
define STYLE_CLASSIC = 0
|
||
define STYLE_WIN10 = 1
|
||
define STYLE_WIN11 = 2
|
||
define STYLE_WIN7 = 3
|
||
define STYLE_WINXP = 4
|
||
|
||
_fb: uint32*
|
||
_screen_w: int = 0
|
||
_screen_h: int = 0
|
||
_fb_pitch: int = 0
|
||
_cursor_x: int = 512
|
||
_cursor_y: int = 300
|
||
_cursor_buttons: uint8 = 0
|
||
_back_fb: uint32*
|
||
_last_time_sec: uint32 = 0
|
||
_row_buf: uint32*
|
||
_bg_row_buf: uint32*
|
||
|
||
|
||
class AppWindow:
|
||
active: int
|
||
x: int
|
||
y: int
|
||
w: int
|
||
h: int
|
||
title: [32]char
|
||
orig_title: [32]char
|
||
content_h: int
|
||
fb: uint32*
|
||
fb_w: int
|
||
fb_h: int
|
||
z_order: int
|
||
style: int
|
||
close_hover: int
|
||
min_hover: int
|
||
max_hover: int
|
||
is_maximized: int
|
||
orig_x: int
|
||
orig_y: int
|
||
orig_w: int
|
||
orig_h: int
|
||
pid: int
|
||
cursor_type: int
|
||
app_cursor_type: int
|
||
ping_pending: int
|
||
no_resp: int
|
||
no_resp_sec: uint32
|
||
minimized: int
|
||
resizable_w: int
|
||
resizable_h: int
|
||
resize_w: int
|
||
resize_h: int
|
||
old_fb: uint32*
|
||
|
||
_wins: [MAX_APP_WINDOWS]AppWindow
|
||
_win_count: int = 0
|
||
_next_z: int = 1
|
||
_current_app_pid: int = 0
|
||
HB_INTERVAL_SEC: uint32 = 5
|
||
HB_NO_RESP_SEC: uint32 = 30
|
||
_hb_last_sec: uint32 = 0
|
||
_dirty_rows: uint8* = None
|
||
_dirty_count: int = 0
|
||
_dirty_y_min: int = 0
|
||
_dirty_y_max: int = -1
|
||
_vblank_ok: int = -1
|
||
_prev_cursor_x: int = 512
|
||
_prev_cursor_y: int = 300
|
||
_cursor_save_buf: [256]uint32
|
||
_cursor_saved: int = 0
|
||
|
||
_CURSOR_ARROW: [256]uint8 = [
|
||
1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||
1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||
1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,
|
||
1,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,
|
||
1,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,
|
||
1,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,
|
||
1,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,
|
||
1,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,
|
||
1,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,
|
||
1,2,2,2,1,1,1,1,1,1,0,0,0,0,0,0,
|
||
1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,
|
||
1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||
1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||
]
|
||
|
||
_CURSOR_IBEAM: [256]uint8 = [
|
||
0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,
|
||
0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,
|
||
0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,
|
||
]
|
||
|
||
_CURSOR_RESIZE_EW: [256]uint8 = [
|
||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,1,1,1,0,1,1,1,0,0,0,0,0,
|
||
0,0,0,1,2,2,1,1,1,2,2,1,0,0,0,0,
|
||
0,0,1,2,2,2,2,2,2,2,2,2,1,0,0,0,
|
||
0,1,2,2,2,2,2,2,2,2,2,2,2,1,0,0,
|
||
0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,
|
||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,
|
||
0,1,2,2,2,2,2,2,2,2,2,2,2,1,0,0,
|
||
0,0,1,2,2,2,2,2,2,2,2,2,1,0,0,0,
|
||
0,0,0,1,2,2,1,1,1,2,2,1,0,0,0,0,
|
||
0,0,0,0,1,1,1,0,1,1,1,0,0,0,0,0,
|
||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||
]
|
||
|
||
_CURSOR_RESIZE_NS: [256]uint8 = [
|
||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,1,2,2,1,0,0,0,0,0,0,0,0,0,
|
||
0,0,1,2,2,2,2,1,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,
|
||
0,0,1,2,2,2,2,1,0,0,0,0,0,0,0,0,
|
||
0,0,0,1,2,2,1,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||
]
|
||
|
||
_CURSOR_RESIZE_NWSE: [256]uint8 = [
|
||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||
0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,
|
||
0,1,2,1,1,0,0,0,0,0,0,0,0,0,0,0,
|
||
0,0,1,2,2,1,0,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,1,2,2,1,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,1,2,2,1,1,0,0,0,0,0,0,0,
|
||
0,0,0,0,0,1,2,2,1,0,0,0,0,0,0,0,
|
||
0,0,0,0,0,0,1,2,2,1,0,0,0,0,0,0,
|
||
0,0,0,0,0,0,0,1,2,2,1,0,0,0,0,0,
|
||
0,0,0,0,0,0,0,0,1,2,2,1,0,0,0,0,
|
||
0,0,0,0,0,0,0,0,0,1,2,2,1,0,0,0,
|
||
0,0,0,0,0,0,0,0,0,0,1,2,1,0,0,0,
|
||
0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,
|
||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||
]
|
||
_last_clock_sec: uint32 = 0
|
||
_prev_btn: uint8 = 0
|
||
|
||
define EVENT_QUEUE_SIZE = 32
|
||
define EVENT_TYPE_NONE = 0
|
||
define EVENT_TYPE_CLICK = 1
|
||
define EVENT_TYPE_HOVER = 2
|
||
define EVENT_TYPE_RELEASE = 3
|
||
define EVENT_TYPE_HOVER_LEAVE = 4
|
||
define EVENT_TYPE_KEY = 5
|
||
define EVENT_TYPE_KEY_RELEASE = 6
|
||
define EVENT_TYPE_PING = 7
|
||
define EVENT_TYPE_RESIZE = 8
|
||
|
||
define RESIZE_EDGE_NONE = 0
|
||
define RESIZE_EDGE_RIGHT = 1
|
||
define RESIZE_EDGE_BOTTOM = 2
|
||
define RESIZE_EDGE_CORNER = 3
|
||
define RESIZE_BORDER = 6
|
||
define RESIZE_MIN_W = 100
|
||
define RESIZE_MIN_H = 60
|
||
|
||
define CURSOR_DEFAULT = 0
|
||
define CURSOR_IBEAM = 1
|
||
define CURSOR_RESIZE_EW = 2
|
||
define CURSOR_RESIZE_NS = 3
|
||
define CURSOR_RESIZE_NWSE = 4
|
||
|
||
define KEY_STATE_PRESS = 1
|
||
define KEY_STATE_RELEASE = 2
|
||
define KEY_STATE_REPEAT = 3
|
||
|
||
define TYPEMATIC_DELAY = 500
|
||
define TYPEMATIC_RATE = 40
|
||
|
||
define HELD_KEY_MAX = 8
|
||
|
||
class held_key_info:
|
||
keycode: uint8
|
||
is_usb: uint8
|
||
press_time: uint64
|
||
last_repeat: uint64
|
||
|
||
_held_keys: [HELD_KEY_MAX]held_key_info
|
||
_held_key_count: int = 0
|
||
|
||
@__attribute__(packed)
|
||
class InputEvent:
|
||
type: uint32
|
||
win_id: int
|
||
x: int
|
||
y: int
|
||
button: uint8
|
||
_pad0: uint8
|
||
_pad1: uint8
|
||
_pad2: uint8
|
||
|
||
define INPUT_EVENT_SIZE = 20
|
||
|
||
_event_queue: [EVENT_QUEUE_SIZE]InputEvent
|
||
_event_head: int = 0
|
||
_event_tail: int = 0
|
||
_last_hover_win: int = -1
|
||
_focused_win: int = -1
|
||
|
||
define KBD_DEV_PS2 = 0
|
||
define KBD_DEV_USB = 1
|
||
|
||
_ps2_shift_l: uint8 = 0
|
||
_ps2_shift_r: uint8 = 0
|
||
_ps2_caps: uint8 = 0
|
||
_ps2_e0_pending: uint8 = 0
|
||
_kbd_irq_masked: int = 0
|
||
|
||
define VK_LEFT = -1
|
||
define VK_RIGHT = -2
|
||
define VK_UP = -3
|
||
define VK_DOWN = -4
|
||
define VK_HOME = -5
|
||
define VK_END = -6
|
||
define VK_DELETE = -7
|
||
|
||
_ps2_set1: [128]char = [
|
||
'\0', '\x1b', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',
|
||
'\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',
|
||
'\0', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', "'", '`', '\0',
|
||
'\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', '\0', '\0', '\0', ' ',
|
||
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '-', '\0', '\0', '\0', '\0',
|
||
'\0', '+', '\0', '\0', '\0', '0', '.', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
|
||
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
|
||
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
|
||
'\0'
|
||
]
|
||
|
||
_ps2_set1_shift: [128]char = [
|
||
'\0', '\x1b', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b',
|
||
'\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n',
|
||
'\0', 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', '\0',
|
||
'|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', '\0', '\0', '\0', ' ',
|
||
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '-', '\0', '\0', '\0', '\0',
|
||
'\0', '+', '\0', '\0', '\0', '0', '.', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
|
||
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
|
||
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
|
||
'\0'
|
||
]
|
||
|
||
def _ps2_scancode_to_ascii(scancode: uint8) -> int:
|
||
code: uint8 = scancode & uint8(0x7F)
|
||
if code >= 128: return 0
|
||
shifted: int = 0
|
||
if (_ps2_shift_l or _ps2_shift_r) != 0:
|
||
shifted = 1
|
||
if _ps2_caps != 0:
|
||
if code >= 0x10 and code <= 0x32:
|
||
shifted = shifted ^ 1
|
||
if shifted:
|
||
return int(_ps2_set1_shift[code])
|
||
return int(_ps2_set1[code])
|
||
|
||
|
||
def _push_event(evt: InputEvent):
|
||
global _event_head, _event_tail
|
||
if evt.type == EVENT_TYPE_RESIZE:
|
||
i: int = _event_head
|
||
while i != _event_tail:
|
||
if _event_queue[i].type == EVENT_TYPE_RESIZE and _event_queue[i].win_id == evt.win_id:
|
||
_event_queue[i].type = EVENT_TYPE_NONE
|
||
i = (i + 1) % EVENT_QUEUE_SIZE
|
||
next_tail: int = (_event_tail + 1) % EVENT_QUEUE_SIZE
|
||
if next_tail == _event_head:
|
||
_event_head = (_event_head + 1) % EVENT_QUEUE_SIZE
|
||
string.memcpy(&_event_queue[_event_tail], &evt, INPUT_EVENT_SIZE)
|
||
_event_tail = next_tail
|
||
|
||
def poll_event(buf_ptr: rawptr, caller_pid: int) -> int:
|
||
global _event_head, _event_tail
|
||
while _event_head != _event_tail:
|
||
if _event_queue[_event_head].type == EVENT_TYPE_NONE:
|
||
_event_head = (_event_head + 1) % EVENT_QUEUE_SIZE
|
||
continue
|
||
evt_win: int = _event_queue[_event_head].win_id
|
||
evt_pid: int = 0
|
||
win_ok: int = 0
|
||
if evt_win >= 0 and evt_win < _win_count:
|
||
evt_pid = _wins[evt_win].pid
|
||
win_ok = 1
|
||
if evt_pid == caller_pid:
|
||
string.memcpy(buf_ptr, &_event_queue[_event_head], INPUT_EVENT_SIZE)
|
||
_event_head = (_event_head + 1) % EVENT_QUEUE_SIZE
|
||
return 1
|
||
if win_ok and not _wins[evt_win].active:
|
||
_event_head = (_event_head + 1) % EVENT_QUEUE_SIZE
|
||
continue
|
||
scan: int = (_event_head + 1) % EVENT_QUEUE_SIZE
|
||
while scan != _event_tail:
|
||
if _event_queue[scan].type == EVENT_TYPE_NONE:
|
||
scan = (scan + 1) % EVENT_QUEUE_SIZE
|
||
continue
|
||
evt_win2: int = _event_queue[scan].win_id
|
||
evt_pid2: int = 0
|
||
if evt_win2 >= 0 and evt_win2 < _win_count:
|
||
evt_pid2 = _wins[evt_win2].pid
|
||
if evt_pid2 == caller_pid:
|
||
string.memcpy(buf_ptr, &_event_queue[scan], INPUT_EVENT_SIZE)
|
||
i: int = scan
|
||
while i != _event_tail:
|
||
next_i: int = (i + 1) % EVENT_QUEUE_SIZE
|
||
if next_i != _event_head:
|
||
string.memcpy(&_event_queue[i], &_event_queue[next_i], INPUT_EVENT_SIZE)
|
||
i = next_i
|
||
if _event_tail == 0:
|
||
_event_tail = EVENT_QUEUE_SIZE - 1
|
||
else:
|
||
_event_tail = _event_tail - 1
|
||
return 1
|
||
scan = (scan + 1) % EVENT_QUEUE_SIZE
|
||
break
|
||
return 0
|
||
|
||
def _get_title_height(style: int) -> int:
|
||
if style == STYLE_CLASSIC:
|
||
return TITLE_HEIGHT_CLASSIC
|
||
return TITLE_HEIGHT_WIN
|
||
|
||
def _mark_rows_dirty(start_y: int, count: int):
|
||
global _dirty_count, _dirty_y_min, _dirty_y_max
|
||
for i in range(start_y, start_y + count):
|
||
if i >= 0 and i < _screen_h:
|
||
if not _dirty_rows[i]:
|
||
_dirty_rows[i] = 1
|
||
_dirty_count += 1
|
||
if i < _dirty_y_min or _dirty_count == 1:
|
||
_dirty_y_min = i
|
||
if i > _dirty_y_max or _dirty_count == 1:
|
||
_dirty_y_max = i
|
||
|
||
def _mark_all_dirty():
|
||
global _dirty_count, _dirty_y_min, _dirty_y_max
|
||
string.memset(_dirty_rows, 1, _screen_h)
|
||
_dirty_count = _screen_h
|
||
_dirty_y_min = 0
|
||
_dirty_y_max = _screen_h - 1
|
||
|
||
def _has_dirty_rows() -> int:
|
||
return _dirty_count
|
||
|
||
def _clear_dirty_flags():
|
||
global _dirty_count, _dirty_y_min, _dirty_y_max
|
||
string.memset(_dirty_rows, 0, _screen_h)
|
||
_dirty_count = 0
|
||
_dirty_y_min = _screen_h
|
||
_dirty_y_max = -1
|
||
|
||
def _wait_vblank():
|
||
global _vblank_ok
|
||
if _vblank_ok == 0:
|
||
return
|
||
timeout: int = 50000
|
||
while (asm.inb(0x3DA) & 0x08) != 0:
|
||
timeout -= 1
|
||
if timeout <= 0:
|
||
_vblank_ok = 0
|
||
return
|
||
while (asm.inb(0x3DA) & 0x08) == 0:
|
||
timeout -= 1
|
||
if timeout <= 0:
|
||
_vblank_ok = 0
|
||
return
|
||
_vblank_ok = 1
|
||
|
||
def _fill_rect(buf: uint32*, bw: int, bh: int, rx: int, ry: int, rw: int, rh: int, color: uint32):
|
||
if rx < 0: rw += rx; rx = 0
|
||
if ry < 0: rh += ry; ry = 0
|
||
if rx + rw > bw: rw = bw - rx
|
||
if ry + rh > bh: rh = bh - ry
|
||
if rw <= 0 or rh <= 0: return
|
||
if _row_buf:
|
||
for i in range(rw):
|
||
_row_buf[i] = color
|
||
row_bytes: int = rw * 4
|
||
for py in range(ry, ry + rh):
|
||
string.memcpy(rawptr(uint64(buf) + (py * bw + rx) * 4), _row_buf, row_bytes)
|
||
|
||
def _alpha_blend(bg: uint32, fg: uint32, alpha: int) -> uint32:
|
||
a: int = alpha
|
||
ia: int = 255 - a
|
||
bg_r: int = int(bg & 0xFF)
|
||
bg_g: int = int((bg >> 8) & 0xFF)
|
||
bg_b: int = int((bg >> 16) & 0xFF)
|
||
fg_r: int = int(fg & 0xFF)
|
||
fg_g: int = int((fg >> 8) & 0xFF)
|
||
fg_b: int = int((fg >> 16) & 0xFF)
|
||
r: int = (fg_r * a + bg_r * ia) >> 8
|
||
g: int = (fg_g * a + bg_g * ia) >> 8
|
||
b: int = (fg_b * a + bg_b * ia) >> 8
|
||
return uint32(r | (g << 8) | (b << 16) | 0xFF000000)
|
||
|
||
def _is_in_corner(px: int, py: int, cx: int, cy: int, r: int) -> int:
|
||
dx: int = px - cx
|
||
dy: int = py - cy
|
||
if dx < 0: dx = -dx
|
||
if dy < 0: dy = -dy
|
||
if dx <= r and dy <= r:
|
||
dist2: int = dx * dx + dy * dy
|
||
r2: int = r * r
|
||
if dist2 > r2:
|
||
return 0
|
||
return 1
|
||
|
||
def _is_on_border(rx: int, ry: int, ww: int, wh: int, r: int) -> int:
|
||
if rx == 0 or rx == ww - 1 or ry == 0 or ry == wh - 1:
|
||
return 1
|
||
if ry < r:
|
||
if rx < r:
|
||
dx: int = rx - r
|
||
dy: int = ry - r
|
||
dist2: int = dx * dx + dy * dy
|
||
if dist2 >= (r - 1) * (r - 1): return 1
|
||
elif rx >= ww - r:
|
||
dx = rx - (ww - r - 1)
|
||
dy = ry - r
|
||
dist2 = dx * dx + dy * dy
|
||
if dist2 >= (r - 1) * (r - 1): return 1
|
||
elif ry >= wh - r:
|
||
if rx < r:
|
||
dx = rx - r
|
||
dy = ry - (wh - r - 1)
|
||
dist2 = dx * dx + dy * dy
|
||
if dist2 >= (r - 1) * (r - 1): return 1
|
||
elif rx >= ww - r:
|
||
dx = rx - (ww - r - 1)
|
||
dy = ry - (wh - r - 1)
|
||
dist2 = dx * dx + dy * dy
|
||
if dist2 >= (r - 1) * (r - 1): return 1
|
||
return 0
|
||
|
||
def _corner_alpha(px: int, py: int, cx: int, cy: int, r: int) -> int:
|
||
dx: int = px - cx
|
||
dy: int = py - cy
|
||
if dx < 0: dx = -dx
|
||
if dy < 0: dy = -dy
|
||
if dx <= r and dy <= r:
|
||
dist2: int = dx * dx + dy * dy
|
||
r2: int = r * r
|
||
if dist2 > r2:
|
||
return 0
|
||
outer: int = (r + 1) * (r + 1)
|
||
if dist2 >= outer:
|
||
return 160
|
||
return 255
|
||
return 255
|
||
|
||
def _draw_char(buf: uint32*, bw: int, bh: int, x: int, y: int, ch: char, color: uint32):
|
||
cval: uint8 = uint8(ch)
|
||
if cval >= 128: cval = 0
|
||
for fy in range(16):
|
||
if y + fy < 0 or y + fy >= bh: continue
|
||
row: uint8 = vga.font[cval][fy]
|
||
if row == 0: continue
|
||
for fx in range(8):
|
||
if row & (0x80 >> fx):
|
||
px: int = x + fx
|
||
if px >= 0 and px < bw:
|
||
buf[(y + fy) * bw + px] = color
|
||
|
||
def _draw_text(buf: uint32*, bw: int, bh: int, x: int, y: int, s: const str, color: uint32):
|
||
cx: int = x
|
||
cy: int = y
|
||
for ch in s:
|
||
if ch == 10:
|
||
cy += 16
|
||
cx = x
|
||
else:
|
||
_draw_char(buf, bw, bh, cx, cy, ch, color)
|
||
cx += 8
|
||
|
||
_drag_idx: int = -1
|
||
|
||
def _eff_w(aw2: AppWindow*) -> int:
|
||
if aw2.resize_w != 0: return aw2.resize_w
|
||
return aw2.w
|
||
|
||
def _eff_h(aw2: AppWindow*) -> int:
|
||
if aw2.resize_h != 0: return aw2.resize_h
|
||
return aw2.content_h
|
||
_drag_off_x: int = 0
|
||
_drag_off_y: int = 0
|
||
_rsz_drag_idx: int = -1
|
||
_rsz_edge: int = 0
|
||
_rsz_start_x: int = 0
|
||
_rsz_start_y: int = 0
|
||
_rsz_start_w: int = 0
|
||
_rsz_start_h: int = 0
|
||
_rsz_last_w: int = 0
|
||
_rsz_last_h: int = 0
|
||
_rsz_last_tick: uint64 = 0
|
||
_mouse_moved: int = 0
|
||
|
||
def _bring_to_front(idx: int):
|
||
global _next_z
|
||
if idx < 0 or idx >= _win_count: return
|
||
aw: AppWindow* = &_wins[idx]
|
||
if not aw.active or aw.minimized: return
|
||
_next_z += 1
|
||
aw.z_order = _next_z
|
||
|
||
def _hit_test_window(mx: int, my: int) -> int:
|
||
best: int = -1
|
||
best_z: int = -1
|
||
for ai in range(_win_count):
|
||
if _wins[ai].active and not _wins[ai].minimized:
|
||
ax: int = _wins[ai].x
|
||
ay: int = _wins[ai].y
|
||
aw: int = _eff_w(&_wins[ai])
|
||
ah: int = _wins[ai].h
|
||
if mx >= ax and mx < ax + aw and my >= ay and my < ay + ah:
|
||
z: int = _wins[ai].z_order
|
||
if z > best_z:
|
||
best_z = z
|
||
best = ai
|
||
return best
|
||
|
||
def _hit_test_resize(mx: int, my: int, idx: int) -> int:
|
||
if idx < 0 or idx >= _win_count: return RESIZE_EDGE_NONE
|
||
aw: AppWindow* = &_wins[idx]
|
||
if not aw.active or aw.minimized: return RESIZE_EDGE_NONE
|
||
if not aw.resizable_w and not aw.resizable_h: return RESIZE_EDGE_NONE
|
||
on_right: int = 0
|
||
on_bottom: int = 0
|
||
if aw.resizable_w:
|
||
if mx >= aw.x + _eff_w(aw) - RESIZE_BORDER and mx < aw.x + _eff_w(aw):
|
||
on_right = 1
|
||
if aw.resizable_h:
|
||
if my >= aw.y + aw.h - RESIZE_BORDER and my < aw.y + aw.h:
|
||
on_bottom = 1
|
||
if on_right and on_bottom: return RESIZE_EDGE_CORNER
|
||
if on_right: return RESIZE_EDGE_RIGHT
|
||
if on_bottom: return RESIZE_EDGE_BOTTOM
|
||
return RESIZE_EDGE_NONE
|
||
|
||
def _hid_to_ascii(keycode: uint8, modifier: uint8) -> int:
|
||
global _ps2_caps
|
||
shift: int = 0
|
||
if (modifier & 0x22) != 0:
|
||
shift = 1
|
||
kc: int = int(keycode)
|
||
if kc >= 4 and kc <= 29:
|
||
if _ps2_caps != 0:
|
||
shift = shift ^ 1
|
||
if shift:
|
||
return kc - 4 + 65
|
||
return kc - 4 + 97
|
||
if kc >= 30 and kc <= 38:
|
||
if shift:
|
||
shifts9: [9]int
|
||
shifts9[0] = 33; shifts9[1] = 64; shifts9[2] = 35
|
||
shifts9[3] = 36; shifts9[4] = 37; shifts9[5] = 94
|
||
shifts9[6] = 38; shifts9[7] = 42; shifts9[8] = 40
|
||
return shifts9[kc - 30]
|
||
return kc - 30 + 49
|
||
if kc == 39:
|
||
if shift: return 41
|
||
return 48
|
||
if kc == 44: return 32
|
||
if kc == 40: return 13
|
||
if kc == 42: return 8
|
||
if kc == 43: return 9
|
||
if kc == 41: return 27
|
||
if kc == 45:
|
||
if shift: return 95
|
||
return 45
|
||
if kc == 46:
|
||
if shift: return 43
|
||
return 61
|
||
if kc == 47:
|
||
if shift: return 123
|
||
return 91
|
||
if kc == 48:
|
||
if shift: return 125
|
||
return 93
|
||
if kc == 49:
|
||
if shift: return 124
|
||
return 92
|
||
if kc == 51:
|
||
if shift: return 58
|
||
return 59
|
||
if kc == 52:
|
||
if shift: return 34
|
||
return 39
|
||
if kc == 53:
|
||
if shift: return 126
|
||
return 96
|
||
if kc == 54:
|
||
if shift: return 60
|
||
return 44
|
||
if kc == 55:
|
||
if shift: return 62
|
||
return 46
|
||
if kc == 56:
|
||
if shift: return 63
|
||
return 47
|
||
return 0
|
||
|
||
def _held_key_add(keycode: uint8, is_usb: uint8):
|
||
global _held_key_count
|
||
if _held_key_count >= HELD_KEY_MAX: return
|
||
for i in range(_held_key_count):
|
||
if _held_keys[i].keycode == keycode and _held_keys[i].is_usb == is_usb:
|
||
return
|
||
now: uint64 = timer.timer_get_ticks()
|
||
_held_keys[_held_key_count].keycode = keycode
|
||
_held_keys[_held_key_count].is_usb = is_usb
|
||
_held_keys[_held_key_count].press_time = now
|
||
_held_keys[_held_key_count].last_repeat = now
|
||
_held_key_count += 1
|
||
|
||
def _held_key_remove(keycode: uint8, is_usb: uint8):
|
||
global _held_key_count
|
||
for i in range(_held_key_count):
|
||
if _held_keys[i].keycode == keycode and _held_keys[i].is_usb == is_usb:
|
||
for j in range(i, _held_key_count - 1):
|
||
_held_keys[j].keycode = _held_keys[j + 1].keycode
|
||
_held_keys[j].is_usb = _held_keys[j + 1].is_usb
|
||
_held_keys[j].press_time = _held_keys[j + 1].press_time
|
||
_held_keys[j].last_repeat = _held_keys[j + 1].last_repeat
|
||
_held_key_count -= 1
|
||
return
|
||
|
||
def _check_key_repeat():
|
||
global _held_key_count
|
||
if _focused_win < 0 or _focused_win >= _win_count: return
|
||
if not _wins[_focused_win].active: return
|
||
now: uint64 = timer.timer_get_ticks()
|
||
for i in range(_held_key_count):
|
||
hk: held_key_info* = &_held_keys[i]
|
||
elapsed: uint64 = now - hk.press_time
|
||
if elapsed < uint64(TYPEMATIC_DELAY):
|
||
continue
|
||
since_last: uint64 = now - hk.last_repeat
|
||
if since_last >= uint64(TYPEMATIC_RATE):
|
||
hk.last_repeat = now
|
||
ascii_ch: int = 0
|
||
if hk.is_usb:
|
||
mod: uint8 = 0
|
||
if _ps2_shift_l or _ps2_shift_r:
|
||
mod = mod | 0x22
|
||
if _ps2_caps:
|
||
mod = mod | 0x01
|
||
ascii_ch = _hid_to_ascii(hk.keycode, mod)
|
||
else:
|
||
ascii_ch = _ps2_scancode_to_ascii(hk.keycode)
|
||
if ascii_ch != 0:
|
||
rep_evt: InputEvent
|
||
rep_evt.type = EVENT_TYPE_KEY
|
||
rep_evt.win_id = _focused_win
|
||
rep_evt.x = ascii_ch
|
||
rep_evt.y = int(hk.keycode)
|
||
rep_evt.button = KEY_STATE_REPEAT
|
||
rep_evt._pad0 = 0
|
||
rep_evt._pad1 = 0
|
||
rep_evt._pad2 = 0
|
||
_push_event(rep_evt)
|
||
|
||
def _update_keyboard():
|
||
global _focused_win, _kbd_irq_masked
|
||
global _ps2_shift_l, _ps2_shift_r, _ps2_caps
|
||
global _ps2_e0_pending
|
||
# Quick check: skip if no PS/2 data and no USB data
|
||
ps2_status: uint8 = asm.inb(0x64)
|
||
if (ps2_status & 0x01) == 0 and not usb_kbd.has_data():
|
||
_check_key_repeat()
|
||
return
|
||
if _kbd_irq_masked == 0:
|
||
pic.clearMask(1)
|
||
_kbd_irq_masked = 1
|
||
kbd_cnt: int = 0
|
||
while usb_kbd.has_data() and kbd_cnt < 32:
|
||
kbd_cnt += 1
|
||
kevt: usb_kbd.usb_kbd_event
|
||
usb_kbd.read_event(&kevt)
|
||
if kevt.keycode == 0x39 and kevt.event_type == usb_kbd.KBD_EVT_PRESS:
|
||
_ps2_caps = _ps2_caps ^ uint8(1)
|
||
if kevt.event_type == usb_kbd.KBD_EVT_PRESS:
|
||
_held_key_add(kevt.keycode, 1)
|
||
usb_ascii: int = _hid_to_ascii(kevt.keycode, kevt.modifier)
|
||
if usb_ascii == 0:
|
||
kc2: int = int(kevt.keycode)
|
||
if kc2 == 0x50: usb_ascii = VK_LEFT
|
||
elif kc2 == 0x4F: usb_ascii = VK_RIGHT
|
||
elif kc2 == 0x52: usb_ascii = VK_UP
|
||
elif kc2 == 0x51: usb_ascii = VK_DOWN
|
||
elif kc2 == 0x4A: usb_ascii = VK_HOME
|
||
elif kc2 == 0x4D: usb_ascii = VK_END
|
||
elif kc2 == 0x4C: usb_ascii = VK_DELETE
|
||
if usb_ascii != 0 and _focused_win >= 0 and _focused_win < _win_count and _wins[_focused_win].active:
|
||
usb_evt: InputEvent
|
||
usb_evt.type = EVENT_TYPE_KEY
|
||
usb_evt.win_id = _focused_win
|
||
usb_evt.x = usb_ascii
|
||
usb_evt.y = int(kevt.keycode)
|
||
usb_evt.button = KEY_STATE_PRESS
|
||
usb_evt._pad0 = 0
|
||
usb_evt._pad1 = 0
|
||
usb_evt._pad2 = 0
|
||
_push_event(usb_evt)
|
||
elif kevt.event_type == usb_kbd.KBD_EVT_RELEASE:
|
||
_held_key_remove(kevt.keycode, 1)
|
||
if _focused_win >= 0 and _focused_win < _win_count and _wins[_focused_win].active:
|
||
rel_evt: InputEvent
|
||
rel_evt.type = EVENT_TYPE_KEY_RELEASE
|
||
rel_evt.win_id = _focused_win
|
||
rel_evt.x = 0
|
||
rel_evt.y = int(kevt.keycode)
|
||
rel_evt.button = KEY_STATE_RELEASE
|
||
rel_evt._pad0 = 0
|
||
rel_evt._pad1 = 0
|
||
rel_evt._pad2 = 0
|
||
_push_event(rel_evt)
|
||
ps2_poll_count: int = 0
|
||
while ps2_poll_count < 16:
|
||
ps2_status: uint8 = asm.inb(0x64)
|
||
if (ps2_status & 0x01) == 0:
|
||
break
|
||
if (ps2_status & 0x20) != 0:
|
||
asm.inb(0x60)
|
||
ps2_poll_count += 1
|
||
continue
|
||
sc: uint8 = asm.inb(0x60)
|
||
ps2_poll_count += 1
|
||
if sc == 0x00 or sc == 0xFA or sc == 0xFE or sc == 0xFF:
|
||
_ps2_e0_pending = 0
|
||
continue
|
||
if sc == 0xE0:
|
||
_ps2_e0_pending = 1
|
||
continue
|
||
if sc == 0xE1:
|
||
_ps2_e0_pending = 0
|
||
continue
|
||
if _ps2_e0_pending != 0:
|
||
_ps2_e0_pending = 0
|
||
e0_code: uint8 = sc & uint8(0x7F)
|
||
if (sc & 0x80) != 0:
|
||
_held_key_remove(e0_code | 0x80, 0)
|
||
if _focused_win >= 0 and _focused_win < _win_count and _wins[_focused_win].active:
|
||
ps2_rel2: InputEvent
|
||
ps2_rel2.type = EVENT_TYPE_KEY_RELEASE
|
||
ps2_rel2.win_id = _focused_win
|
||
ps2_rel2.x = 0
|
||
ps2_rel2.y = int(e0_code | 0x80)
|
||
ps2_rel2.button = KEY_STATE_RELEASE
|
||
ps2_rel2._pad0 = 0
|
||
ps2_rel2._pad1 = 0
|
||
ps2_rel2._pad2 = 0
|
||
_push_event(ps2_rel2)
|
||
continue
|
||
vk: int = 0
|
||
if e0_code == 0x4B: vk = VK_LEFT
|
||
elif e0_code == 0x4D: vk = VK_RIGHT
|
||
elif e0_code == 0x48: vk = VK_UP
|
||
elif e0_code == 0x50: vk = VK_DOWN
|
||
elif e0_code == 0x47: vk = VK_HOME
|
||
elif e0_code == 0x4F: vk = VK_END
|
||
elif e0_code == 0x53: vk = VK_DELETE
|
||
if vk != 0:
|
||
_held_key_add(e0_code | 0x80, 0)
|
||
if _focused_win >= 0 and _focused_win < _win_count and _wins[_focused_win].active:
|
||
e0_evt: InputEvent
|
||
e0_evt.type = EVENT_TYPE_KEY
|
||
e0_evt.win_id = _focused_win
|
||
e0_evt.x = vk
|
||
e0_evt.y = int(e0_code | 0x80)
|
||
e0_evt.button = KEY_STATE_PRESS
|
||
e0_evt._pad0 = 0
|
||
e0_evt._pad1 = 0
|
||
e0_evt._pad2 = 0
|
||
_push_event(e0_evt)
|
||
continue
|
||
if sc == 0x2A:
|
||
_ps2_shift_l = 1
|
||
continue
|
||
if sc == 0xAA:
|
||
_ps2_shift_l = 0
|
||
continue
|
||
if sc == 0x36:
|
||
_ps2_shift_r = 1
|
||
continue
|
||
if sc == 0xB6:
|
||
_ps2_shift_r = 0
|
||
continue
|
||
if sc == 0x3A:
|
||
_ps2_caps = _ps2_caps ^ uint8(1)
|
||
continue
|
||
if (sc & 0x80) != 0:
|
||
release_sc: uint8 = sc & uint8(0x7F)
|
||
_held_key_remove(release_sc, 0)
|
||
if _focused_win >= 0 and _focused_win < _win_count and _wins[_focused_win].active:
|
||
ps2_rel: InputEvent
|
||
ps2_rel.type = EVENT_TYPE_KEY_RELEASE
|
||
ps2_rel.win_id = _focused_win
|
||
ps2_rel.x = 0
|
||
ps2_rel.y = int(release_sc)
|
||
ps2_rel.button = KEY_STATE_RELEASE
|
||
ps2_rel._pad0 = 0
|
||
ps2_rel._pad1 = 0
|
||
ps2_rel._pad2 = 0
|
||
_push_event(ps2_rel)
|
||
continue
|
||
_held_key_add(sc, 0)
|
||
ascii_ch: int = _ps2_scancode_to_ascii(sc)
|
||
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
|
||
push_evt.win_id = _focused_win
|
||
push_evt.x = ascii_ch
|
||
push_evt.y = int(sc)
|
||
push_evt.button = KEY_STATE_PRESS
|
||
push_evt._pad0 = 0
|
||
push_evt._pad1 = 0
|
||
push_evt._pad2 = 0
|
||
_push_event(push_evt)
|
||
_check_key_repeat()
|
||
|
||
def set_cursor_type(win_id: int, cursor_t: int):
|
||
if win_id >= 0 and win_id < _win_count:
|
||
_wins[win_id].app_cursor_type = cursor_t
|
||
_wins[win_id].cursor_type = cursor_t
|
||
|
||
def _update_mouse() -> int:
|
||
global _cursor_x, _cursor_y, _cursor_buttons
|
||
global _drag_idx, _drag_off_x, _drag_off_y
|
||
global _mouse_moved, _prev_btn, _last_hover_win, _focused_win
|
||
global _rsz_drag_idx, _rsz_edge, _rsz_start_x, _rsz_start_y
|
||
global _rsz_start_w, _rsz_start_h, _rsz_last_w, _rsz_last_h, _rsz_last_tick
|
||
moved: int = 0
|
||
poll_cnt: int = 0
|
||
while (mouse.has_data() or usb_mouse.has_data()) and poll_cnt < 32:
|
||
poll_cnt += 1
|
||
pkt_dx: int32 = 0
|
||
pkt_dy: int32 = 0
|
||
pkt_btn: uint8 = 0
|
||
if mouse.has_data():
|
||
pkt: mouse.mouse_packet
|
||
mouse.read_packet(&pkt)
|
||
# PS/2 协议:dx/dy 字节是“幅值”,符号在 flags 的 bit4(X)/bit5(Y)。
|
||
# 若按 int8 直接解释,|位移| ≥ 128 时字节最高位会被当成符号位 → 方向翻转
|
||
# (向右快移被读成负数 → 光标一路向左撞到 x=0,“飞没了”)。
|
||
# 故按协议用“幅值 + 符号位”重建真正的带符号位移(驱动存的是原始 uint8 幅值)。
|
||
dx_mag: int = int(pkt.dx)
|
||
dy_mag: int = int(pkt.dy)
|
||
if (pkt.flags & 0x10) != 0: dx_mag = dx_mag - 256
|
||
if (pkt.flags & 0x20) != 0: dy_mag = dy_mag - 256
|
||
pkt_dx = int32(dx_mag)
|
||
pkt_dy = -int32(dy_mag)
|
||
pkt_btn = pkt.buttons
|
||
elif usb_mouse.has_data():
|
||
upkt: usb_mouse.usb_mouse_packet
|
||
usb_mouse.read_packet(&upkt)
|
||
pkt_dx = int32(upkt.dx)
|
||
pkt_dy = -int32(upkt.dy)
|
||
pkt_btn = upkt.buttons
|
||
if pkt_dx != 0 or pkt_dy != 0:
|
||
_cursor_x = _cursor_x + pkt_dx
|
||
_cursor_y = _cursor_y + pkt_dy
|
||
if _cursor_x < 0: _cursor_x = 0
|
||
if _cursor_y < 0: _cursor_y = 0
|
||
if _cursor_x >= _screen_w: _cursor_x = _screen_w - 1
|
||
if _cursor_y >= _screen_h: _cursor_y = _screen_h - 1
|
||
moved = 1
|
||
_mouse_moved = 1
|
||
btn_down: uint8 = pkt_btn & 0x01
|
||
prev_down: uint8 = _prev_btn & 0x01
|
||
_cursor_buttons = pkt_btn
|
||
_prev_btn = pkt_btn
|
||
if btn_down and not prev_down:
|
||
hit_app: int = _hit_test_window(_cursor_x, _cursor_y)
|
||
if hit_app >= 0:
|
||
aw: AppWindow* = &_wins[hit_app]
|
||
th: int = _get_title_height(aw.style)
|
||
btn_w: int = CLOSE_BTN_SIZE
|
||
btn_h: int = CLOSE_BTN_SIZE
|
||
ew: int = _eff_w(aw)
|
||
btn_x: int = aw.x + ew - btn_w - 4
|
||
btn_y: int = aw.y + (th - btn_h) // 2
|
||
if aw.style == STYLE_WIN7:
|
||
btn_w = CLOSE_BTN_W7_W
|
||
btn_h = CLOSE_BTN_W7_H
|
||
btn_x = aw.x + ew - btn_w - 5
|
||
btn_y = aw.y + (th - btn_h) // 2
|
||
elif aw.style == STYLE_WINXP:
|
||
btn_w = CLOSE_BTN_XP_SZ
|
||
btn_h = CLOSE_BTN_XP_SZ
|
||
btn_x = aw.x + ew - btn_w - 5
|
||
btn_y = aw.y + (th - btn_h) // 2
|
||
elif aw.style == STYLE_WIN10 or aw.style == STYLE_WIN11:
|
||
btn_w = CLOSE_BTN_W_WIN
|
||
btn_h = th - 2
|
||
btn_x = aw.x + ew - btn_w
|
||
btn_y = aw.y + 1
|
||
if _cursor_x >= btn_x and _cursor_x < btn_x + btn_w and _cursor_y >= btn_y and _cursor_y < btn_y + btn_h:
|
||
aw.active = 0
|
||
if _focused_win == hit_app:
|
||
_focused_win = -1
|
||
j: int = _win_count - 1
|
||
while j >= 0:
|
||
if _wins[j].active and not _wins[j].minimized:
|
||
_focused_win = j
|
||
break
|
||
j -= 1
|
||
if _last_hover_win == hit_app:
|
||
_last_hover_win = -1
|
||
_mark_all_dirty()
|
||
else:
|
||
max_bx2: int = btn_x - btn_w
|
||
if aw.style == STYLE_WIN7 or aw.style == STYLE_WINXP:
|
||
max_bx2 = btn_x - btn_w - 1
|
||
if _cursor_x >= max_bx2 and _cursor_x < max_bx2 + btn_w and _cursor_y >= btn_y and _cursor_y < btn_y + btn_h:
|
||
if aw.is_maximized:
|
||
aw.is_maximized = 0
|
||
aw.x = aw.orig_x
|
||
aw.y = aw.orig_y
|
||
aw.h = aw.orig_h
|
||
aw.resize_w = aw.orig_w
|
||
aw.resize_h = aw.orig_h - _get_title_height(aw.style) - 2
|
||
else:
|
||
aw.is_maximized = 1
|
||
aw.orig_x = aw.w
|
||
aw.orig_y = aw.y
|
||
aw.orig_w = aw.w
|
||
aw.orig_h = aw.h
|
||
aw.x = 0
|
||
aw.y = 0
|
||
tb_h_mx: int = TASKBAR_HEIGHT
|
||
aw.h = _screen_h - tb_h_mx
|
||
aw.resize_w = _screen_w
|
||
aw.resize_h = _screen_h - tb_h_mx - _get_title_height(aw.style) - 2
|
||
rs_evt3: InputEvent
|
||
rs_evt3.type = EVENT_TYPE_RESIZE
|
||
rs_evt3.win_id = hit_app
|
||
rs_evt3.x = aw.resize_w
|
||
rs_evt3.y = aw.resize_h
|
||
rs_evt3.button = 0
|
||
rs_evt3._pad0 = 0
|
||
rs_evt3._pad1 = 0
|
||
rs_evt3._pad2 = 0
|
||
_push_event(rs_evt3)
|
||
_mark_all_dirty()
|
||
else:
|
||
min_bx2: int = max_bx2 - btn_w
|
||
if aw.style == STYLE_WIN7 or aw.style == STYLE_WINXP:
|
||
min_bx2 = max_bx2 - btn_w - 1
|
||
if _cursor_x >= min_bx2 and _cursor_x < min_bx2 + btn_w and _cursor_y >= btn_y and _cursor_y < btn_y + btn_h:
|
||
aw.minimized = 1
|
||
if _focused_win == hit_app:
|
||
_focused_win = -1
|
||
j2: int = _win_count - 1
|
||
while j2 >= 0:
|
||
if _wins[j2].active and not _wins[j2].minimized:
|
||
_focused_win = j2
|
||
break
|
||
j2 -= 1
|
||
_mark_all_dirty()
|
||
else:
|
||
re: int = _hit_test_resize(_cursor_x, _cursor_y, hit_app)
|
||
if re != RESIZE_EDGE_NONE:
|
||
_rsz_drag_idx = hit_app
|
||
_rsz_edge = re
|
||
_rsz_start_x = _cursor_x
|
||
_rsz_start_y = _cursor_y
|
||
_rsz_start_w = _eff_w(aw)
|
||
_rsz_start_h = aw.h
|
||
_rsz_last_w = _eff_w(aw)
|
||
_rsz_last_h = aw.h
|
||
_rsz_last_tick = timer.timer_get_ticks()
|
||
_bring_to_front(hit_app)
|
||
_focused_win = hit_app
|
||
_mark_all_dirty()
|
||
elif _cursor_y >= aw.y and _cursor_y < aw.y + th:
|
||
_drag_idx = hit_app
|
||
_drag_off_x = _cursor_x - aw.x
|
||
_drag_off_y = _cursor_y - aw.y
|
||
_bring_to_front(hit_app)
|
||
_focused_win = hit_app
|
||
_mark_all_dirty()
|
||
else:
|
||
_bring_to_front(hit_app)
|
||
_focused_win = hit_app
|
||
_mark_all_dirty()
|
||
evt: InputEvent
|
||
evt.type = EVENT_TYPE_CLICK
|
||
evt.win_id = hit_app
|
||
evt.x = _cursor_x - aw.x
|
||
evt.y = _cursor_y - aw.y - _get_title_height(aw.style)
|
||
evt.button = 1
|
||
evt._pad0 = 0
|
||
evt._pad1 = 0
|
||
evt._pad2 = 0
|
||
_push_event(evt)
|
||
else:
|
||
tb_h_cl: int = TASKBAR_HEIGHT
|
||
bar_y2: int = _screen_h - tb_h_cl
|
||
if _cursor_y >= bar_y2:
|
||
logo_x2: int = 4
|
||
logo_sz2: int = TASKBAR_LOGO_SZ
|
||
logo_r2: int = logo_x2 + logo_sz2
|
||
logo_b2: int = bar_y2 + tb_h_cl
|
||
if _cursor_x >= logo_x2 and _cursor_x < logo_r2 and _cursor_y >= bar_y2 and _cursor_y < logo_b2:
|
||
if _focused_win >= 0 and _focused_win < _win_count:
|
||
if _wins[_focused_win].active:
|
||
_wins[_focused_win].active = 0
|
||
_focused_win = -1
|
||
_mark_all_dirty()
|
||
else:
|
||
icon_sz2: int = TASKBAR_ICON_SZ
|
||
icon_pad2: int = TASKBAR_ICON_PAD
|
||
icon_x2: int = logo_x2 + logo_sz2 + 80
|
||
icon_cnt2: int = 0
|
||
for ti2 in range(_win_count):
|
||
if _wins[ti2].active:
|
||
ix2: int = icon_x2 + icon_cnt2 * (icon_sz2 + icon_pad2)
|
||
iy2: int = bar_y2 + (tb_h_cl - icon_sz2) // 2
|
||
if _cursor_x >= ix2 and _cursor_x < ix2 + icon_sz2 and _cursor_y >= iy2 and _cursor_y < iy2 + icon_sz2:
|
||
if _wins[ti2].minimized:
|
||
_wins[ti2].minimized = 0
|
||
_bring_to_front(ti2)
|
||
_focused_win = ti2
|
||
elif _focused_win == ti2:
|
||
_wins[ti2].minimized = 1
|
||
_focused_win = -1
|
||
for j3 in range(_win_count - 1, -1, -1):
|
||
if _wins[j3].active and not _wins[j3].minimized:
|
||
_focused_win = j3
|
||
break
|
||
else:
|
||
_bring_to_front(ti2)
|
||
_focused_win = ti2
|
||
_mark_all_dirty()
|
||
break
|
||
icon_cnt2 += 1
|
||
if not btn_down:
|
||
if prev_down:
|
||
if _rsz_drag_idx >= 0:
|
||
rs_aw: AppWindow* = &_wins[_rsz_drag_idx]
|
||
final_w: int = _rsz_last_w
|
||
final_h: int = _rsz_last_h
|
||
old_h_mu: int = _eff_h(rs_aw) + _get_title_height(rs_aw.style) + 2
|
||
_mark_rows_dirty(rs_aw.y, old_h_mu)
|
||
final_ch: int = final_h - _get_title_height(rs_aw.style) - 2
|
||
rs_aw.resize_w = final_w
|
||
rs_aw.resize_h = final_ch
|
||
rs_aw.h = final_h
|
||
_mark_rows_dirty(rs_aw.y, final_h)
|
||
rs_evt: InputEvent
|
||
rs_evt.type = EVENT_TYPE_RESIZE
|
||
rs_evt.win_id = _rsz_drag_idx
|
||
rs_evt.x = final_w
|
||
rs_evt.y = final_ch
|
||
rs_evt.button = 0
|
||
rs_evt._pad0 = 0
|
||
rs_evt._pad1 = 0
|
||
rs_evt._pad2 = 0
|
||
_push_event(rs_evt)
|
||
_rsz_drag_idx = -1
|
||
_rsz_edge = RESIZE_EDGE_NONE
|
||
else:
|
||
rel_hit: int = _hit_test_window(_cursor_x, _cursor_y)
|
||
if rel_hit >= 0:
|
||
aw_rel: AppWindow* = &_wins[rel_hit]
|
||
rel_evt: InputEvent
|
||
rel_evt.type = EVENT_TYPE_RELEASE
|
||
rel_evt.win_id = rel_hit
|
||
rel_evt.x = _cursor_x - aw_rel.x
|
||
rel_evt.y = _cursor_y - aw_rel.y - _get_title_height(aw_rel.style)
|
||
rel_evt.button = 0
|
||
rel_evt._pad0 = 0
|
||
rel_evt._pad1 = 0
|
||
rel_evt._pad2 = 0
|
||
_push_event(rel_evt)
|
||
if _drag_idx >= 0:
|
||
moved = 1
|
||
_mark_rows_dirty(_wins[_drag_idx].y, _wins[_drag_idx].h)
|
||
_drag_idx = -1
|
||
if moved and _drag_idx < 0:
|
||
hit_hov: int = _hit_test_window(_cursor_x, _cursor_y)
|
||
if hit_hov >= 0:
|
||
if _last_hover_win >= 0 and _last_hover_win != hit_hov and _last_hover_win < _win_count:
|
||
if _wins[_last_hover_win].active:
|
||
leave_evt3: InputEvent
|
||
leave_evt3.type = EVENT_TYPE_HOVER_LEAVE
|
||
leave_evt3.win_id = _last_hover_win
|
||
leave_evt3.x = -1
|
||
leave_evt3.y = -1
|
||
leave_evt3.button = 0
|
||
leave_evt3._pad0 = 0
|
||
leave_evt3._pad1 = 0
|
||
leave_evt3._pad2 = 0
|
||
_push_event(leave_evt3)
|
||
aw_hov: AppWindow* = &_wins[hit_hov]
|
||
th_hov: int = _get_title_height(aw_hov.style)
|
||
hov_evt: InputEvent
|
||
hov_evt.type = EVENT_TYPE_HOVER
|
||
hov_evt.win_id = hit_hov
|
||
hov_evt.x = _cursor_x - aw_hov.x
|
||
hov_evt.y = _cursor_y - aw_hov.y - th_hov
|
||
hov_evt.button = 0
|
||
hov_evt._pad0 = 0
|
||
hov_evt._pad1 = 0
|
||
hov_evt._pad2 = 0
|
||
_push_event(hov_evt)
|
||
_last_hover_win = hit_hov
|
||
re_hov: int = _hit_test_resize(_cursor_x, _cursor_y, hit_hov)
|
||
if re_hov == RESIZE_EDGE_RIGHT:
|
||
aw_hov.cursor_type = CURSOR_RESIZE_EW
|
||
elif re_hov == RESIZE_EDGE_BOTTOM:
|
||
aw_hov.cursor_type = CURSOR_RESIZE_NS
|
||
elif re_hov == RESIZE_EDGE_CORNER:
|
||
aw_hov.cursor_type = CURSOR_RESIZE_NWSE
|
||
else:
|
||
aw_hov.cursor_type = aw_hov.app_cursor_type
|
||
else:
|
||
if _last_hover_win >= 0 and _last_hover_win < _win_count:
|
||
if _wins[_last_hover_win].active:
|
||
leave_evt: InputEvent
|
||
leave_evt.type = EVENT_TYPE_HOVER_LEAVE
|
||
leave_evt.win_id = _last_hover_win
|
||
leave_evt.x = -1
|
||
leave_evt.y = -1
|
||
leave_evt.button = 0
|
||
leave_evt._pad0 = 0
|
||
leave_evt._pad1 = 0
|
||
leave_evt._pad2 = 0
|
||
_push_event(leave_evt)
|
||
_last_hover_win = -1
|
||
if _drag_idx >= 0:
|
||
aw2: AppWindow* = &_wins[_drag_idx]
|
||
_drag_old_y: int = aw2.y
|
||
_mark_rows_dirty(_drag_old_y, aw2.h)
|
||
aw2.x = _cursor_x - _drag_off_x
|
||
aw2.y = _cursor_y - _drag_off_y
|
||
_mark_rows_dirty(aw2.y, aw2.h)
|
||
if _rsz_drag_idx >= 0:
|
||
rs_aw2: AppWindow* = &_wins[_rsz_drag_idx]
|
||
dx: int = _cursor_x - _rsz_start_x
|
||
dy: int = _cursor_y - _rsz_start_y
|
||
new_w: int = _rsz_start_w
|
||
new_h: int = _rsz_start_h
|
||
if _rsz_edge == RESIZE_EDGE_RIGHT or _rsz_edge == RESIZE_EDGE_CORNER:
|
||
new_w = _rsz_start_w + dx
|
||
if new_w < RESIZE_MIN_W: new_w = RESIZE_MIN_W
|
||
if _rsz_edge == RESIZE_EDGE_BOTTOM or _rsz_edge == RESIZE_EDGE_CORNER:
|
||
new_h = _rsz_start_h + dy
|
||
if new_h < RESIZE_MIN_H: new_h = RESIZE_MIN_H
|
||
if new_w != rs_aw2.w or new_h != rs_aw2.h:
|
||
dw: int = new_w - _rsz_last_w
|
||
if dw < 0: dw = -dw
|
||
dh: int = new_h - _rsz_last_h
|
||
if dh < 0: dh = -dh
|
||
now_tick: uint64 = timer.timer_get_ticks()
|
||
elapsed: uint64 = now_tick - _rsz_last_tick
|
||
need_update: int = 0
|
||
if dw >= 32 or dh >= 32:
|
||
need_update = 1
|
||
elif elapsed >= 100 and (dw >= 4 or dh >= 4):
|
||
need_update = 1
|
||
if need_update:
|
||
_rsz_last_w = new_w
|
||
_rsz_last_h = new_h
|
||
_rsz_last_tick = now_tick
|
||
old_h_rs: int = _eff_h(rs_aw2) + _get_title_height(rs_aw2.style) + 2
|
||
_mark_rows_dirty(rs_aw2.y, old_h_rs)
|
||
new_ch: int = new_h - _get_title_height(rs_aw2.style) - 2
|
||
rs_aw2.resize_w = new_w
|
||
rs_aw2.resize_h = new_ch
|
||
rs_aw2.h = new_h
|
||
_mark_rows_dirty(rs_aw2.y, new_h)
|
||
rs_evt2: InputEvent
|
||
rs_evt2.type = EVENT_TYPE_RESIZE
|
||
rs_evt2.win_id = _rsz_drag_idx
|
||
rs_evt2.x = new_w
|
||
rs_evt2.y = new_ch
|
||
rs_evt2.button = 0
|
||
rs_evt2._pad0 = 0
|
||
rs_evt2._pad1 = 0
|
||
rs_evt2._pad2 = 0
|
||
_push_event(rs_evt2)
|
||
if not moved and _last_hover_win < 0:
|
||
hit_hov2: int = _hit_test_window(_cursor_x, _cursor_y)
|
||
if hit_hov2 >= 0 and _wins[hit_hov2].active:
|
||
aw_hov2: AppWindow* = &_wins[hit_hov2]
|
||
th_hov2: int = _get_title_height(aw_hov2.style)
|
||
hov_evt2: InputEvent
|
||
hov_evt2.type = EVENT_TYPE_HOVER
|
||
hov_evt2.win_id = hit_hov2
|
||
hov_evt2.x = _cursor_x - aw_hov2.x
|
||
hov_evt2.y = _cursor_y - aw_hov2.y - th_hov2
|
||
hov_evt2.button = 0
|
||
hov_evt2._pad0 = 0
|
||
hov_evt2._pad1 = 0
|
||
hov_evt2._pad2 = 0
|
||
_push_event(hov_evt2)
|
||
_last_hover_win = hit_hov2
|
||
if not moved and _last_hover_win >= 0:
|
||
if _last_hover_win >= _win_count or not _wins[_last_hover_win].active:
|
||
leave_evt2: InputEvent
|
||
leave_evt2.type = EVENT_TYPE_HOVER_LEAVE
|
||
leave_evt2.win_id = _last_hover_win
|
||
leave_evt2.x = -1
|
||
leave_evt2.y = -1
|
||
leave_evt2.button = 0
|
||
leave_evt2._pad0 = 0
|
||
leave_evt2._pad1 = 0
|
||
leave_evt2._pad2 = 0
|
||
_push_event(leave_evt2)
|
||
_last_hover_win = -1
|
||
return moved
|
||
|
||
_render_count: int = 0
|
||
_cached_top_win: int = -1
|
||
_perf_t_mouse: uint64 = 0
|
||
_perf_t_kbd: uint64 = 0
|
||
_perf_t_render: uint64 = 0
|
||
_perf_t_flush: uint64 = 0
|
||
_perf_t_total: uint64 = 0
|
||
_perf_frames: uint32 = 0
|
||
_perf_start_ticks: uint64 = 0
|
||
_perf_t_bg: uint64 = 0
|
||
_perf_t_win: uint64 = 0
|
||
_perf_t_cur: uint64 = 0
|
||
_perf_t_fb: uint64 = 0
|
||
|
||
def _render():
|
||
global _render_count, _cached_top_win
|
||
global _perf_t_bg, _perf_t_win, _perf_t_cur, _perf_t_fb
|
||
w: int = _screen_w
|
||
h: int = _screen_h
|
||
pitch: int = _fb_pitch
|
||
back: uint32* = _back_fb
|
||
|
||
_cached_top_win = _get_top_window()
|
||
|
||
# No need to erase cursor - dirty row mechanism redraws background + windows from scratch
|
||
|
||
t0: uint64 = timer.timer_get_ticks()
|
||
row_bytes: int = w * 4
|
||
# fail-fast:_dirty_y_min/_dirty_y_max 是 .bss 全局,一旦被写坏(曾观测到 fb/pitch/w/h
|
||
# 被随机清零),循环会退化成死循环并把脏行越界写到帧缓冲之外的 MMIO(实测把宿主 QEMU
|
||
# 写崩)。这里不做夹取兜底,直接报错停机,暴露上游写坏源。
|
||
# 说明:空脏行集合的合法值是 _dirty_count==0 配合 _dirty_y_max==-1(_dirty_y_min 此时为 h)。
|
||
if _dirty_y_max < -1 or _dirty_y_max > h - 1 or _dirty_y_min < 0:
|
||
serial.puts("[desktop] FATAL: dirty row range corrupted\n")
|
||
while True:
|
||
sched.Scheduler._yield()
|
||
if _bg_row_buf and _dirty_count > 0:
|
||
py_bg: int = _dirty_y_min
|
||
while py_bg <= _dirty_y_max:
|
||
if _dirty_rows[py_bg]:
|
||
string.memcpy(rawptr(uint64(back) + py_bg * row_bytes), _bg_row_buf, row_bytes)
|
||
py_bg += 1
|
||
t1: uint64 = timer.timer_get_ticks()
|
||
_perf_t_bg += (t1 - t0)
|
||
|
||
_draw_taskbar(back)
|
||
|
||
t2: uint64 = timer.timer_get_ticks()
|
||
sorted: [MAX_APP_WINDOWS]int
|
||
cnt: int = 0
|
||
for si in range(_win_count):
|
||
if _wins[si].active and not _wins[si].minimized:
|
||
sorted[cnt] = si
|
||
cnt += 1
|
||
for si in range(cnt - 1):
|
||
for sj in range(si + 1, cnt):
|
||
zi: int = _wins[sorted[si]].z_order
|
||
zj: int = _wins[sorted[sj]].z_order
|
||
if zi > zj:
|
||
tmp: int = sorted[si]
|
||
sorted[si] = sorted[sj]
|
||
sorted[sj] = tmp
|
||
for si in range(cnt):
|
||
_draw_app_window(back, &_wins[sorted[si]])
|
||
t3: uint64 = timer.timer_get_ticks()
|
||
_perf_t_win += (t3 - t2)
|
||
|
||
# Mark cursor rows as dirty so cursor gets flushed to framebuffer
|
||
_mark_rows_dirty(_cursor_y, CURSOR_H)
|
||
|
||
_draw_cursor(back)
|
||
t4: uint64 = timer.timer_get_ticks()
|
||
_perf_t_cur += (t4 - t3)
|
||
|
||
_wait_vblank()
|
||
if _dirty_count > 0:
|
||
py_fb: int = _dirty_y_min
|
||
while py_fb <= _dirty_y_max:
|
||
if _dirty_rows[py_fb]:
|
||
run_start: int = py_fb
|
||
while py_fb <= _dirty_y_max and _dirty_rows[py_fb]:
|
||
py_fb += 1
|
||
run_bytes: int = (py_fb - run_start) * row_bytes
|
||
string.memcpy(rawptr(uint64(_fb) + run_start * pitch), rawptr(uint64(back) + run_start * row_bytes), run_bytes)
|
||
else:
|
||
py_fb += 1
|
||
t5: uint64 = timer.timer_get_ticks()
|
||
_perf_t_fb += (t5 - t4)
|
||
|
||
_render_count += 1
|
||
|
||
def _draw_taskbar(back: uint32*):
|
||
w: int = _screen_w
|
||
h: int = _screen_h
|
||
tb_h: int = TASKBAR_HEIGHT
|
||
bar_y: int = h - tb_h
|
||
if bar_y + tb_h <= _dirty_y_min or bar_y > _dirty_y_max:
|
||
return
|
||
bar_col: uint32 = gfx.COLOR_RGB(32, 32, 32)
|
||
line_col: uint32 = gfx.COLOR_RGB(60, 60, 60)
|
||
|
||
_fill_rect(back, w, h, 0, bar_y, w, tb_h, bar_col)
|
||
_fill_rect(back, w, h, 0, bar_y, w, 1, line_col)
|
||
|
||
logo_x: int = 4
|
||
logo_sz: int = TASKBAR_LOGO_SZ
|
||
logo_y: int = bar_y + (tb_h - logo_sz) // 2
|
||
lc1: uint32 = gfx.COLOR_RGB(80, 120, 220)
|
||
lc2: uint32 = gfx.COLOR_RGB(50, 80, 180)
|
||
lc3: uint32 = gfx.COLOR_RGB(100, 160, 255)
|
||
for lx in range(logo_sz):
|
||
for ly in range(logo_sz):
|
||
px: int = logo_x + lx
|
||
py: int = logo_y + ly
|
||
if px >= 0 and px < w and py >= 0 and py < h:
|
||
col: uint32 = lc2
|
||
cx: int = logo_sz // 2
|
||
cy: int = logo_sz // 2
|
||
dx: int = lx - cx
|
||
dy: int = ly - cy
|
||
dist: int = dx * dx + dy * dy
|
||
r2: int = cx * cx
|
||
if dist <= r2:
|
||
col = lc1
|
||
if dist <= r2 // 2:
|
||
col = lc3
|
||
back[py * w + px] = col
|
||
|
||
_draw_text(back, w, h, logo_x + logo_sz + 8, bar_y + (tb_h - 16) // 2, "ViperOS", 0xFFFFFFFF)
|
||
|
||
icon_sz: int = TASKBAR_ICON_SZ
|
||
icon_pad: int = TASKBAR_ICON_PAD
|
||
icon_x: int = logo_x + logo_sz + 80
|
||
icon_cnt: int = 0
|
||
for ti in range(_win_count):
|
||
if _wins[ti].active:
|
||
ix: int = icon_x + icon_cnt * (icon_sz + icon_pad)
|
||
iy: int = bar_y + (tb_h - icon_sz) // 2
|
||
is_focused: int = 1 if ti == _focused_win else 0
|
||
is_minimized: int = _wins[ti].minimized
|
||
ic_bg: uint32 = gfx.COLOR_RGB(40, 40, 70)
|
||
if is_focused:
|
||
ic_bg = gfx.COLOR_RGB(60, 60, 120)
|
||
elif is_minimized:
|
||
ic_bg = gfx.COLOR_RGB(30, 30, 50)
|
||
_fill_rect(back, w, h, ix, iy, icon_sz, icon_sz, ic_bg)
|
||
ic_border: uint32 = gfx.COLOR_RGB(80, 80, 140)
|
||
if is_focused:
|
||
ic_border = gfx.COLOR_RGB(120, 120, 220)
|
||
_fill_rect(back, w, h, ix, iy, icon_sz, 1, ic_border)
|
||
_fill_rect(back, w, h, ix, iy + icon_sz - 1, icon_sz, 1, ic_border)
|
||
_fill_rect(back, w, h, ix, iy, 1, icon_sz, ic_border)
|
||
_fill_rect(back, w, h, ix + icon_sz - 1, iy, 1, icon_sz, ic_border)
|
||
if _wins[ti].title[0] != 0:
|
||
first_ch: char = _wins[ti].title[0]
|
||
ch_col: uint32 = 0xFFFFFFFF
|
||
if not is_focused:
|
||
ch_col = gfx.COLOR_RGB(180, 180, 200)
|
||
ch_x: int = ix + (icon_sz - 8) // 2
|
||
ch_y: int = iy + (icon_sz - 16) // 2
|
||
_draw_char(back, w, h, ch_x, ch_y, first_ch, ch_col)
|
||
icon_cnt += 1
|
||
|
||
# RTC 缓存由桌面线程的“每秒”分支负责重读(见 desktop_thread 内 _last_clock_sec 那段)。
|
||
# 此前是在这里按 `_render_count % 30 == 0` 重读 —— 会把时钟刷新频率绑到渲染速率上:
|
||
# 空闲时约 1 帧/秒 → 时钟 30 秒才动一次;鼠标一动渲染变快,时钟立刻跟上(表现为
|
||
# “必须鼠标移动才刷新”)。
|
||
hr: uint8 = rtc.rtc_get_hours()
|
||
mn: uint8 = rtc.rtc_get_minutes()
|
||
sc: uint8 = rtc.rtc_get_seconds()
|
||
day: uint8 = rtc.rtc_get_day()
|
||
mon: uint8 = rtc.rtc_get_month()
|
||
yr: uint16 = rtc.rtc_get_year()
|
||
time_buf: [64]char
|
||
string.memset(&time_buf, 0, 64)
|
||
viperlib.snprintf(&time_buf, 64, "%04u-%02u-%02u %02u:%02u:%02u", uint(yr), uint(mon), uint(day), uint(hr), uint(mn), uint(sc))
|
||
time_str_x: int = w - 180
|
||
_draw_text(back, w, h, time_str_x, bar_y + (tb_h - 16) // 2, time_buf, 0xFFFFFFFF)
|
||
|
||
def _draw_window_shadow(back: uint32*, bw: int, bh: int, wx: int, wy: int, ww: int, wh: int, is_rounded: int, rad: int):
|
||
if not SHADOW_ENABLED: return
|
||
sl: int = SHADOW_LEFT
|
||
sr: int = SHADOW_RIGHT
|
||
st: int = SHADOW_TOP
|
||
sb: int = SHADOW_BOTTOM
|
||
sa: int = SHADOW_ALPHA
|
||
shadow_col: uint32 = gfx.COLOR_RGB(0, 0, 0)
|
||
for py in range(wy - st, wy + wh + sb):
|
||
if py >= 0 and py < bh:
|
||
if not _dirty_rows[py]:
|
||
continue
|
||
for px in range(wx - sl, wx + ww + sr):
|
||
if px >= 0 and px < bw:
|
||
in_win: int = 0
|
||
if px >= wx and px < wx + ww and py >= wy and py < wy + wh:
|
||
in_win = 1
|
||
if is_rounded:
|
||
rx: int = px - wx
|
||
ry: int = py - wy
|
||
if rx < rad and ry < rad:
|
||
if (rx - rad) * (rx - rad) + (ry - rad) * (ry - rad) > rad * rad:
|
||
in_win = 0
|
||
elif rx >= ww - rad and ry < rad:
|
||
if (rx - (ww - rad - 1)) * (rx - (ww - rad - 1)) + (ry - rad) * (ry - rad) > rad * rad:
|
||
in_win = 0
|
||
elif rx < rad and ry >= wh - rad:
|
||
if (rx - rad) * (rx - rad) + (ry - (wh - rad - 1)) * (ry - (wh - rad - 1)) > rad * rad:
|
||
in_win = 0
|
||
elif rx >= ww - rad and ry >= wh - rad:
|
||
if (rx - (ww - rad - 1)) * (rx - (ww - rad - 1)) + (ry - (wh - rad - 1)) * (ry - (wh - rad - 1)) > rad * rad:
|
||
in_win = 0
|
||
if in_win:
|
||
continue
|
||
cpx: int = px
|
||
if cpx < wx: cpx = wx
|
||
elif cpx > wx + ww - 1: cpx = wx + ww - 1
|
||
cpy: int = py
|
||
if cpy < wy: cpy = wy
|
||
elif cpy > wy + wh - 1: cpy = wy + wh - 1
|
||
if is_rounded and rad > 0:
|
||
if cpx < wx + rad and cpy < wy + rad:
|
||
acx: int = wx + rad
|
||
acy: int = wy + rad
|
||
adx: int = cpx - acx
|
||
ady: int = cpy - acy
|
||
ad_sq: int = adx * adx + ady * ady
|
||
if ad_sq > rad * rad and ad_sq > 0:
|
||
aax: int = adx
|
||
if aax < 0: aax = 0 - aax
|
||
aay: int = ady
|
||
if aay < 0: aay = 0 - aay
|
||
am: int = aax
|
||
if aay > am: am = aay
|
||
an: int = aax
|
||
if aay < an: an = aay
|
||
ad: int = am + (an * 3) / 8
|
||
if ad > 0:
|
||
cpx = acx + (adx * rad) / ad
|
||
cpy = acy + (ady * rad) / ad
|
||
elif cpx >= wx + ww - rad and cpy < wy + rad:
|
||
acx = wx + ww - rad - 1
|
||
acy = wy + rad
|
||
adx = cpx - acx
|
||
ady = cpy - acy
|
||
ad_sq = adx * adx + ady * ady
|
||
if ad_sq > rad * rad and ad_sq > 0:
|
||
aax = adx
|
||
if aax < 0: aax = 0 - aax
|
||
aay = ady
|
||
if aay < 0: aay = 0 - aay
|
||
am = aax
|
||
if aay > am: am = aay
|
||
an = aax
|
||
if aay < an: an = aay
|
||
ad = am + (an * 3) / 8
|
||
if ad > 0:
|
||
cpx = acx + (adx * rad) / ad
|
||
cpy = acy + (ady * rad) / ad
|
||
elif cpx < wx + rad and cpy >= wy + wh - rad:
|
||
acx = wx + rad
|
||
acy = wy + wh - rad - 1
|
||
adx = cpx - acx
|
||
ady = cpy - acy
|
||
ad_sq = adx * adx + ady * ady
|
||
if ad_sq > rad * rad and ad_sq > 0:
|
||
aax = adx
|
||
if aax < 0: aax = 0 - aax
|
||
aay = ady
|
||
if aay < 0: aay = 0 - aay
|
||
am = aax
|
||
if aay > am: am = aay
|
||
an = aax
|
||
if aay < an: an = aay
|
||
ad = am + (an * 3) / 8
|
||
if ad > 0:
|
||
cpx = acx + (adx * rad) / ad
|
||
cpy = acy + (ady * rad) / ad
|
||
elif cpx >= wx + ww - rad and cpy >= wy + wh - rad:
|
||
acx = wx + ww - rad - 1
|
||
acy = wy + wh - rad - 1
|
||
adx = cpx - acx
|
||
ady = cpy - acy
|
||
ad_sq = adx * adx + ady * ady
|
||
if ad_sq > rad * rad and ad_sq > 0:
|
||
aax = adx
|
||
if aax < 0: aax = 0 - aax
|
||
aay = ady
|
||
if aay < 0: aay = 0 - aay
|
||
am = aax
|
||
if aay > am: am = aay
|
||
an = aax
|
||
if aay < an: an = aay
|
||
ad = am + (an * 3) / 8
|
||
if ad > 0:
|
||
cpx = acx + (adx * rad) / ad
|
||
cpy = acy + (ady * rad) / ad
|
||
dx: int = px - cpx
|
||
dy: int = py - cpy
|
||
adx_d: int = dx
|
||
if adx_d < 0: adx_d = 0 - adx_d
|
||
ady_d: int = dy
|
||
if ady_d < 0: ady_d = 0 - ady_d
|
||
dist: int = adx_d
|
||
if ady_d > dist: dist = ady_d
|
||
dist = dist + ((adx_d + ady_d - dist) * 3) / 8
|
||
me_x: int = sl
|
||
if dx > 0: me_x = sr
|
||
me_y: int = st
|
||
if dy > 0: me_y = sb
|
||
me: int = me_x
|
||
if adx_d + ady_d > 0:
|
||
me = (me_x * adx_d + me_y * ady_d) / (adx_d + ady_d)
|
||
if me > 0 and dist > 0 and dist < me:
|
||
t_val: int = (256 * dist) / me
|
||
t_inv: int = 256 - t_val
|
||
a: int = (sa * t_inv * t_inv) / 65536
|
||
if a > 0:
|
||
back[py * bw + px] = _alpha_blend(back[py * bw + px], shadow_col, a)
|
||
|
||
def _cursor_save_bg(back: uint32*, cx: int, cy: int, w: int, h: int):
|
||
global _cursor_saved
|
||
cw: int = CURSOR_W
|
||
ch: int = CURSOR_H
|
||
for dy in range(ch):
|
||
for dx in range(cw):
|
||
sx: int = cx + dx
|
||
sy: int = cy + dy
|
||
if sx >= 0 and sx < w and sy >= 0 and sy < h:
|
||
_cursor_save_buf[dy * cw + dx] = back[sy * w + sx]
|
||
else:
|
||
_cursor_save_buf[dy * cw + dx] = 0
|
||
_cursor_saved = 1
|
||
|
||
def _cursor_restore_bg(back: uint32*, cx: int, cy: int, w: int, h: int):
|
||
global _cursor_saved
|
||
if _cursor_saved == 0: return
|
||
cw: int = CURSOR_W
|
||
ch: int = CURSOR_H
|
||
for dy in range(ch):
|
||
for dx in range(cw):
|
||
sx: int = cx + dx
|
||
sy: int = cy + dy
|
||
if sx >= 0 and sx < w and sy >= 0 and sy < h:
|
||
back[sy * w + sx] = _cursor_save_buf[dy * cw + dx]
|
||
_cursor_saved = 0
|
||
|
||
def _cursor_draw_bitmap(back: uint32*, cx: int, cy: int, w: int, h: int, bmp: [256]uint8):
|
||
cw: int = CURSOR_W
|
||
ch: int = CURSOR_H
|
||
white: uint32 = gfx.COLOR_RGB(255, 255, 255)
|
||
black: uint32 = gfx.COLOR_RGB(0, 0, 0)
|
||
for dy in range(ch):
|
||
for dx in range(cw):
|
||
pixel: uint8 = bmp[dy * cw + dx]
|
||
if pixel != 0:
|
||
px: int = cx + dx
|
||
py: int = cy + dy
|
||
if px >= 0 and px < w and py >= 0 and py < h:
|
||
if pixel == 1:
|
||
back[py * w + px] = black
|
||
elif pixel == 2:
|
||
back[py * w + px] = white
|
||
|
||
def _cursor_draw_xor(back: uint32*, cx: int, cy: int, w: int, h: int, bmp: [256]uint8):
|
||
cw: int = CURSOR_W
|
||
ch: int = CURSOR_H
|
||
xor_val: uint32 = uint32(0x00FFFFFF)
|
||
for dy in range(ch):
|
||
for dx in range(cw):
|
||
pixel: uint8 = bmp[dy * cw + dx]
|
||
if pixel != 0:
|
||
px: int = cx + dx
|
||
py: int = cy + dy
|
||
if px >= 0 and px < w and py >= 0 and py < h:
|
||
back[py * w + px] = back[py * w + px] ^ xor_val
|
||
|
||
def _draw_cursor(back: uint32*):
|
||
cx: int = _cursor_x
|
||
cy: int = _cursor_y
|
||
w: int = _screen_w
|
||
h: int = _screen_h
|
||
cur_type: int = CURSOR_DEFAULT
|
||
if _last_hover_win >= 0 and _last_hover_win < _win_count:
|
||
cur_type = _wins[_last_hover_win].cursor_type
|
||
if cur_type == CURSOR_IBEAM:
|
||
_cursor_draw_bitmap(back, cx, cy, w, h, _CURSOR_IBEAM)
|
||
elif cur_type == CURSOR_RESIZE_EW:
|
||
_cursor_draw_bitmap(back, cx, cy, w, h, _CURSOR_RESIZE_EW)
|
||
elif cur_type == CURSOR_RESIZE_NS:
|
||
_cursor_draw_bitmap(back, cx, cy, w, h, _CURSOR_RESIZE_NS)
|
||
elif cur_type == CURSOR_RESIZE_NWSE:
|
||
_cursor_draw_bitmap(back, cx, cy, w, h, _CURSOR_RESIZE_NWSE)
|
||
else:
|
||
_cursor_draw_bitmap(back, cx, cy, w, h, _CURSOR_ARROW)
|
||
|
||
def _erase_cursor(back: uint32*):
|
||
global _cursor_saved
|
||
if _cursor_saved == 0: return
|
||
_cursor_restore_bg(back, _prev_cursor_x, _prev_cursor_y, _screen_w, _screen_h)
|
||
|
||
def desktop_thread(arg: rawptr) -> int:
|
||
global _last_clock_sec, _mouse_moved
|
||
global _prev_cursor_x, _prev_cursor_y
|
||
global _perf_t_mouse, _perf_t_kbd, _perf_t_render, _perf_t_flush, _perf_t_total, _perf_frames, _perf_start_ticks
|
||
global _perf_t_bg, _perf_t_win, _perf_t_cur, _perf_t_fb
|
||
serial.puts("[desktop] thread started\n")
|
||
_mark_all_dirty()
|
||
_dt_hb_sec: uint32 = 0
|
||
tb_h_dt: int = TASKBAR_HEIGHT
|
||
_perf_buf: [256]char
|
||
_perf_start_ticks = timer.timer_get_ticks()
|
||
_kbd_poll_ticks: uint64 = 0
|
||
_mouse_poll_ticks: uint64 = 0
|
||
while True:
|
||
_loop_t0: uint64 = timer.timer_get_ticks()
|
||
# Poll mouse at most every 4ms to avoid excessive I/O port overhead
|
||
_now_ticks: uint64 = timer.timer_get_ticks()
|
||
if _now_ticks - _mouse_poll_ticks >= 4:
|
||
_mouse_poll_ticks = _now_ticks
|
||
_mt0: uint64 = timer.timer_get_ticks()
|
||
mouse_active: int = _update_mouse()
|
||
_mt1: uint64 = timer.timer_get_ticks()
|
||
_perf_t_mouse += (_mt1 - _mt0)
|
||
# Poll keyboard at most every 4ms to avoid excessive I/O port overhead
|
||
if _now_ticks - _kbd_poll_ticks >= 4:
|
||
_kbd_poll_ticks = _now_ticks
|
||
_kt0: uint64 = timer.timer_get_ticks()
|
||
_update_keyboard()
|
||
_kt1: uint64 = timer.timer_get_ticks()
|
||
_perf_t_kbd += (_kt1 - _kt0)
|
||
_heartbeat_check()
|
||
sched.Scheduler.try_reschedule()
|
||
_dt_cur_sec: uint32 = timer.timer_get_seconds()
|
||
if _dt_cur_sec != _dt_hb_sec:
|
||
_dt_hb_sec = _dt_cur_sec
|
||
_CheckFrameInvariants()
|
||
cur_sec: uint32 = timer.timer_get_seconds()
|
||
if cur_sec != _last_clock_sec:
|
||
_last_clock_sec = cur_sec
|
||
# 每秒重读 RTC 缓存后再标脏任务栏:RTC 只有重读才会变,而此前重读是按渲染次数
|
||
# 节流的(见 _draw_taskbar),导致空闲时时钟几十秒不动、鼠标一动就刷新。
|
||
rtc.rtc_read_time()
|
||
_mark_rows_dirty(_screen_h - tb_h_dt, tb_h_dt)
|
||
if _mouse_moved:
|
||
_mouse_moved = 0
|
||
sz: int = CURSOR_H
|
||
_mark_rows_dirty(_prev_cursor_y, sz)
|
||
_mark_rows_dirty(_cursor_y, sz)
|
||
_prev_cursor_x = _cursor_x
|
||
_prev_cursor_y = _cursor_y
|
||
top_win: int = _hit_test_window(_cursor_x, _cursor_y)
|
||
for ai in range(_win_count):
|
||
if _wins[ai].active and not _wins[ai].minimized:
|
||
th: int = _get_title_height(_wins[ai].style)
|
||
btn_w: int = CLOSE_BTN_SIZE
|
||
btn_h: int = CLOSE_BTN_SIZE
|
||
ew_hov: int = _eff_w(&_wins[ai])
|
||
btn_x: int = _wins[ai].x + ew_hov - btn_w - 4
|
||
btn_y: int = _wins[ai].y + (th - btn_h) // 2
|
||
if _wins[ai].style == STYLE_WIN7:
|
||
btn_w = CLOSE_BTN_W7_W
|
||
btn_h = CLOSE_BTN_W7_H
|
||
btn_x = _wins[ai].x + ew_hov - btn_w - 5
|
||
btn_y = _wins[ai].y + (th - btn_h) // 2
|
||
elif _wins[ai].style == STYLE_WINXP:
|
||
btn_w = CLOSE_BTN_XP_SZ
|
||
btn_h = CLOSE_BTN_XP_SZ
|
||
btn_x = _wins[ai].x + ew_hov - btn_w - 5
|
||
btn_y = _wins[ai].y + (th - btn_h) // 2
|
||
elif _wins[ai].style == STYLE_WIN10 or _wins[ai].style == STYLE_WIN11:
|
||
btn_w = CLOSE_BTN_W_WIN
|
||
btn_h = th - 2
|
||
btn_x = _wins[ai].x + ew_hov - btn_w
|
||
btn_y = _wins[ai].y + 1
|
||
max_bx3: int = btn_x - btn_w
|
||
if _wins[ai].style == STYLE_WIN7 or _wins[ai].style == STYLE_WINXP:
|
||
max_bx3 = btn_x - btn_w - 1
|
||
min_bx3: int = max_bx3 - btn_w
|
||
if _wins[ai].style == STYLE_WIN7 or _wins[ai].style == STYLE_WINXP:
|
||
min_bx3 = max_bx3 - btn_w - 1
|
||
is_over_close: int = 0
|
||
is_over_max: int = 0
|
||
is_over_min: int = 0
|
||
if ai == top_win:
|
||
if _cursor_x >= btn_x and _cursor_x < btn_x + btn_w and _cursor_y >= btn_y and _cursor_y < btn_y + btn_h:
|
||
is_over_close = 1
|
||
elif _cursor_x >= max_bx3 and _cursor_x < max_bx3 + btn_w and _cursor_y >= btn_y and _cursor_y < btn_y + btn_h:
|
||
is_over_max = 1
|
||
elif _cursor_x >= min_bx3 and _cursor_x < min_bx3 + btn_w and _cursor_y >= btn_y and _cursor_y < btn_y + btn_h:
|
||
is_over_min = 1
|
||
if is_over_close and _wins[ai].close_hover < 255:
|
||
_old_hov: int = _wins[ai].close_hover
|
||
_wins[ai].close_hover += 60
|
||
if _wins[ai].close_hover > 255:
|
||
_wins[ai].close_hover = 255
|
||
if _wins[ai].close_hover != _old_hov:
|
||
_mark_rows_dirty(btn_y, btn_h)
|
||
elif not is_over_close and _wins[ai].close_hover > 0:
|
||
_old_hov2: int = _wins[ai].close_hover
|
||
_wins[ai].close_hover -= 45
|
||
if _wins[ai].close_hover < 0:
|
||
_wins[ai].close_hover = 0
|
||
if _wins[ai].close_hover != _old_hov2:
|
||
_mark_rows_dirty(btn_y, btn_h)
|
||
if is_over_max and _wins[ai].max_hover < 255:
|
||
_old_mh: int = _wins[ai].max_hover
|
||
_wins[ai].max_hover += 60
|
||
if _wins[ai].max_hover > 255:
|
||
_wins[ai].max_hover = 255
|
||
if _wins[ai].max_hover != _old_mh:
|
||
_mark_rows_dirty(btn_y, btn_h)
|
||
elif not is_over_max and _wins[ai].max_hover > 0:
|
||
_old_mh2: int = _wins[ai].max_hover
|
||
_wins[ai].max_hover -= 45
|
||
if _wins[ai].max_hover < 0:
|
||
_wins[ai].max_hover = 0
|
||
if _wins[ai].max_hover != _old_mh2:
|
||
_mark_rows_dirty(btn_y, btn_h)
|
||
if is_over_min and _wins[ai].min_hover < 255:
|
||
_old_nh: int = _wins[ai].min_hover
|
||
_wins[ai].min_hover += 60
|
||
if _wins[ai].min_hover > 255:
|
||
_wins[ai].min_hover = 255
|
||
if _wins[ai].min_hover != _old_nh:
|
||
_mark_rows_dirty(btn_y, btn_h)
|
||
elif not is_over_min and _wins[ai].min_hover > 0:
|
||
_old_nh2: int = _wins[ai].min_hover
|
||
_wins[ai].min_hover -= 45
|
||
if _wins[ai].min_hover < 0:
|
||
_wins[ai].min_hover = 0
|
||
if _wins[ai].min_hover != _old_nh2:
|
||
_mark_rows_dirty(btn_y, btn_h)
|
||
if _has_dirty_rows():
|
||
_rt0: uint64 = timer.timer_get_ticks()
|
||
_render()
|
||
_rt1: uint64 = timer.timer_get_ticks()
|
||
_perf_t_render += (_rt1 - _rt0)
|
||
_ft0: uint64 = timer.timer_get_ticks()
|
||
_clear_dirty_flags()
|
||
_ft1: uint64 = timer.timer_get_ticks()
|
||
_perf_t_flush += (_ft1 - _ft0)
|
||
_perf_frames += 1
|
||
_loop_t1: uint64 = timer.timer_get_ticks()
|
||
_perf_t_total += (_loop_t1 - _loop_t0)
|
||
_now_ticks: uint64 = timer.timer_get_ticks()
|
||
_elapsed_ms: uint64 = _now_ticks - _perf_start_ticks
|
||
if _elapsed_ms >= 1000:
|
||
_fps_val: uint32 = 0
|
||
if _elapsed_ms > 0:
|
||
_fps_val = uint32(uint64(_perf_frames) * 1000 / _elapsed_ms)
|
||
string.memset(&_perf_buf, 0, 256)
|
||
# [DIAG] .rodata 格式串校验:win=%lu/work=%lu 曾出现 ' / , 字节破坏(越界写)。
|
||
# 校验和变化 → 确认为真实内存破坏(配合 .rodata 只读后 #PF 定位写源);
|
||
# 校验和不变 → 疑似 printf/串口输出伪影,走另一条排查线。
|
||
_perf_fmt_str: str = "[perf] fps=%u wall=%lu work=%lu mouse=%lu kbd=%lu render=%lu(bg=%lu win=%lu cur=%lu fb=%lu) dirty=%d vsync=%d sh=%d dc=%d ki=%lu mi=%lu\n"
|
||
_fmt_sum: uint32 = 0
|
||
for _fi in range(110):
|
||
_fmt_sum = _fmt_sum + uint32(uint8(_perf_fmt_str[_fi]))
|
||
_chk_buf: [48]char
|
||
viperlib.snprintf(&_chk_buf, 48, "[chk] sum=%u\n", _fmt_sum)
|
||
serial.puts(&_chk_buf)
|
||
viperlib.snprintf(&_perf_buf, 256, _perf_fmt_str, _fps_val, uint64(_elapsed_ms), uint64(_perf_t_total), uint64(_perf_t_mouse), uint64(_perf_t_kbd), uint64(_perf_t_render), uint64(_perf_t_bg), uint64(_perf_t_win), uint64(_perf_t_cur), uint64(_perf_t_fb), _dirty_count, _vblank_ok, _screen_h, _dirty_count, keyboard.kbd_irq_cnt, mouse.mse_irq_cnt)
|
||
serial.puts(&_perf_buf)
|
||
_perf_t_mouse = 0
|
||
_perf_t_kbd = 0
|
||
_perf_t_render = 0
|
||
_perf_t_flush = 0
|
||
_perf_t_total = 0
|
||
_perf_t_bg = 0
|
||
_perf_t_win = 0
|
||
_perf_t_cur = 0
|
||
_perf_t_fb = 0
|
||
_perf_frames = 0
|
||
_perf_start_ticks = _now_ticks
|
||
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()
|
||
|
||
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
|
||
global _dirty_rows, _prev_cursor_x, _prev_cursor_y
|
||
# fail-fast:帧缓冲地址为 NULL 时绝不继续 —— 否则刷新会把后备缓冲写到物理 0 起,
|
||
# 覆盖 IVT/BDA、低端内存乃至内核镜像自身(实测会边跑边改坏自己正在执行的代码而卡死)。
|
||
if uint64(fb_ptr) == 0:
|
||
serial.puts("[desktop] FATAL: framebuffer pointer is NULL\n")
|
||
while True:
|
||
sched.Scheduler._yield()
|
||
_fb = fb_ptr
|
||
_screen_w = w
|
||
_screen_h = h
|
||
if pitch > 0:
|
||
_fb_pitch = pitch
|
||
else:
|
||
_fb_pitch = w * 4
|
||
_cursor_x = w // 2
|
||
_cursor_y = h // 2
|
||
_prev_cursor_x = _cursor_x
|
||
_prev_cursor_y = _cursor_y
|
||
|
||
_dirty_rows = mm.malloc(uint64(h))
|
||
if _dirty_rows:
|
||
string.memset(_dirty_rows, 0, h)
|
||
else:
|
||
serial.puts("[desktop] dirty_rows malloc FAILED\n")
|
||
return None
|
||
|
||
_back_fb = mm.malloc(uint64(w) * uint64(h) * 4)
|
||
_dbg_bf: [96]char
|
||
viperlib.snprintf(&_dbg_bf, 96, "[dbg] back_fb ret=0x%lx w=%d h=%d\n", uint64(_back_fb), w, h)
|
||
serial.puts(&_dbg_bf)
|
||
if _back_fb:
|
||
string.memset(_back_fb, 0, w * h * 4)
|
||
else:
|
||
serial.puts("[desktop] back_fb malloc FAILED\n")
|
||
return None
|
||
|
||
_row_buf = mm.malloc(uint64(w) * 4)
|
||
_dbg_rb: [96]char
|
||
viperlib.snprintf(&_dbg_rb, 96, "[dbg] row_buf ret=0x%lx\n", uint64(_row_buf))
|
||
serial.puts(&_dbg_rb)
|
||
if _row_buf:
|
||
string.memset(_row_buf, 0, w * 4)
|
||
else:
|
||
serial.puts("[desktop] row_buf malloc FAILED!\n")
|
||
|
||
# mm.malloc(w*4) 走裸名回退生成 call @malloc(i32),形参 i64 不匹配(第 4 次
|
||
# 调用 RAX 返回值被破坏收到 0)。显式 CUInt64T 让参数按 i64 传递。
|
||
_bg_row_buf = mm.malloc(uint64(w) * 4)
|
||
_dbg_br: [96]char
|
||
viperlib.snprintf(&_dbg_br, 96, "[dbg] bg_row_buf ret=0x%lx\n", uint64(_bg_row_buf))
|
||
serial.puts(&_dbg_br)
|
||
if _bg_row_buf:
|
||
desktop_init_col: uint32 = gfx.COLOR_RGB(25, 25, 35)
|
||
for ri in range(w):
|
||
_bg_row_buf[ri] = desktop_init_col
|
||
else:
|
||
serial.puts("[desktop] bg_row_buf malloc FAILED!\n")
|
||
|
||
_mark_all_dirty()
|
||
_SetFrameInvariants()
|
||
p: process.Process* = process.ProcessManager.create_process("desktop", None)
|
||
desktop_pid: int = 0
|
||
if p:
|
||
desktop_pid = p.pid
|
||
th: sched.Thread* = sched.Scheduler.create_thread(desktop_thread, None, desktop_pid)
|
||
if p and th:
|
||
p.addThread(th.tid)
|
||
return th
|
||
|
||
def _get_top_window() -> int:
|
||
best: int = -1
|
||
best_z: int = -1
|
||
for ai in range(_win_count):
|
||
if _wins[ai].active and not _wins[ai].minimized:
|
||
z: int = _wins[ai].z_order
|
||
if z > best_z:
|
||
best_z = z
|
||
best = ai
|
||
return best
|
||
|
||
def _draw_min_btn(back: uint32*, bw: int, bh: int, bx: int, by: int, btn_w: int, btn_h: int, hover_alpha: int, style: int):
|
||
if hover_alpha > 0:
|
||
hover_col: uint32 = gfx.COLOR_RGB(80, 80, 80)
|
||
if style == STYLE_WIN7:
|
||
hover_col = gfx.COLOR_RGB(200, 220, 240)
|
||
elif style == STYLE_WINXP:
|
||
hover_col = gfx.COLOR_RGB(180, 210, 240)
|
||
for py in range(by, by + btn_h):
|
||
if py >= 0 and py < bh:
|
||
for px in range(bx, bx + btn_w):
|
||
if px >= 0 and px < bw:
|
||
bg_pixel: uint32 = back[py * bw + px]
|
||
back[py * bw + px] = _alpha_blend(bg_pixel, hover_col, hover_alpha)
|
||
line_col: uint32 = gfx.COLOR_RGB(180, 180, 180)
|
||
if hover_alpha > 0:
|
||
line_col = gfx.COLOR_RGB(255, 255, 255)
|
||
cx: int = bx + btn_w // 2
|
||
cy: int = by + btn_h // 2
|
||
lw: int = 5
|
||
if btn_w >= 30:
|
||
lw = 8
|
||
ly: int = cy + 3
|
||
if ly >= 0 and ly < bh:
|
||
for lx in range(cx - lw, cx + lw + 1):
|
||
if lx >= 0 and lx < bw:
|
||
back[ly * bw + lx] = line_col
|
||
|
||
def _draw_max_btn(back: uint32*, bw: int, bh: int, bx: int, by: int, btn_w: int, btn_h: int, hover_alpha: int, style: int, is_maximized: int):
|
||
if hover_alpha > 0:
|
||
hover_col2: uint32 = gfx.COLOR_RGB(80, 80, 80)
|
||
if style == STYLE_WIN7:
|
||
hover_col2 = gfx.COLOR_RGB(200, 220, 240)
|
||
elif style == STYLE_WINXP:
|
||
hover_col2 = gfx.COLOR_RGB(180, 210, 240)
|
||
for py2 in range(by, by + btn_h):
|
||
if py2 >= 0 and py2 < bh:
|
||
for px2 in range(bx, bx + btn_w):
|
||
if px2 >= 0 and px2 < bw:
|
||
bg_pixel2: uint32 = back[py2 * bw + px2]
|
||
back[py2 * bw + px2] = _alpha_blend(bg_pixel2, hover_col2, hover_alpha)
|
||
rect_col: uint32 = gfx.COLOR_RGB(180, 180, 180)
|
||
if hover_alpha > 0:
|
||
rect_col = gfx.COLOR_RGB(255, 255, 255)
|
||
cx2: int = bx + btn_w // 2
|
||
cy2: int = by + btn_h // 2
|
||
sz: int = 4
|
||
if btn_w >= 30:
|
||
sz = 6
|
||
if is_maximized:
|
||
off: int = 2
|
||
for ry1 in range(cy2 - sz + off, cy2 + sz + off + 1):
|
||
if ry1 >= 0 and ry1 < bh:
|
||
for rx1 in range(cx2 - sz + off, cx2 + sz + off + 1):
|
||
if rx1 >= 0 and rx1 < bw:
|
||
is_top: int = 0
|
||
if ry1 == cy2 - sz + off or ry1 == cy2 - sz + off + 1:
|
||
is_top = 1
|
||
is_border: int = 0
|
||
if ry1 == cy2 - sz + off or ry1 == cy2 + sz + off or rx1 == cx2 - sz + off or rx1 == cx2 + sz + off:
|
||
is_border = 1
|
||
if is_top or is_border:
|
||
back[ry1 * bw + rx1] = rect_col
|
||
for ry2 in range(cy2 - sz, cy2 + sz + 1):
|
||
if ry2 >= 0 and ry2 < bh:
|
||
for rx2 in range(cx2 - sz, cx2 + sz + 1):
|
||
if rx2 >= 0 and rx2 < bw:
|
||
if ry2 == cy2 - sz or ry2 == cy2 - sz + 1 or rx2 == cx2 - sz or rx2 == cx2 + sz:
|
||
back[ry2 * bw + rx2] = rect_col
|
||
else:
|
||
for ry3 in range(cy2 - sz, cy2 + sz + 1):
|
||
if ry3 >= 0 and ry3 < bh:
|
||
for rx3 in range(cx2 - sz, cx2 + sz + 1):
|
||
if rx3 >= 0 and rx3 < bw:
|
||
if ry3 == cy2 - sz or ry3 == cy2 + sz or rx3 == cx2 - sz or rx3 == cx2 + sz:
|
||
back[ry3 * bw + rx3] = rect_col
|
||
top_y: int = cy2 - sz
|
||
if top_y >= 0 and top_y < bh:
|
||
for tx in range(cx2 - sz, cx2 + sz + 1):
|
||
if tx >= 0 and tx < bw:
|
||
back[top_y * bw + tx] = rect_col
|
||
if top_y + 1 < bh:
|
||
for tx2 in range(cx2 - sz, cx2 + sz + 1):
|
||
if tx2 >= 0 and tx2 < bw:
|
||
back[(top_y + 1) * bw + tx2] = rect_col
|
||
|
||
def _draw_close_btn(back: uint32*, bw: int, bh: int, bx: int, by: int, btn_w: int, btn_h: int, hover_alpha: int, style: int, clip_wx: int, clip_wy: int, clip_ww: int, clip_r: int):
|
||
x_col: uint32 = gfx.COLOR_RGB(200, 200, 200)
|
||
if style == STYLE_CLASSIC:
|
||
bg: uint32 = gfx.COLOR_RGB(40, 40, 70)
|
||
if hover_alpha > 0:
|
||
red: uint32 = gfx.COLOR_RGB(200, 50, 50)
|
||
bg = _alpha_blend(bg, red, hover_alpha)
|
||
_fill_rect(back, bw, bh, bx, by, btn_w, btn_h, bg)
|
||
if hover_alpha > 128:
|
||
x_col = gfx.COLOR_RGB(255, 255, 255)
|
||
elif style == STYLE_WIN7:
|
||
br: int = CLOSE_BTN_W7_R
|
||
bg_r: int = 200
|
||
bg_g: int = 50
|
||
bg_b: int = 50
|
||
if hover_alpha > 0:
|
||
bg_r = 200 + ((232 - 200) * hover_alpha) / 255
|
||
bg_g = 50 + ((17 - 50) * hover_alpha) / 255
|
||
bg_b = 50 + ((35 - 50) * hover_alpha) / 255
|
||
bg_col7: uint32 = gfx.COLOR_RGB(bg_r, bg_g, bg_b)
|
||
for py2 in range(by, by + btn_h):
|
||
if py2 >= 0 and py2 < bh:
|
||
for px2 in range(bx, bx + btn_w):
|
||
if px2 >= 0 and px2 < bw:
|
||
lrx: int = px2 - bx
|
||
lry: int = py2 - by
|
||
if lry < br:
|
||
if lrx < br:
|
||
if (lrx - br) * (lrx - br) + (lry - br) * (lry - br) > br * br:
|
||
continue
|
||
elif lrx >= btn_w - br:
|
||
if (lrx - (btn_w - br - 1)) * (lrx - (btn_w - br - 1)) + (lry - br) * (lry - br) > br * br:
|
||
continue
|
||
elif lry >= btn_h - br:
|
||
if lrx < br:
|
||
if (lrx - br) * (lrx - br) + (lry - (btn_h - br - 1)) * (lry - (btn_h - br - 1)) > br * br:
|
||
continue
|
||
elif lrx >= btn_w - br:
|
||
if (lrx - (btn_w - br - 1)) * (lrx - (btn_w - br - 1)) + (lry - (btn_h - br - 1)) * (lry - (btn_h - br - 1)) > br * br:
|
||
continue
|
||
if clip_r > 0:
|
||
crx: int = px2 - clip_wx
|
||
cry: int = py2 - clip_wy
|
||
if cry < clip_r and crx >= clip_ww - clip_r:
|
||
cdx: int = crx - (clip_ww - clip_r - 1)
|
||
cdy: int = cry - clip_r
|
||
if cdx * cdx + cdy * cdy > clip_r * clip_r:
|
||
continue
|
||
back[py2 * bw + px2] = bg_col7
|
||
x_col = gfx.COLOR_RGB(255, 255, 255)
|
||
if hover_alpha > 0:
|
||
x_col = _alpha_blend(gfx.COLOR_RGB(255, 255, 255), gfx.COLOR_RGB(255, 240, 240), hover_alpha)
|
||
elif style == STYLE_WINXP:
|
||
br_xp: int = 3
|
||
bg_r_xp: int = 200
|
||
bg_g_xp: int = 50
|
||
bg_b_xp: int = 50
|
||
if hover_alpha > 0:
|
||
bg_r_xp = 200 + ((232 - 200) * hover_alpha) / 255
|
||
bg_g_xp = 50 + ((17 - 50) * hover_alpha) / 255
|
||
bg_b_xp = 50 + ((35 - 50) * hover_alpha) / 255
|
||
bg_col_xp: uint32 = gfx.COLOR_RGB(bg_r_xp, bg_g_xp, bg_b_xp)
|
||
for py_xp in range(by, by + btn_h):
|
||
if py_xp >= 0 and py_xp < bh:
|
||
for px_xp in range(bx, bx + btn_w):
|
||
if px_xp >= 0 and px_xp < bw:
|
||
lrx_xp: int = px_xp - bx
|
||
lry_xp: int = py_xp - by
|
||
if lry_xp < br_xp:
|
||
if lrx_xp < br_xp:
|
||
if (lrx_xp - br_xp) * (lrx_xp - br_xp) + (lry_xp - br_xp) * (lry_xp - br_xp) > br_xp * br_xp:
|
||
continue
|
||
elif lrx_xp >= btn_w - br_xp:
|
||
if (lrx_xp - (btn_w - br_xp - 1)) * (lrx_xp - (btn_w - br_xp - 1)) + (lry_xp - br_xp) * (lry_xp - br_xp) > br_xp * br_xp:
|
||
continue
|
||
elif lry_xp >= btn_h - br_xp:
|
||
if lrx_xp < br_xp:
|
||
if (lrx_xp - br_xp) * (lrx_xp - br_xp) + (lry_xp - (btn_h - br_xp - 1)) * (lry_xp - (btn_h - br_xp - 1)) > br_xp * br_xp:
|
||
continue
|
||
elif lrx_xp >= btn_w - br_xp:
|
||
if (lrx_xp - (btn_w - br_xp - 1)) * (lrx_xp - (btn_w - br_xp - 1)) + (lry_xp - (btn_h - br_xp - 1)) * (lry_xp - (btn_h - br_xp - 1)) > br_xp * br_xp:
|
||
continue
|
||
back[py_xp * bw + px_xp] = bg_col_xp
|
||
x_col = gfx.COLOR_RGB(255, 255, 255)
|
||
if hover_alpha > 0:
|
||
x_col = _alpha_blend(gfx.COLOR_RGB(255, 255, 255), gfx.COLOR_RGB(255, 240, 240), hover_alpha)
|
||
else:
|
||
if hover_alpha > 0:
|
||
red = gfx.COLOR_RGB(232, 17, 35)
|
||
for py3 in range(by, by + btn_h):
|
||
if py3 >= 0 and py3 < bh:
|
||
for px3 in range(bx, bx + btn_w):
|
||
if px3 >= 0 and px3 < bw:
|
||
if clip_r > 0:
|
||
crx3: int = px3 - clip_wx
|
||
cry3: int = py3 - clip_wy
|
||
if cry3 < clip_r and crx3 >= clip_ww - clip_r:
|
||
cdx3: int = crx3 - (clip_ww - clip_r - 1)
|
||
cdy3: int = cry3 - clip_r
|
||
if cdx3 * cdx3 + cdy3 * cdy3 > clip_r * clip_r:
|
||
continue
|
||
bg_pixel: uint32 = back[py3 * bw + px3]
|
||
back[py3 * bw + px3] = _alpha_blend(bg_pixel, red, hover_alpha)
|
||
x_col = gfx.COLOR_RGB(150, 150, 150)
|
||
if hover_alpha > 0:
|
||
x_col = _alpha_blend(gfx.COLOR_RGB(150, 150, 150), gfx.COLOR_RGB(255, 255, 255), hover_alpha)
|
||
cx: int = bx + btn_w // 2
|
||
cy: int = by + btn_h // 2
|
||
d: int = 3
|
||
if btn_w >= 30:
|
||
d = 4
|
||
i: int = -d
|
||
while i <= d:
|
||
px1: int = cx + i
|
||
py1: int = cy + i
|
||
px2b: int = cx + i
|
||
py2b: int = cy - i
|
||
if px1 >= 0 and px1 < bw and py1 >= 0 and py1 < bh:
|
||
if clip_r > 0:
|
||
crx1: int = px1 - clip_wx
|
||
cry1: int = py1 - clip_wy
|
||
if cry1 < clip_r and crx1 >= clip_ww - clip_r:
|
||
cdx1: int = crx1 - (clip_ww - clip_r - 1)
|
||
cdy1: int = cry1 - clip_r
|
||
if cdx1 * cdx1 + cdy1 * cdy1 > clip_r * clip_r:
|
||
i += 1
|
||
continue
|
||
back[py1 * bw + px1] = x_col
|
||
if px2b >= 0 and px2b < bw and py2b >= 0 and py2b < bh:
|
||
if clip_r > 0:
|
||
crx2: int = px2b - clip_wx
|
||
cry2: int = py2b - clip_wy
|
||
if cry2 < clip_r and crx2 >= clip_ww - clip_r:
|
||
cdx2: int = crx2 - (clip_ww - clip_r - 1)
|
||
cdy2: int = cry2 - clip_r
|
||
if cdx2 * cdx2 + cdy2 * cdy2 > clip_r * clip_r:
|
||
i += 1
|
||
continue
|
||
back[py2b * bw + px2b] = x_col
|
||
i += 1
|
||
|
||
def _draw_app_window(back: uint32*, win: AppWindow*):
|
||
w: int = _screen_w
|
||
h: int = _screen_h
|
||
wx: int = win.x
|
||
wy: int = win.y
|
||
ww: int = _eff_w(win)
|
||
wh: int = win.h
|
||
ch: int = _eff_h(win)
|
||
style: int = win.style
|
||
th: int = _get_title_height(style)
|
||
|
||
if wy + wh <= _dirty_y_min or wy > _dirty_y_max:
|
||
return
|
||
|
||
top_idx: int = _cached_top_win
|
||
is_active: int = 0
|
||
if top_idx >= 0:
|
||
top_z: int = _wins[top_idx].z_order
|
||
if win.z_order == top_z:
|
||
is_active = 1
|
||
|
||
title_col: uint32 = gfx.COLOR_RGB(50, 50, 90)
|
||
border_col: uint32 = gfx.COLOR_RGB(100, 100, 180)
|
||
content_col: uint32 = gfx.COLOR_RGB(30, 30, 50)
|
||
outline_col: uint32 = gfx.COLOR_RGB(100, 100, 180)
|
||
title_alpha: int = 255
|
||
has_separator: int = 1
|
||
if style == STYLE_WIN10:
|
||
has_separator = 0
|
||
if is_active:
|
||
title_col = gfx.COLOR_RGB(0, 0, 0)
|
||
border_col = gfx.COLOR_RGB(0, 0, 0)
|
||
content_col = gfx.COLOR_RGB(32, 32, 32)
|
||
outline_col = gfx.COLOR_RGB(80, 80, 80)
|
||
else:
|
||
title_col = gfx.COLOR_RGB(32, 32, 32)
|
||
border_col = gfx.COLOR_RGB(32, 32, 32)
|
||
content_col = gfx.COLOR_RGB(24, 24, 24)
|
||
outline_col = gfx.COLOR_RGB(60, 60, 60)
|
||
elif style == STYLE_WIN11:
|
||
has_separator = 0
|
||
if is_active:
|
||
title_col = gfx.COLOR_RGB(32, 32, 32)
|
||
border_col = gfx.COLOR_RGB(32, 32, 32)
|
||
content_col = gfx.COLOR_RGB(32, 32, 32)
|
||
outline_col = gfx.COLOR_RGB(70, 70, 70)
|
||
else:
|
||
title_col = gfx.COLOR_RGB(40, 40, 40)
|
||
border_col = gfx.COLOR_RGB(40, 40, 40)
|
||
content_col = gfx.COLOR_RGB(40, 40, 40)
|
||
outline_col = gfx.COLOR_RGB(60, 60, 60)
|
||
elif style == STYLE_WIN7:
|
||
has_separator = 0
|
||
if is_active:
|
||
title_col = gfx.COLOR_RGB(156, 181, 209)
|
||
border_col = gfx.COLOR_RGB(156, 181, 209)
|
||
content_col = gfx.COLOR_RGB(240, 240, 240)
|
||
outline_col = gfx.COLOR_RGB(100, 130, 170)
|
||
else:
|
||
title_col = gfx.COLOR_RGB(180, 195, 210)
|
||
border_col = gfx.COLOR_RGB(180, 195, 210)
|
||
content_col = gfx.COLOR_RGB(240, 240, 240)
|
||
outline_col = gfx.COLOR_RGB(140, 155, 175)
|
||
title_alpha = 102
|
||
elif style == STYLE_WINXP:
|
||
has_separator = 0
|
||
if is_active:
|
||
title_col = gfx.COLOR_RGB(107, 158, 212)
|
||
border_col = gfx.COLOR_RGB(107, 158, 212)
|
||
content_col = gfx.COLOR_RGB(235, 235, 235)
|
||
outline_col = gfx.COLOR_RGB(43, 87, 151)
|
||
else:
|
||
title_col = gfx.COLOR_RGB(140, 160, 195)
|
||
border_col = gfx.COLOR_RGB(140, 160, 195)
|
||
content_col = gfx.COLOR_RGB(235, 235, 235)
|
||
outline_col = gfx.COLOR_RGB(100, 115, 145)
|
||
else:
|
||
if is_active:
|
||
title_col = gfx.COLOR_RGB(70, 70, 130)
|
||
border_col = gfx.COLOR_RGB(130, 130, 220)
|
||
outline_col = gfx.COLOR_RGB(130, 130, 220)
|
||
|
||
rounded: int = 0
|
||
if style == STYLE_WIN11 or style == STYLE_WIN7 or style == STYLE_WINXP:
|
||
rounded = 1
|
||
r: int = WIN_RADIUS
|
||
|
||
_draw_window_shadow(back, w, h, wx, wy, ww, wh, rounded, r)
|
||
|
||
if rounded:
|
||
for py in range(wy, wy + wh):
|
||
if py < 0 or py >= h: continue
|
||
if not _dirty_rows[py]: continue
|
||
ry: int = py - wy
|
||
row_col: uint32 = content_col
|
||
row_is_title: int = 0
|
||
row_is_sep: int = 0
|
||
if ry < th:
|
||
row_is_title = 1
|
||
if style == STYLE_WIN7:
|
||
ratio: int = (ry * 100) / th
|
||
if ratio > 100: ratio = 100
|
||
if is_active:
|
||
gr: int = 156 + ((186 - 156) * ratio) / 100
|
||
gg: int = 181 + ((209 - 181) * ratio) / 100
|
||
gb: int = 209 + ((233 - 209) * ratio) / 100
|
||
else:
|
||
gr = 180 + ((200 - 180) * ratio) / 100
|
||
gg = 195 + ((210 - 195) * ratio) / 100
|
||
gb = 210 + ((225 - 210) * ratio) / 100
|
||
row_col = gfx.COLOR_RGB(gr, gg, gb)
|
||
if ry == 0:
|
||
row_col = _alpha_blend(row_col, gfx.COLOR_RGB(220, 235, 255), 50)
|
||
elif ry == 1:
|
||
row_col = _alpha_blend(row_col, gfx.COLOR_RGB(210, 225, 250), 20)
|
||
if ry == th - 1:
|
||
row_col = _alpha_blend(row_col, gfx.COLOR_RGB(130, 160, 200), 25)
|
||
elif style == STYLE_WINXP:
|
||
ratio = (ry * 100) / th
|
||
if ratio > 100: ratio = 100
|
||
if is_active:
|
||
gr = 107 + ((58 - 107) * ratio) / 100
|
||
gg = 158 + ((124 - 158) * ratio) / 100
|
||
gb = 212 + ((195 - 212) * ratio) / 100
|
||
else:
|
||
gr = 150 + ((115 - 150) * ratio) / 100
|
||
gg = 170 + ((135 - 170) * ratio) / 100
|
||
gb = 205 + ((175 - 205) * ratio) / 100
|
||
row_col = gfx.COLOR_RGB(gr, gg, gb)
|
||
if ry == 0:
|
||
row_col = _alpha_blend(row_col, gfx.COLOR_RGB(200, 225, 255), 50)
|
||
elif ry == 1:
|
||
row_col = _alpha_blend(row_col, gfx.COLOR_RGB(200, 225, 255), 25)
|
||
if ry == th - 1:
|
||
row_col = _alpha_blend(row_col, gfx.COLOR_RGB(100, 160, 230), 40)
|
||
else:
|
||
row_col = title_col
|
||
elif ry == th and has_separator:
|
||
row_is_sep = 1
|
||
row_col = outline_col
|
||
in_corner_zone: int = 0
|
||
if ry < r or ry >= wh - r:
|
||
in_corner_zone = 1
|
||
if not in_corner_zone:
|
||
left_border: int = wx
|
||
right_border: int = wx + ww - 1
|
||
if left_border >= 0 and left_border < w:
|
||
back[py * w + left_border] = outline_col
|
||
if right_border >= 0 and right_border < w:
|
||
back[py * w + right_border] = outline_col
|
||
fill_left: int = wx + 1
|
||
fill_right: int = wx + ww - 1
|
||
if fill_left < 0: fill_left = 0
|
||
if fill_right > w: fill_right = w
|
||
fill_w: int = fill_right - fill_left
|
||
if fill_w > 0:
|
||
if row_is_title and (style == STYLE_WIN7 or style == STYLE_WINXP) and title_alpha < 255:
|
||
for fx in range(fill_left, fill_right):
|
||
bg_p: uint32 = back[py * w + fx]
|
||
back[py * w + fx] = _alpha_blend(bg_p, row_col, title_alpha)
|
||
else:
|
||
_fill_rect(back, w, h, fill_left, py, fill_w, 1, row_col)
|
||
else:
|
||
for px in range(wx, wx + ww):
|
||
if px < 0 or px >= w: continue
|
||
rx: int = px - wx
|
||
in_corner: int = 1
|
||
on_border: int = 0
|
||
if ry < r:
|
||
if rx < r:
|
||
in_corner = _is_in_corner(rx, ry, r, r, r)
|
||
elif rx >= ww - r:
|
||
in_corner = _is_in_corner(rx, ry, ww - r - 1, r, r)
|
||
elif ry >= wh - r:
|
||
if rx < r:
|
||
in_corner = _is_in_corner(rx, ry, r, wh - r - 1, r)
|
||
elif rx >= ww - r:
|
||
in_corner = _is_in_corner(rx, ry, ww - r - 1, wh - r - 1, r)
|
||
if not in_corner:
|
||
on_border = _is_on_border(rx, ry, ww, wh, r)
|
||
if not on_border:
|
||
continue
|
||
col: uint32 = row_col
|
||
is_border_pixel: int = 0
|
||
if on_border or _is_on_border(rx, ry, ww, wh, r):
|
||
col = outline_col
|
||
is_border_pixel = 1
|
||
ca: int = 255
|
||
if ry < r:
|
||
if rx < r:
|
||
ca = _corner_alpha(rx, ry, r, r, r)
|
||
elif rx >= ww - r:
|
||
ca = _corner_alpha(rx, ry, ww - r - 1, r, r)
|
||
elif ry >= wh - r:
|
||
if rx < r:
|
||
ca = _corner_alpha(rx, ry, r, wh - r - 1, r)
|
||
elif rx >= ww - r:
|
||
ca = _corner_alpha(rx, ry, ww - r - 1, wh - r - 1, r)
|
||
if is_border_pixel and ca > 0 and ca < 255:
|
||
ca = 255
|
||
if row_is_title and (style == STYLE_WIN7 or style == STYLE_WINXP) and ry >= 1 and rx >= 1 and rx < ww - 1 and ca == 255 and not is_border_pixel:
|
||
ca = title_alpha
|
||
if ca < 255:
|
||
bg_pixel: uint32 = back[py * w + px]
|
||
col = _alpha_blend(bg_pixel, col, ca)
|
||
back[py * w + px] = col
|
||
else:
|
||
_fill_rect(back, w, h, wx, wy, ww, th, title_col)
|
||
_fill_rect(back, w, h, wx, wy + th + 1, ww, ch, content_col)
|
||
if not has_separator:
|
||
_fill_rect(back, w, h, wx, wy + th, ww, 1, content_col)
|
||
if wx >= 0 and wx < w:
|
||
for py in range(wy, wy + wh):
|
||
if py >= 0 and py < h:
|
||
back[py * w + wx] = border_col
|
||
if wx + ww - 1 >= 0 and wx + ww - 1 < w:
|
||
for py in range(wy, wy + wh):
|
||
if py >= 0 and py < h:
|
||
back[py * w + wx + ww - 1] = border_col
|
||
_fill_rect(back, w, h, wx, wy, ww, 1, border_col)
|
||
_fill_rect(back, w, h, wx, wy + wh - 1, ww, 1, border_col)
|
||
if has_separator:
|
||
_fill_rect(back, w, h, wx, wy + th, ww, 1, border_col)
|
||
|
||
win_fb: uint32* = win.fb
|
||
if win_fb:
|
||
fb_stride: int = win.fb_w
|
||
fbh: int = win.fb_h
|
||
if fb_stride <= 0 or fbh <= 0:
|
||
win_fb = None
|
||
if win_fb:
|
||
draw_rows: int = ch
|
||
if draw_rows > fbh: draw_rows = fbh
|
||
if draw_rows < 0: draw_rows = 0
|
||
content_y: int = wy + th + 1
|
||
fb_total: int = fb_stride * fbh
|
||
for row in range(draw_rows):
|
||
dst_y: int = content_y + row
|
||
if dst_y < 0 or dst_y >= h: continue
|
||
if not _dirty_rows[dst_y]: continue
|
||
ry: int = th + 1 + row
|
||
if rounded:
|
||
if ry >= r and ry < wh - r:
|
||
src_x: int = 1
|
||
dst_x: int = wx + 1
|
||
if dst_x < 0:
|
||
src_x = src_x - dst_x
|
||
dst_x = 0
|
||
end_x: int = wx + ww - 1
|
||
if end_x > w: end_x = w
|
||
actual_w: int = end_x - dst_x
|
||
if actual_w <= 0: continue
|
||
if src_x + actual_w > fb_stride: actual_w = fb_stride - src_x
|
||
if actual_w <= 0: continue
|
||
src_off: int = row * fb_stride + src_x
|
||
if src_off + actual_w > fb_total: continue
|
||
string.memcpy(rawptr(uint64(back) + (dst_y * w + dst_x) * 4), rawptr(uint64(win_fb) + src_off * 4), actual_w * 4)
|
||
else:
|
||
copy_w: int = fb_stride - 1
|
||
if copy_w > ww - 1: copy_w = ww - 1
|
||
for col_px in range(1, copy_w):
|
||
if ry < r:
|
||
if col_px < r:
|
||
if not _is_in_corner(col_px, ry, r, r, r): continue
|
||
elif col_px >= ww - r:
|
||
if not _is_in_corner(col_px, ry, ww - r - 1, r, r): continue
|
||
elif ry >= wh - r:
|
||
if col_px < r:
|
||
if not _is_in_corner(col_px, ry, r, wh - r - 1, r): continue
|
||
elif col_px >= ww - r:
|
||
if not _is_in_corner(col_px, ry, ww - r - 1, wh - r - 1, r): continue
|
||
if _is_on_border(col_px, ry, ww, wh, r): continue
|
||
dst_x2: int = wx + col_px
|
||
if dst_x2 < 0 or dst_x2 >= w: continue
|
||
if col_px >= fb_stride: continue
|
||
px_off: int = row * fb_stride + col_px
|
||
if px_off < 0 or px_off >= fb_total: continue
|
||
back[dst_y * w + dst_x2] = win_fb[px_off]
|
||
else:
|
||
src_x2: int = 1
|
||
dst_x3: int = wx + 1
|
||
if dst_x3 < 0:
|
||
src_x2 = src_x2 - dst_x3
|
||
dst_x3 = 0
|
||
end_x2: int = wx + ww - 1
|
||
if end_x2 > w: end_x2 = w
|
||
actual_w2: int = end_x2 - dst_x3
|
||
if actual_w2 <= 0: continue
|
||
if src_x2 + actual_w2 > fb_stride: actual_w2 = fb_stride - src_x2
|
||
if actual_w2 <= 0: continue
|
||
src_off2: int = row * fb_stride + src_x2
|
||
if src_off2 + actual_w2 > fb_total: continue
|
||
string.memcpy(rawptr(uint64(back) + (dst_y * w + dst_x3) * 4), rawptr(uint64(win_fb) + src_off2 * 4), actual_w2 * 4)
|
||
|
||
btn_w: int = CLOSE_BTN_SIZE
|
||
btn_h: int = CLOSE_BTN_SIZE
|
||
btn_x: int = wx + ww - btn_w - 4
|
||
btn_y: int = wy + (th - btn_h) // 2
|
||
clip_r: int = 0
|
||
if style == STYLE_WIN7:
|
||
btn_w = CLOSE_BTN_W7_W
|
||
btn_h = CLOSE_BTN_W7_H
|
||
btn_x = wx + ww - btn_w - 5
|
||
btn_y = wy + (th - btn_h) // 2
|
||
if rounded:
|
||
clip_r = r
|
||
elif style == STYLE_WINXP:
|
||
btn_w = CLOSE_BTN_XP_SZ
|
||
btn_h = CLOSE_BTN_XP_SZ
|
||
btn_x = wx + ww - btn_w - 5
|
||
btn_y = wy + (th - btn_h) // 2
|
||
elif style == STYLE_WIN10 or style == STYLE_WIN11:
|
||
btn_w = CLOSE_BTN_W_WIN
|
||
btn_h = th - 2
|
||
btn_x = wx + ww - btn_w
|
||
btn_y = wy + 1
|
||
if rounded:
|
||
clip_r = r
|
||
_draw_close_btn(back, w, h, btn_x, btn_y, btn_w, btn_h, win.close_hover, style, wx, wy, ww, clip_r)
|
||
max_bx: int = btn_x - btn_w
|
||
if style == STYLE_WIN7 or style == STYLE_WINXP:
|
||
max_bx = btn_x - btn_w - 1
|
||
_draw_max_btn(back, w, h, max_bx, btn_y, btn_w, btn_h, win.max_hover, style, win.is_maximized)
|
||
min_bx: int = max_bx - btn_w
|
||
if style == STYLE_WIN7 or style == STYLE_WINXP:
|
||
min_bx = max_bx - btn_w - 1
|
||
_draw_min_btn(back, w, h, min_bx, btn_y, btn_w, btn_h, win.min_hover, style)
|
||
|
||
text_col: uint32 = 0xFFFFFFFF
|
||
if not is_active:
|
||
text_col = gfx.COLOR_RGB(170, 170, 170)
|
||
text_y: int = wy + (th - 16) // 2
|
||
_draw_text(back, w, h, wx + 8, text_y, &win.title[0], text_col)
|
||
|
||
def create(x: int, y: int, w: int, h: int, title: const str, style: int) -> int:
|
||
global _win_count, _next_z, _focused_win
|
||
if _win_count >= MAX_APP_WINDOWS: return -1
|
||
idx: int = _win_count
|
||
_win_count += 1
|
||
aw: AppWindow* = &_wins[idx]
|
||
aw.active = 1
|
||
aw.x = x
|
||
aw.y = y
|
||
aw.w = w
|
||
aw.h = h
|
||
aw.content_h = h - _get_title_height(style) - 2
|
||
_next_z += 1
|
||
aw.z_order = _next_z
|
||
aw.style = style
|
||
aw.close_hover = 0
|
||
aw.min_hover = 0
|
||
aw.max_hover = 0
|
||
aw.is_maximized = 0
|
||
aw.orig_x = x
|
||
aw.orig_y = y
|
||
aw.orig_w = w
|
||
aw.orig_h = h
|
||
aw.pid = _current_app_pid
|
||
aw.cursor_type = 0
|
||
aw.app_cursor_type = 0
|
||
aw.fb_w = 0
|
||
aw.fb_h = 0
|
||
aw.old_fb = None
|
||
aw.ping_pending = 0
|
||
aw.no_resp = 0
|
||
aw.no_resp_sec = 0
|
||
aw.minimized = 0
|
||
aw.resizable_w = 1
|
||
aw.resizable_h = 1
|
||
aw.resize_w = 0
|
||
aw.resize_h = 0
|
||
string.memset(&aw.title, 0, 32)
|
||
string.memset(&aw.orig_title, 0, 32)
|
||
for i in range(31):
|
||
ch: char = title[i]
|
||
if ch == 0: break
|
||
aw.title[i] = ch
|
||
aw.orig_title[i] = ch
|
||
aw.fb = mm.malloc(w * aw.content_h * 4)
|
||
aw.fb_w = w
|
||
aw.fb_h = aw.content_h
|
||
if aw.fb:
|
||
bg_col: uint32 = gfx.COLOR_RGB(25, 25, 35)
|
||
fb_size: int = w * aw.content_h
|
||
for fi in range(fb_size):
|
||
aw.fb[fi] = bg_col
|
||
_mark_rows_dirty(y, h)
|
||
_focused_win = idx
|
||
return idx
|
||
|
||
def draw_text(win_id: int, x: int, y: int, text: const str, color: uint32) -> int:
|
||
if win_id < 0 or win_id >= _win_count: return -1
|
||
aw: AppWindow* = &_wins[win_id]
|
||
if not aw.active: return -1
|
||
win_fb: uint32* = aw.fb
|
||
if not win_fb: return -1
|
||
fbw: int = aw.fb_w
|
||
fbh: int = aw.fb_h
|
||
_draw_text(win_fb, fbw, fbh, x, y, text, color)
|
||
_mark_rows_dirty(aw.y + _get_title_height(aw.style) + 1 + y, 16)
|
||
return 0
|
||
|
||
def set_app_pid(pid: int):
|
||
global _current_app_pid
|
||
_current_app_pid = pid
|
||
|
||
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(win_id: int) -> uint32*:
|
||
if win_id < 0 or win_id >= _win_count: return None
|
||
aw: AppWindow* = &_wins[win_id]
|
||
if not aw.active: return None
|
||
if aw.old_fb:
|
||
mm.free(aw.old_fb)
|
||
aw.old_fb = None
|
||
if aw.resize_w != 0 and (aw.fb_w != aw.resize_w or aw.fb_h != aw.resize_h):
|
||
target_w: int = aw.resize_w
|
||
target_h: int = aw.resize_h
|
||
if target_w <= 0 or target_h <= 0:
|
||
aw.resize_w = 0
|
||
aw.resize_h = 0
|
||
return aw.fb
|
||
new_fb_sz_gf: int = target_w * target_h
|
||
new_fb_gf: uint32* = mm.malloc(new_fb_sz_gf * 4)
|
||
if not new_fb_gf:
|
||
aw.resize_w = 0
|
||
aw.resize_h = 0
|
||
return aw.fb
|
||
old_fb_gf: uint32* = aw.fb
|
||
old_fbw_gf: int = aw.fb_w
|
||
old_fbh_gf: int = aw.fb_h
|
||
aw.fb = new_fb_gf
|
||
aw.fb_w = target_w
|
||
aw.fb_h = target_h
|
||
aw.w = target_w
|
||
aw.content_h = target_h
|
||
aw.resize_w = 0
|
||
aw.resize_h = 0
|
||
bg_gf: uint32 = gfx.COLOR_RGB(30, 30, 50)
|
||
for fi_gf in range(new_fb_sz_gf):
|
||
aw.fb[fi_gf] = bg_gf
|
||
if old_fb_gf:
|
||
cph_gf: int = old_fbh_gf
|
||
if cph_gf > target_h: cph_gf = target_h
|
||
for ri_gf in range(cph_gf):
|
||
rb_gf: int = old_fbw_gf
|
||
if rb_gf > target_w: rb_gf = target_w
|
||
string.memcpy(rawptr(uint64(aw.fb) + ri_gf * target_w * 4), rawptr(uint64(old_fb_gf) + ri_gf * old_fbw_gf * 4), rb_gf * 4)
|
||
if aw.old_fb:
|
||
mm.free(aw.old_fb)
|
||
aw.old_fb = old_fb_gf
|
||
return aw.fb
|
||
|
||
def get_size(win_id: int) -> int:
|
||
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 << 16) | (aw.fb_h & 0xFFFF)
|
||
|
||
_NO_RESP_SUFFIX: [14]char = " - (No Resp)"
|
||
|
||
def _update_no_resp_title(win_id: int, mark: int):
|
||
if win_id < 0 or win_id >= _win_count: return
|
||
aw: AppWindow* = &_wins[win_id]
|
||
if not aw.active: return
|
||
string.memset(&aw.title, 0, 32)
|
||
if mark:
|
||
j: int = 0
|
||
for ch in aw.orig_title:
|
||
if j >= 31: break
|
||
if ch == 0: break
|
||
aw.title[j] = ch
|
||
j += 1
|
||
for sc in _NO_RESP_SUFFIX:
|
||
if j >= 13: break
|
||
if sc == 0: break
|
||
if j < 31:
|
||
aw.title[j] = sc
|
||
j += 1
|
||
else:
|
||
j2: int = 0
|
||
for ch2 in aw.orig_title:
|
||
if j2 >= 31: break
|
||
if ch2 == 0: break
|
||
aw.title[j2] = ch2
|
||
j2 += 1
|
||
_mark_rows_dirty(aw.y, _get_title_height(aw.style) + 2)
|
||
|
||
def _heartbeat_check():
|
||
global _hb_last_sec
|
||
cur_sec: uint32 = timer.timer_get_seconds()
|
||
if cur_sec - _hb_last_sec < HB_INTERVAL_SEC: return
|
||
_hb_last_sec = cur_sec
|
||
for i in range(_win_count):
|
||
aw: AppWindow* = &_wins[i]
|
||
if not aw.active:
|
||
continue
|
||
if aw.pid == 0:
|
||
continue
|
||
if aw.ping_pending:
|
||
if not aw.no_resp:
|
||
aw.no_resp = 1
|
||
aw.no_resp_sec = cur_sec
|
||
_update_no_resp_title(i, 1)
|
||
else:
|
||
elapsed: uint32 = cur_sec - aw.no_resp_sec
|
||
if elapsed >= HB_NO_RESP_SEC:
|
||
aw.active = 0
|
||
_mark_rows_dirty(aw.y, aw.h)
|
||
p_ref: process.Process* = process.ProcessManager.get_process(aw.pid)
|
||
if p_ref is not None:
|
||
p_ref.exit()
|
||
else:
|
||
aw.ping_pending = 1
|
||
evt: InputEvent
|
||
string.memset(&evt, 0, INPUT_EVENT_SIZE)
|
||
evt.type = EVENT_TYPE_PING
|
||
evt.win_id = i
|
||
_push_event(evt)
|
||
|
||
def flush(win_id: int):
|
||
if win_id < 0 or win_id >= _win_count: return
|
||
aw: AppWindow* = &_wins[win_id]
|
||
if not aw.active: return
|
||
if aw.old_fb:
|
||
mm.free(aw.old_fb)
|
||
aw.old_fb = None
|
||
if aw.ping_pending:
|
||
aw.ping_pending = 0
|
||
if aw.no_resp:
|
||
aw.no_resp = 0
|
||
aw.no_resp_sec = 0
|
||
_update_no_resp_title(win_id, 0)
|
||
_mark_rows_dirty(aw.y + _get_title_height(aw.style) + 1, _eff_h(aw))
|
||
|
||
def set_title(win_id: int, title: const str) -> int:
|
||
if win_id < 0 or win_id >= _win_count: return -1
|
||
aw: AppWindow* = &_wins[win_id]
|
||
if not aw.active: return -1
|
||
string.memset(&aw.title, 0, 32)
|
||
string.memset(&aw.orig_title, 0, 32)
|
||
for i in range(31):
|
||
ch: char = title[i]
|
||
if ch == 0: break
|
||
aw.title[i] = ch
|
||
aw.orig_title[i] = ch
|
||
_mark_rows_dirty(aw.y, _get_title_height(aw.style) + 2)
|
||
return 0
|
||
|
||
def set_geometry(win_id: int, x: int, y: int, w: int, h: int) -> int:
|
||
if win_id < 0 or win_id >= _win_count: return -1
|
||
aw: AppWindow* = &_wins[win_id]
|
||
if not aw.active: return -1
|
||
old_y: int = aw.y
|
||
old_h: int = aw.h
|
||
aw.x = x
|
||
aw.y = y
|
||
aw.resize_w = w
|
||
aw.resize_h = h - _get_title_height(aw.style) - 2
|
||
aw.h = h
|
||
_mark_rows_dirty(old_y, old_h)
|
||
_mark_rows_dirty(y, h)
|
||
return 0
|
||
|
||
def set_resizable(win_id: int, rw: int, rh: int) -> int:
|
||
if win_id < 0 or win_id >= _win_count: return -1
|
||
aw: AppWindow* = &_wins[win_id]
|
||
if not aw.active: return -1
|
||
aw.resizable_w = rw
|
||
aw.resizable_h = rh
|
||
return 0
|
||
|
||
def destroy_by_pid(pid: int):
|
||
global _focused_win, _last_hover_win
|
||
for i in range(_win_count):
|
||
if _wins[i].pid == pid and _wins[i].active:
|
||
_wins[i].active = 0
|
||
_mark_rows_dirty(_wins[i].y, _wins[i].h)
|
||
if _focused_win == i:
|
||
_focused_win = -1
|
||
if _last_hover_win == i:
|
||
_last_hover_win = -1
|
||
if _focused_win < 0:
|
||
for j in range(_win_count - 1, -1, -1):
|
||
if _wins[j].active and not _wins[j].minimized:
|
||
_focused_win = j
|
||
break
|