import vpsdk.window as window import vpsdk.process as process import vpsdk.syscall as _syscall from stdint import * import t, c import string TITLE_H: t.CDefine = 32 MAX_WIDGETS: t.CDefine = 24 CHAR_W: t.CDefine = 8 CHAR_H: t.CDefine = 16 WIDGET_LABEL: t.CDefine = 1 WIDGET_BUTTON: t.CDefine = 2 WIDGET_ENTRY: t.CDefine = 3 WIDGET_TEXT: t.CDefine = 4 WIDGET_SCROLLBAR: t.CDefine = 5 WIDGET_FBCANVAS: t.CDefine = 6 VERTICAL: t.CDefine = 1 HORIZONTAL: t.CDefine = 2 TEXT_MAX_LINES: t.CDefine = 200 PACK_TOP: t.CDefine = 1 PACK_BOTTOM: t.CDefine = 2 PACK_LEFT: t.CDefine = 3 PACK_RIGHT: t.CDefine = 4 FILL_NONE: t.CDefine = 0 FILL_X: t.CDefine = 1 FILL_Y: t.CDefine = 2 FILL_BOTH: t.CDefine = 3 STICKY_NONE: t.CDefine = 0 STICKY_W: t.CDefine = 1 STICKY_E: t.CDefine = 2 STICKY_N: t.CDefine = 4 STICKY_S: t.CDefine = 8 STICKY_NSEW: t.CDefine = 15 _g_wl_type: t.CArray[t.CInt, MAX_WIDGETS] _g_wl_side: t.CArray[t.CInt, MAX_WIDGETS] _g_wl_fill: t.CArray[t.CInt, MAX_WIDGETS] _g_wl_expand: t.CArray[t.CInt, MAX_WIDGETS] _g_wl_padx: t.CArray[t.CInt, MAX_WIDGETS] _g_wl_pady: t.CArray[t.CInt, MAX_WIDGETS] _g_wl_row: t.CArray[t.CInt, MAX_WIDGETS] _g_wl_col: t.CArray[t.CInt, MAX_WIDGETS] _g_wl_rowspan: t.CArray[t.CInt, MAX_WIDGETS] _g_wl_colspan: t.CArray[t.CInt, MAX_WIDGETS] _g_wl_sticky: t.CArray[t.CInt, MAX_WIDGETS] _g_layout_init: t.CInt _hex_tbl: t.CArray[t.CChar, 17] = "0123456789ABCDEF" _hex_buf: t.CArray[t.CChar, 20] = "0000000000000000000" def _serial_puts(s: t.CConst | t.CChar | t.CPtr) -> t.CVoid: _syscall._syscall1(t.CUInt64T(_syscall.SERIAL_PUTS), t.CUInt64T(s)) def _ptr_to_hex(val: t.CUInt64T, buf: t.CVoid | t.CPtr) -> t.CVoid: buf[0] = 48 buf[1] = 120 i: t.CInt = 15 while i >= 0: nibble: t.CInt = t.CInt(val & t.CUInt64T(0xF)) buf[2 + i] = _hex_tbl[nibble] val = val >> 4 i -= 1 buf[18] = 0 def _fill_rect(fb: UINT32PTR, fw: t.CInt, fh: t.CInt, x1: t.CInt, y1: t.CInt, w: t.CInt, h: t.CInt, color: t.CUInt32T): if not fb: return if x1 < 0: w += x1; x1 = 0 if y1 < 0: h += y1; y1 = 0 if w <= 0 or h <= 0: return for py in range(y1, y1 + h): if py >= 0 and py < fh: for px in range(x1, x1 + w): if px >= 0 and px < fw: fb[py * fw + px] = color def _draw_hline(fb: UINT32PTR, fw: t.CInt, fh: t.CInt, x1: t.CInt, y: t.CInt, w: t.CInt, color: t.CUInt32T): if not fb: return if y < 0 or y >= fh: return for px in range(x1, x1 + w): if px >= 0 and px < fw: fb[y * fw + px] = color def _draw_vline(fb: UINT32PTR, fw: t.CInt, fh: t.CInt, x: t.CInt, y1: t.CInt, h: t.CInt, color: t.CUInt32T): if not fb: return if x < 0 or x >= fw: return for py in range(y1, y1 + h): if py >= 0 and py < fh: fb[py * fw + x] = color def _lighten(color: t.CUInt32T, amount: t.CInt) -> t.CUInt32T: r: t.CInt = t.CInt(color & 0xFF) g: t.CInt = t.CInt((color >> 8) & 0xFF) b: t.CInt = t.CInt((color >> 16) & 0xFF) r = r + ((255 - r) * amount) / 256 g = g + ((255 - g) * amount) / 256 b = b + ((255 - b) * amount) / 256 return t.CUInt32T(r | (g << 8) | (b << 16)) def _darken(color: t.CUInt32T, amount: t.CInt) -> t.CUInt32T: r: t.CInt = t.CInt(color & 0xFF) g: t.CInt = t.CInt((color >> 8) & 0xFF) b: t.CInt = t.CInt((color >> 16) & 0xFF) r = r - (r * amount) / 256 g = g - (g * amount) / 256 b = b - (b * amount) / 256 return t.CUInt32T(r | (g << 8) | (b << 16)) font: t.CArray[t.CArray[t.CUInt8T, 16], 256] = [ {0x00,0x00,0x00,0x00,0x00,0x40,0x55,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x00,0x00,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x00,0x02,0x56,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x00,0x42,0x56,0x42,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x70,0x55,0x55,0x57,0x71,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x70,0x57,0x54,0x57,0x71,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x70,0x57,0x54,0x57,0x75,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x70,0x57,0x51,0x52,0x72,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x70,0x57,0x55,0x57,0x75,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x70,0x57,0x55,0x57,0x71,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x70,0x56,0x51,0x57,0x75,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x70,0x54,0x54,0x57,0x75,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x70,0x50,0x50,0x57,0x74,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x70,0x51,0x51,0x57,0x75,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x70,0x57,0x55,0x57,0x74,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x70,0x53,0x54,0x56,0x74,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x10,0x37,0x15,0x15,0x15,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x10,0x32,0x16,0x12,0x12,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x20,0x6E,0x22,0x2E,0x28,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x10,0x37,0x11,0x13,0x11,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x10,0x35,0x15,0x17,0x11,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x10,0x37,0x14,0x17,0x11,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x10,0x37,0x14,0x17,0x15,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x10,0x37,0x11,0x12,0x12,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x10,0x37,0x15,0x17,0x15,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x10,0x37,0x15,0x17,0x11,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x10,0x36,0x11,0x17,0x15,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x10,0x34,0x14,0x17,0x15,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x10,0x30,0x10,0x17,0x14,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x10,0x31,0x11,0x17,0x15,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x10,0x37,0x15,0x17,0x14,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x10,0x33,0x14,0x16,0x14,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x18,0x18,0x00,0x00,0x00}, {0x6C,0x6C,0x6C,0x6C,0x48,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x36,0x36,0x36,0x7F,0x7F,0x36,0x36,0x36,0x7F,0x7F,0x36,0x36,0x36,0x00,0x00,0x00}, {0x0C,0x0C,0x3E,0x7F,0x68,0x68,0x3E,0x0B,0x0B,0x7F,0x3E,0x18,0x18,0x00,0x00,0x00}, {0x60,0x60,0x66,0x06,0x0C,0x0C,0x18,0x30,0x30,0x60,0x66,0x06,0x06,0x00,0x00,0x00}, {0x38,0x6C,0x6C,0x6C,0x6C,0x6C,0x38,0x6C,0x6D,0x66,0x66,0x6F,0x3B,0x00,0x00,0x00}, {0x18,0x18,0x18,0x18,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x1C,0x3C,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3C,0x1C,0x00,0x00}, {0x38,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x38,0x00,0x00}, {0x00,0x00,0x18,0x18,0x7E,0x3C,0x3C,0x3C,0x7E,0x18,0x18,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x00,0x18,0x18,0x18,0x7E,0x7E,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x18,0x30,0x00}, {0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x00,0x00,0x00}, {0x00,0x00,0x06,0x06,0x0C,0x0C,0x18,0x30,0x30,0x60,0x60,0x00,0x00,0x00,0x00,0x00}, {0x3C,0x7E,0x66,0x66,0x6E,0x6E,0x7E,0x76,0x76,0x66,0x66,0x7E,0x3C,0x00,0x00,0x00}, {0x18,0x38,0x78,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x7E,0x00,0x00,0x00}, {0x3C,0x7E,0x66,0x06,0x06,0x0E,0x1C,0x38,0x70,0x60,0x60,0x7E,0x7E,0x00,0x00,0x00}, {0x3C,0x7E,0x66,0x06,0x06,0x1C,0x1C,0x06,0x06,0x06,0x66,0x7E,0x3C,0x00,0x00,0x00}, {0x0C,0x0C,0x1C,0x1C,0x3C,0x2C,0x6C,0x7E,0x7E,0x0C,0x0C,0x0C,0x0C,0x00,0x00,0x00}, {0x7E,0x7E,0x60,0x60,0x7C,0x7E,0x06,0x06,0x06,0x06,0x66,0x7E,0x3C,0x00,0x00,0x00}, {0x3C,0x7E,0x66,0x60,0x60,0x7C,0x7E,0x66,0x66,0x66,0x66,0x7E,0x3C,0x00,0x00,0x00}, {0x7E,0x7E,0x06,0x06,0x0C,0x0C,0x18,0x18,0x30,0x30,0x30,0x30,0x30,0x00,0x00,0x00}, {0x3C,0x7E,0x66,0x66,0x66,0x7E,0x3C,0x66,0x66,0x66,0x66,0x7E,0x3C,0x00,0x00,0x00}, {0x3C,0x7E,0x66,0x66,0x66,0x7E,0x3E,0x06,0x06,0x06,0x66,0x7E,0x3C,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x18,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x18,0x18,0x30,0x00}, {0x00,0x02,0x06,0x0C,0x18,0x30,0x60,0x60,0x30,0x18,0x0C,0x06,0x02,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x7E,0x7E,0x00,0x00,0x7E,0x7E,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x40,0x60,0x30,0x18,0x0C,0x06,0x06,0x0C,0x18,0x30,0x60,0x40,0x00,0x00,0x00}, {0x3C,0x7E,0x66,0x06,0x0C,0x0C,0x18,0x18,0x18,0x00,0x00,0x18,0x18,0x00,0x00,0x00}, {0x3C,0x7E,0x66,0x66,0x6E,0x6A,0x6A,0x6A,0x6E,0x60,0x60,0x7C,0x3C,0x00,0x00,0x00}, {0x3C,0x7E,0x66,0x66,0x66,0x7E,0x7E,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00}, {0x7C,0x7E,0x66,0x66,0x66,0x7C,0x7C,0x66,0x66,0x66,0x66,0x7E,0x7C,0x00,0x00,0x00}, {0x3C,0x7E,0x66,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x66,0x7E,0x3C,0x00,0x00,0x00}, {0x78,0x7C,0x6C,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x6C,0x7C,0x78,0x00,0x00,0x00}, {0x7E,0x7E,0x60,0x60,0x60,0x7C,0x7C,0x60,0x60,0x60,0x60,0x7E,0x7E,0x00,0x00,0x00}, {0x7E,0x7E,0x60,0x60,0x60,0x7C,0x7C,0x60,0x60,0x60,0x60,0x60,0x60,0x00,0x00,0x00}, {0x3C,0x7E,0x66,0x60,0x60,0x60,0x6E,0x66,0x66,0x66,0x66,0x7E,0x3C,0x00,0x00,0x00}, {0x66,0x66,0x66,0x66,0x66,0x7E,0x7E,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00}, {0x7E,0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x7E,0x00,0x00,0x00}, {0x3E,0x3E,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x6C,0x7C,0x38,0x00,0x00,0x00}, {0x66,0x66,0x66,0x6C,0x6C,0x78,0x78,0x78,0x6C,0x6C,0x66,0x66,0x66,0x00,0x00,0x00}, {0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x7E,0x00,0x00,0x00}, {0x63,0x77,0x7F,0x7F,0x6B,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00}, {0x66,0x66,0x66,0x66,0x76,0x76,0x7E,0x6E,0x6E,0x66,0x66,0x66,0x66,0x00,0x00,0x00}, {0x3C,0x7E,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x7E,0x3C,0x00,0x00,0x00}, {0x7C,0x7E,0x66,0x66,0x66,0x7E,0x7C,0x60,0x60,0x60,0x60,0x60,0x60,0x00,0x00,0x00}, {0x3C,0x7E,0x66,0x66,0x66,0x66,0x66,0x66,0x6A,0x6A,0x6C,0x7E,0x36,0x00,0x00,0x00}, {0x7C,0x7E,0x66,0x66,0x66,0x7E,0x7C,0x6C,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00}, {0x3C,0x7E,0x66,0x60,0x60,0x78,0x3C,0x0E,0x06,0x06,0x66,0x7E,0x3C,0x00,0x00,0x00}, {0x7E,0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00}, {0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x7E,0x3C,0x00,0x00,0x00}, {0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x3C,0x3C,0x18,0x18,0x00,0x00,0x00}, {0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x6B,0x6B,0x7F,0x77,0x77,0x22,0x00,0x00,0x00}, {0x66,0x66,0x66,0x66,0x3C,0x3C,0x18,0x3C,0x3C,0x66,0x66,0x66,0x66,0x00,0x00,0x00}, {0x66,0x66,0x66,0x66,0x7E,0x3C,0x3C,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00}, {0x7E,0x7E,0x06,0x06,0x0C,0x0C,0x18,0x30,0x30,0x60,0x60,0x7E,0x7E,0x00,0x00,0x00}, {0x78,0x78,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x78,0x78,0x00,0x00}, {0xC0,0xC0,0x60,0x60,0x30,0x30,0x18,0x0C,0x0C,0x06,0x06,0x03,0x03,0x00,0x00,0x00}, {0x1E,0x1E,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x1E,0x1E,0x00,0x00}, {0x3C,0x7E,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00}, {0x30,0x38,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x3C,0x3E,0x06,0x3E,0x7E,0x66,0x66,0x7E,0x3E,0x00,0x00,0x00}, {0x60,0x60,0x60,0x60,0x7C,0x7E,0x66,0x66,0x66,0x66,0x66,0x7E,0x7C,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x3C,0x7E,0x66,0x60,0x60,0x60,0x66,0x7E,0x3C,0x00,0x00,0x00}, {0x06,0x06,0x06,0x06,0x3E,0x7E,0x66,0x66,0x66,0x66,0x66,0x7E,0x3E,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x3C,0x7E,0x66,0x7E,0x7E,0x60,0x66,0x7E,0x3C,0x00,0x00,0x00}, {0x1C,0x3E,0x30,0x30,0x30,0x7C,0x7C,0x30,0x30,0x30,0x30,0x30,0x30,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x3E,0x7E,0x66,0x66,0x66,0x7E,0x3E,0x06,0x06,0x3E,0x3C,0x00}, {0x60,0x60,0x60,0x60,0x7C,0x7E,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00}, {0x18,0x18,0x00,0x00,0x38,0x38,0x18,0x18,0x18,0x18,0x18,0x7E,0x7E,0x00,0x00,0x00}, {0x0C,0x0C,0x00,0x00,0x3C,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x7C,0x78,0x00}, {0x60,0x60,0x60,0x60,0x66,0x66,0x6C,0x78,0x78,0x6C,0x6C,0x66,0x66,0x00,0x00,0x00}, {0x38,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x7E,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x36,0x7F,0x6B,0x6B,0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x7C,0x7E,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x3C,0x7E,0x66,0x66,0x66,0x66,0x66,0x7E,0x3C,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x7C,0x7E,0x66,0x66,0x66,0x7E,0x7C,0x60,0x60,0x60,0x60,0x00}, {0x00,0x00,0x00,0x00,0x3E,0x7E,0x66,0x66,0x66,0x7E,0x3E,0x06,0x06,0x07,0x07,0x00}, {0x00,0x00,0x00,0x00,0x6C,0x7E,0x76,0x60,0x60,0x60,0x60,0x60,0x60,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x3C,0x7E,0x62,0x70,0x3C,0x0E,0x46,0x7E,0x3C,0x00,0x00,0x00}, {0x30,0x30,0x30,0x30,0x7C,0x7C,0x30,0x30,0x30,0x30,0x30,0x3C,0x1C,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x7E,0x3E,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x3C,0x3C,0x18,0x18,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x63,0x63,0x63,0x63,0x6B,0x6B,0x7F,0x7F,0x36,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x66,0x66,0x3C,0x3C,0x18,0x3C,0x3C,0x66,0x66,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x7E,0x3E,0x06,0x06,0x7E,0x7C,0x00}, {0x00,0x00,0x00,0x00,0x7E,0x7E,0x0C,0x0C,0x18,0x30,0x30,0x7E,0x7E,0x00,0x00,0x00}, {0x0E,0x1E,0x18,0x18,0x18,0x18,0x30,0x30,0x18,0x18,0x18,0x18,0x1E,0x0E,0x00,0x00}, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00}, {0x70,0x78,0x18,0x18,0x18,0x18,0x0C,0x0C,0x18,0x18,0x18,0x18,0x78,0x70,0x00,0x00}, {0x31,0x79,0x6B,0x4F,0x46,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x70,0x13,0x24,0x26,0x24,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, ] def _draw_char(buf: UINT32PTR, bw: t.CInt, bh: t.CInt, x: t.CInt, y: t.CInt, ch: t.CChar, color: t.CUInt32T): if not buf: return if bw <= 0 or bh <= 0: return ci: t.CUInt8T = t.CUInt8T(ch) if ci >= 128: ci = 0 fy: t.CInt = 0 while fy < CHAR_H: if y + fy < 0 or y + fy >= bh: fy += 1 continue row: t.CUInt8T = font[ci][fy] if row == 0: fy += 1 continue fx: t.CInt = 0 while fx < CHAR_W: if row & (0x80 >> fx): px: t.CInt = x + fx if px >= 0 and px < bw: buf[(y + fy) * bw + px] = color fx += 1 fy += 1 def _draw_text(buf: UINT32PTR, bw: t.CInt, bh: t.CInt, x: t.CInt, y: t.CInt, s: t.CConst | str, color: t.CUInt32T): if not buf: return if bw <= 0 or bh <= 0: return cx: t.CInt = x cy: t.CInt = y i: t.CInt = 0 while True: ch: t.CChar = s[i] if ch == 0: break if ch == 10: cy += 16 cx = x else: _draw_char(buf, bw, bh, cx, cy, ch, color) cx += 8 i += 1 def _register_widget(master: t.CVoid | t.CPtr, widget: Widget | t.CPtr) -> t.CInt: tk: Tk | t.CPtr = t.CType(master, Tk, t.CPtr) idx: t.CInt = tk.widget_count if idx < MAX_WIDGETS: tk.widgets[idx] = widget tk.widget_count = idx + 1 return idx @t.Object @t.CVTable class Widget: x: t.CInt y: t.CInt w: t.CInt h: t.CInt visible: t.CInt master: t.CVoid | t.CPtr widget_type: t.CInt focused: t.CInt hovered: t.CInt def draw(self, fb: UINT32PTR, fw: t.CInt, fh: t.CInt): pass def handle_event(self, evt: window.InputEvent | t.CPtr) -> t.CInt: return 0 def contains(self, px: t.CInt, py: t.CInt) -> t.CInt: if px >= self.x and px < self.x + self.w and py >= self.y and py < self.y + self.h: return 1 return 0 def place(self, x: t.CInt = 0, y: t.CInt = 0, w: t.CInt = 0, h: t.CInt = 0): self.x = x self.y = y if w > 0: self.w = w if h > 0: self.h = h @t.Object @t.CVTable class Label(Widget): text: t.CArray[t.CChar, 64] color: t.CUInt32T def __init__(self, master: t.CVoid | t.CPtr, text: str, color: t.CUInt32T): self.x = 0 self.y = 0 self.w = 0 self.h = 0 self.visible = 1 self.master = master self.widget_type = WIDGET_LABEL self.focused = 0 self.hovered = 0 self.color = color i: t.CInt = 0 for ch in text: if i >= 63: break self.text[i] = ch i += 1 self.text[i] = 0 _register_widget(master, t.CType(self, Widget, t.CPtr)) def draw(self, fb: UINT32PTR, fw: t.CInt, fh: t.CInt): _draw_text(fb, fw, fh, self.x, self.y, c.Addr(self.text[0]), self.color) def handle_event(self, evt: window.InputEvent | t.CPtr) -> t.CInt: return 0 def config(self, text: t.CConst | t.CChar | t.CPtr = None, color: t.CUInt32T = None): if text is not None: i: t.CInt = 0 while i < 63: ch: t.CChar = c.Deref(text + i) self.text[i] = ch if ch == 0: break i += 1 self.text[i] = 0 if color is not None: self.color = color @t.Object @t.CVTable class Button(Widget): text: t.CArray[t.CChar, 32] fill_color: t.CUInt32T text_color: t.CUInt32T pressed: t.CInt callback: t.CVoid | t.CPtr def __init__(self, master: t.CVoid | t.CPtr, w: t.CInt, h: t.CInt, text: str, callback: t.CVoid | t.CPtr): self.x = 0 self.y = 0 self.w = w self.h = h self.visible = 1 self.master = master self.widget_type = WIDGET_BUTTON self.focused = 0 self.hovered = 0 self.fill_color = t.CUInt32T(0x3C3C5A) self.text_color = t.CUInt32T(0xFFFFFFFF) self.pressed = 0 self.callback = callback i: t.CInt = 0 for ch in text: if i >= 31: break self.text[i] = ch i += 1 self.text[i] = 0 _register_widget(master, t.CType(self, Widget, t.CPtr)) def draw(self, fb: UINT32PTR, fw: t.CInt, fh: t.CInt): fill: t.CUInt32T = self.fill_color border: t.CUInt32T = t.CUInt32T(0x8080B0) if self.pressed: fill = _lighten(self.fill_color, 60) border = t.CUInt32T(0xA0A0D0) elif self.hovered: fill = _lighten(self.fill_color, 30) border = t.CUInt32T(0xA0A0D0) _fill_rect(fb, fw, fh, self.x, self.y, self.w, self.h, fill) top_left: t.CUInt32T = _lighten(fill, 80) bottom_right: t.CUInt32T = _darken(fill, 80) if self.pressed: top_left = _darken(fill, 60) bottom_right = _lighten(fill, 40) _draw_hline(fb, fw, fh, self.x, self.y, self.w, top_left) _draw_hline(fb, fw, fh, self.x, self.y + 1, self.w, top_left) _draw_vline(fb, fw, fh, self.x, self.y, self.h, top_left) _draw_vline(fb, fw, fh, self.x + 1, self.y, self.h, top_left) _draw_hline(fb, fw, fh, self.x, self.y + self.h - 1, self.w, bottom_right) _draw_hline(fb, fw, fh, self.x, self.y + self.h - 2, self.w, bottom_right) _draw_vline(fb, fw, fh, self.x + self.w - 1, self.y, self.h, bottom_right) _draw_vline(fb, fw, fh, self.x + self.w - 2, self.y, self.h, bottom_right) _draw_hline(fb, fw, fh, self.x - 1, self.y - 1, self.w + 2, border) _draw_hline(fb, fw, fh, self.x - 1, self.y + self.h, self.w + 2, border) _draw_vline(fb, fw, fh, self.x - 1, self.y - 1, self.h + 2, border) _draw_vline(fb, fw, fh, self.x + self.w, self.y - 1, self.h + 2, border) tl: t.CInt = string.strlen(c.Addr(self.text[0])) tx: t.CInt = self.x + (self.w - 8 * tl) / 2 ty: t.CInt = self.y + (self.h - CHAR_H) / 2 _draw_text(fb, fw, fh, tx, ty, c.Addr(self.text[0]), self.text_color) def handle_event(self, evt: window.InputEvent | t.CPtr) -> t.CInt: etype: t.CUInt32T = evt.type ex: t.CInt = evt.x ey: t.CInt = evt.y if etype == window.EVENT_TYPE_HOVER: if self.contains(ex, ey): if not self.hovered: self.hovered = 1 return 1 else: if self.hovered and not self.pressed: self.hovered = 0 return 1 elif etype == window.EVENT_TYPE_HOVER_LEAVE: if self.hovered: self.hovered = 0 return 1 elif etype == window.EVENT_TYPE_CLICK: if self.contains(ex, ey): self.pressed = 1 return 1 elif etype == window.EVENT_TYPE_RELEASE: if self.pressed: self.pressed = 0 if self.contains(ex, ey) and self.callback is not None: c.Asm(f"""push r12 push r13 mov r13, {c.AsmInp(self.callback, t.ASM_DESCR.REG_ANY)} mov r12, rsp and rsp, -16 call r13 mov rsp, r12 pop r13 pop r12""", op=[t.ASM_DESCR.CLOBBER_MEMORY, t.ASM_DESCR.CLOBBER_RAX, t.ASM_DESCR.CLOBBER_RCX, t.ASM_DESCR.CLOBBER_RDX, t.ASM_DESCR.CLOBBER_RSI, t.ASM_DESCR.CLOBBER_RDI, t.ASM_DESCR.CLOBBER_R8, t.ASM_DESCR.CLOBBER_R9, t.ASM_DESCR.CLOBBER_R10, t.ASM_DESCR.CLOBBER_R11]) return 1 return 0 def config(self, text: t.CConst | t.CChar | t.CPtr = None, fill_color: t.CUInt32T = None, text_color: t.CUInt32T = None, callback: t.CVoid | t.CPtr = None): if text is not None: i: t.CInt = 0 while i < 31: ch: t.CChar = c.Deref(text + i) self.text[i] = ch if ch == 0: break i += 1 self.text[i] = 0 if fill_color is not None: self.fill_color = fill_color if text_color is not None: self.text_color = text_color if callback is not None: self.callback = callback @t.Object @t.CVTable class Entry(Widget): text: t.CArray[t.CChar, 64] text_len: t.CInt cursor_pos: t.CInt max_len: t.CInt bg_color: t.CUInt32T border_color: t.CUInt32T text_color: t.CUInt32T cursor_color: t.CUInt32T def __init__(self, master: t.CVoid | t.CPtr, w: t.CInt, h: t.CInt): self.x = 0 self.y = 0 self.w = w self.h = h self.visible = 1 self.master = master self.widget_type = WIDGET_ENTRY self.focused = 0 self.hovered = 0 self.text_len = 0 self.cursor_pos = 0 self.max_len = 30 self.bg_color = t.CUInt32T(0xFF231919) self.border_color = t.CUInt32T(0x606080) self.text_color = t.CUInt32T(0x000000) self.cursor_color = t.CUInt32T(0x000000) for i in range(64): self.text[i] = 0 _register_widget(master, t.CType(self, Widget, t.CPtr)) def draw(self, fb: UINT32PTR, fw: t.CInt, fh: t.CInt): bg: t.CUInt32T = self.bg_color bdr: t.CUInt32T = self.border_color if self.focused: bg = t.CUInt32T(0xFFFFFF) bdr = t.CUInt32T(0x4488FF) elif self.hovered: bdr = t.CUInt32T(0x8080B0) _fill_rect(fb, fw, fh, self.x, self.y, self.w, self.h, bg) _draw_hline(fb, fw, fh, self.x, self.y, self.w, bdr) _draw_hline(fb, fw, fh, self.x, self.y + self.h - 1, self.w, bdr) _draw_vline(fb, fw, fh, self.x, self.y, self.h, bdr) _draw_vline(fb, fw, fh, self.x + self.w - 1, self.y, self.h, bdr) if self.text_len > 0: tc: t.CUInt32T = self.text_color if not self.focused: tc = t.CUInt32T(0xCCCCCC) _draw_text(fb, fw, fh, self.x + 6, self.y + 7, c.Addr(self.text[0]), tc) elif not self.focused: _draw_text(fb, fw, fh, self.x + 6, self.y + 7, "Type here...", t.CUInt32T(0x888888)) if self.focused: cx_pos: t.CInt = self.x + 6 + self.cursor_pos * CHAR_W if cx_pos < self.x + self.w - 2: _draw_vline(fb, fw, fh, cx_pos, self.y + 5, self.h - 10, self.cursor_color) def handle_event(self, evt: window.InputEvent | t.CPtr) -> t.CInt: etype: t.CUInt32T = evt.type ex: t.CInt = evt.x ey: t.CInt = evt.y if etype == window.EVENT_TYPE_HOVER: if self.contains(ex, ey): if not self.hovered: self.hovered = 1 tk: Tk | t.CPtr = t.CType(self.master, Tk, t.CPtr) window.set_cursor_type(tk.win_id, window.CURSOR_IBEAM) return 1 else: if self.hovered: self.hovered = 0 tk2: Tk | t.CPtr = t.CType(self.master, Tk, t.CPtr) window.set_cursor_type(tk2.win_id, window.CURSOR_DEFAULT) return 1 elif etype == window.EVENT_TYPE_HOVER_LEAVE: if self.hovered: self.hovered = 0 tk3: Tk | t.CPtr = t.CType(self.master, Tk, t.CPtr) window.set_cursor_type(tk3.win_id, window.CURSOR_DEFAULT) return 1 elif etype == window.EVENT_TYPE_CLICK: if self.contains(ex, ey): if not self.focused: self.focused = 1 tk4: Tk | t.CPtr = t.CType(self.master, Tk, t.CPtr) tk4.focused_widget = t.CType(self, Widget, t.CPtr) rel_x: t.CInt = ex - self.x - 6 if rel_x < 0: rel_x = 0 new_pos: t.CInt = (rel_x + CHAR_W / 2) / CHAR_W if new_pos > self.text_len: new_pos = self.text_len self.cursor_pos = new_pos return 1 else: if self.focused: self.focused = 0 tk5: Tk | t.CPtr = t.CType(self.master, Tk, t.CPtr) tk5.focused_widget = None return 1 elif etype == window.EVENT_TYPE_KEY: if self.focused: ebtn: t.CUInt8T = evt.button if ebtn == window.KEY_STATE_RELEASE: return 0 ch: t.CInt = ex if ch == 8: if self.cursor_pos > 0: for i in range(self.cursor_pos, self.text_len): self.text[i - 1] = self.text[i] self.text_len -= 1 self.cursor_pos -= 1 self.text[self.text_len] = 0 return 1 elif ch == window.VK_LEFT: if self.cursor_pos > 0: self.cursor_pos -= 1 return 1 elif ch == window.VK_RIGHT: if self.cursor_pos < self.text_len: self.cursor_pos += 1 return 1 elif ch == window.VK_HOME: self.cursor_pos = 0 return 1 elif ch == window.VK_END: self.cursor_pos = self.text_len return 1 elif ch == window.VK_DELETE: if self.cursor_pos < self.text_len: for i in range(self.cursor_pos, self.text_len - 1): self.text[i] = self.text[i + 1] self.text_len -= 1 self.text[self.text_len] = 0 return 1 elif ch >= 32 and ch < 127: if self.text_len < self.max_len: i: t.CInt = self.text_len while i > self.cursor_pos: self.text[i] = self.text[i - 1] i -= 1 self.text[self.cursor_pos] = t.CChar(ch) self.text_len += 1 self.cursor_pos += 1 self.text[self.text_len] = 0 return 1 return 0 def get(self) -> t.CConst | t.CChar | t.CPtr: return c.Addr(self.text[0]) def delete(self, start: t.CInt, end: t.CInt): self.text_len = 0 for i in range(64): self.text[i] = 0 def config(self, bg_color: t.CUInt32T = None, text_color: t.CUInt32T = None, border_color: t.CUInt32T = None): if bg_color is not None: self.bg_color = bg_color if text_color is not None: self.text_color = text_color if border_color is not None: self.border_color = border_color @t.Object @t.CVTable class Scrollbar(Widget): orient: t.CInt command: t.CVoid | t.CPtr slider_first: t.CInt slider_last: t.CInt action: t.CInt action_value: t.CInt pressed: t.CInt dragging: t.CInt drag_start_y: t.CInt drag_start_first: t.CInt trough_color: t.CUInt32T slider_color: t.CUInt32T arrow_color: t.CUInt32T slider_hover: t.CInt def __init__(self, master: t.CVoid | t.CPtr, w: t.CInt, h: t.CInt, orient: t.CInt): self.x = 0 self.y = 0 self.w = w self.h = h self.visible = 1 self.master = master self.widget_type = WIDGET_SCROLLBAR self.focused = 0 self.hovered = 0 self.orient = orient self.command = None self.slider_first = 0 self.slider_last = 1000 self.action = 0 self.action_value = 0 self.pressed = 0 self.dragging = 0 self.drag_start_y = 0 self.drag_start_first = 0 self.trough_color = t.CUInt32T(0x1E1E2E) self.slider_color = t.CUInt32T(0x4A4A6A) self.arrow_color = t.CUInt32T(0xAAAAAA) self.slider_hover = 0 _register_widget(master, t.CType(self, Widget, t.CPtr)) def draw(self, fb: UINT32PTR, fw: t.CInt, fh: t.CInt): _fill_rect(fb, fw, fh, self.x, self.y, self.w, self.h, self.trough_color) if self.orient != VERTICAL: return arrow_sz: t.CInt = self.w if arrow_sz > 16: arrow_sz = 16 self._draw_arrow_btn(fb, fw, fh, self.x, self.y, self.w, arrow_sz, 0, 1 if self.pressed == 1 else 0) self._draw_arrow_btn(fb, fw, fh, self.x, self.y + self.h - arrow_sz, self.w, arrow_sz, 1, 1 if self.pressed == 2 else 0) trough_y: t.CInt = self.y + arrow_sz trough_h: t.CInt = self.h - 2 * arrow_sz if trough_h <= 0: return if self.slider_last > self.slider_first: sl_h: t.CInt = (self.slider_last - self.slider_first) * trough_h / 1000 if sl_h < 8: sl_h = 8 sl_y: t.CInt = trough_y + self.slider_first * trough_h / 1000 if sl_y + sl_h > trough_y + trough_h: sl_y = trough_y + trough_h - sl_h if sl_y < trough_y: sl_y = trough_y color: t.CUInt32T = self.slider_color if self.dragging: color = _lighten(self.slider_color, 40) elif self.slider_hover: color = _lighten(self.slider_color, 20) _fill_rect(fb, fw, fh, self.x + 1, sl_y, self.w - 2, sl_h, color) top_c: t.CUInt32T = _lighten(color, 60) bot_c: t.CUInt32T = _darken(color, 60) _draw_hline(fb, fw, fh, self.x + 1, sl_y, self.w - 2, top_c) _draw_hline(fb, fw, fh, self.x + 1, sl_y + sl_h - 1, self.w - 2, bot_c) _draw_vline(fb, fw, fh, self.x + 1, sl_y, sl_h, top_c) _draw_vline(fb, fw, fh, self.x + self.w - 2, sl_y, sl_h, bot_c) def _draw_arrow_btn(self, fb: UINT32PTR, fw: t.CInt, fh: t.CInt, bx: t.CInt, by: t.CInt, bw: t.CInt, bh: t.CInt, direction: t.CInt, is_pressed: t.CInt): color: t.CUInt32T = self.slider_color if is_pressed: color = _lighten(self.slider_color, 30) _fill_rect(fb, fw, fh, bx, by, bw, bh, color) top_c: t.CUInt32T = _lighten(color, 60) bot_c: t.CUInt32T = _darken(color, 60) if is_pressed: top_c = _darken(color, 40) bot_c = _lighten(color, 30) _draw_hline(fb, fw, fh, bx, by, bw, top_c) _draw_hline(fb, fw, fh, bx, by + bh - 1, bw, bot_c) _draw_vline(fb, fw, fh, bx, by, bh, top_c) _draw_vline(fb, fw, fh, bx + bw - 1, by, bh, bot_c) cx: t.CInt = bx + bw / 2 cy: t.CInt = by + bh / 2 if direction == 0: _fill_rect(fb, fw, fh, cx - 1, cy - 3, 3, 1, self.arrow_color) _fill_rect(fb, fw, fh, cx - 2, cy - 2, 5, 1, self.arrow_color) _fill_rect(fb, fw, fh, cx - 3, cy - 1, 7, 1, self.arrow_color) else: _fill_rect(fb, fw, fh, cx - 3, cy - 1, 7, 1, self.arrow_color) _fill_rect(fb, fw, fh, cx - 2, cy, 5, 1, self.arrow_color) _fill_rect(fb, fw, fh, cx - 1, cy + 1, 3, 1, self.arrow_color) def handle_event(self, evt: window.InputEvent | t.CPtr) -> t.CInt: etype: t.CUInt32T = evt.type ex: t.CInt = evt.x ey: t.CInt = evt.y if etype == window.EVENT_TYPE_HOVER: if self.dragging: self._handle_drag(ey) return 1 if self.contains(ex, ey): if not self.slider_hover: self.slider_hover = 1 return 1 else: if self.slider_hover and self.pressed == 0: self.slider_hover = 0 return 1 return 0 elif etype == window.EVENT_TYPE_HOVER_LEAVE: if self.slider_hover: self.slider_hover = 0 if not self.dragging: self.pressed = 0 return 1 return 0 elif etype == window.EVENT_TYPE_CLICK: if self.contains(ex, ey): if self.orient == VERTICAL: arrow_sz: t.CInt = self.w if arrow_sz > 16: arrow_sz = 16 if ey < self.y + arrow_sz: self.pressed = 1 self.action = 1 self._fire_command() return 1 elif ey >= self.y + self.h - arrow_sz: self.pressed = 2 self.action = 2 self._fire_command() return 1 else: trough_y: t.CInt = self.y + arrow_sz trough_h: t.CInt = self.h - 2 * arrow_sz if trough_h <= 0: return 1 sl_h: t.CInt = (self.slider_last - self.slider_first) * trough_h / 1000 if sl_h < 8: sl_h = 8 sl_y: t.CInt = trough_y + self.slider_first * trough_h / 1000 if sl_y + sl_h > trough_y + trough_h: sl_y = trough_y + trough_h - sl_h if ey >= sl_y and ey < sl_y + sl_h: self.pressed = 4 self.dragging = 1 self.drag_start_y = ey self.drag_start_first = self.slider_first else: self.pressed = 3 if ey < sl_y: self.action = 3 else: self.action = 4 self._fire_command() return 1 return 0 elif etype == window.EVENT_TYPE_RELEASE: if self.pressed: self.pressed = 0 self.dragging = 0 return 1 return 0 return 0 def _handle_drag(self, ey: t.CInt): arrow_sz: t.CInt = self.w if arrow_sz > 16: arrow_sz = 16 trough_y: t.CInt = self.y + arrow_sz trough_h: t.CInt = self.h - 2 * arrow_sz if trough_h <= 0: return delta_y: t.CInt = ey - self.drag_start_y delta_first: t.CInt = delta_y * 1000 / trough_h new_first: t.CInt = self.drag_start_first + delta_first slider_size: t.CInt = self.slider_last - self.slider_first if slider_size <= 0: slider_size = 1 if new_first < 0: new_first = 0 if new_first + slider_size > 1000: new_first = 1000 - slider_size if new_first != self.slider_first: self.slider_first = new_first self.slider_last = new_first + slider_size self.action = 5 self.action_value = new_first self._fire_command() def _fire_command(self): if self.command is not None: c.Asm(f"""push r12 push r13 mov r13, {c.AsmInp(self.command, t.ASM_DESCR.REG_ANY)} mov r12, rsp and rsp, -16 call r13 mov rsp, r12 pop r13 pop r12""", op=[t.ASM_DESCR.CLOBBER_MEMORY, t.ASM_DESCR.CLOBBER_RAX, t.ASM_DESCR.CLOBBER_RCX, t.ASM_DESCR.CLOBBER_RDX, t.ASM_DESCR.CLOBBER_RSI, t.ASM_DESCR.CLOBBER_RDI, t.ASM_DESCR.CLOBBER_R8, t.ASM_DESCR.CLOBBER_R9, t.ASM_DESCR.CLOBBER_R10, t.ASM_DESCR.CLOBBER_R11]) def set(self, first: t.CInt, last: t.CInt): self.slider_first = first self.slider_last = last @t.Object @t.CVTable class FBCanvas(Widget): _fb: UINT32PTR _cw: t.CInt _ch: t.CInt def __init__(self, master: t.CVoid | t.CPtr, w: t.CInt, h: t.CInt): self.x = 0 self.y = 0 self.w = w self.h = h self.visible = 1 self.master = master self.widget_type = WIDGET_FBCANVAS self.focused = 0 self.hovered = 0 self._fb = None self._cw = w self._ch = h _register_widget(master, t.CType(self, Widget, t.CPtr)) def draw(self, fb: UINT32PTR, fw: t.CInt, fh: t.CInt): if not self._fb: return if self._cw <= 0 or self._ch <= 0: return copy_w: t.CInt = self._cw if copy_w > self.w: copy_w = self.w copy_h: t.CInt = self._ch if copy_h > self.h: copy_h = self.h for py in range(copy_h): ty: t.CInt = self.y + py if ty < 0 or ty >= fh: continue for px in range(copy_w): tx: t.CInt = self.x + px if tx < 0 or tx >= fw: continue fb[ty * fw + tx] = self._fb[py * self._cw + px] def handle_event(self, evt: window.InputEvent | t.CPtr) -> t.CInt: return 0 def set_fb(self, fb: UINT32PTR, cw: t.CInt, ch: t.CInt): self._fb = fb self._cw = cw self._ch = ch def get_fb(self) -> UINT32PTR: return self._fb def get_w(self) -> t.CInt: return self._cw def get_h(self) -> t.CInt: return self._ch @t.Object @t.CVTable class Text(Widget): lines: t.CArray[t.CArray[t.CChar, 80], TEXT_MAX_LINES] line_count: t.CInt input_buf: t.CArray[t.CChar, 80] input_len: t.CInt cursor_pos: t.CInt bg_color: t.CUInt32T text_color: t.CUInt32T prompt_color: t.CUInt32T cursor_color: t.CUInt32T border_color: t.CUInt32T on_command: t.CVoid | t.CPtr history: t.CArray[t.CArray[t.CChar, 80], 16] history_count: t.CInt history_idx: t.CInt scroll_offset: t.CInt max_lines: t.CInt yscrollbar: t.CVoid | t.CPtr def __init__(self, master: t.CVoid | t.CPtr, w: t.CInt, h: t.CInt): self.x = 0 self.y = 0 self.w = w self.h = h self.visible = 1 self.master = master self.widget_type = WIDGET_TEXT self.focused = 0 self.hovered = 0 self.line_count = 0 self.input_len = 0 self.cursor_pos = 0 self.bg_color = t.CUInt32T(0x0C0C0C) self.text_color = t.CUInt32T(0xCCCCCC) self.prompt_color = t.CUInt32T(0x00FF00) self.cursor_color = t.CUInt32T(0x00FF00) self.border_color = t.CUInt32T(0x333333) self.on_command = None self.history_count = 0 self.history_idx = 0 self.scroll_offset = 0 self.max_lines = TEXT_MAX_LINES self.yscrollbar = None i: t.CInt = 0 while i < TEXT_MAX_LINES: j: t.CInt = 0 while j <= 79: self.lines[i][j] = 0 j += 1 i += 1 i = 0 while i < 80: self.input_buf[i] = 0 i += 1 _register_widget(master, t.CType(self, Widget, t.CPtr)) def draw(self, fb: UINT32PTR, fw: t.CInt, fh: t.CInt): self._update_scrollbar() _fill_rect(fb, fw, fh, self.x, self.y, self.w, self.h, self.bg_color) _fill_rect(fb, fw, fh, self.x, self.y + self.h - 2, self.w, 2, self.border_color) visible: t.CInt = self._visible_lines() for i in range(visible): line_idx: t.CInt = self.scroll_offset + i if line_idx < self.line_count: _draw_text(fb, fw, fh, self.x + 4, self.y + i * CHAR_H, c.Addr(self.lines[line_idx][0]), self.text_color) prompt_y: t.CInt = self.y + self.h - CHAR_H - 4 _draw_text(fb, fw, fh, self.x + 4, prompt_y, "> ", self.prompt_color) if self.input_len > 0: _draw_text(fb, fw, fh, self.x + 4 + 2 * CHAR_W, prompt_y, c.Addr(self.input_buf[0]), t.CUInt32T(0xFFFFFF)) cx_pos: t.CInt = self.x + 4 + (2 + self.cursor_pos) * CHAR_W if cx_pos < self.x + self.w - 4: _fill_rect(fb, fw, fh, cx_pos, prompt_y, CHAR_W // 2, CHAR_H, self.cursor_color) def handle_event(self, evt: window.InputEvent | t.CPtr) -> t.CInt: etype: t.CUInt32T = evt.type ex: t.CInt = evt.x ey: t.CInt = evt.y if etype == window.EVENT_TYPE_CLICK: if self.contains(ex, ey): if not self.focused: self.focused = 1 tk: Tk | t.CPtr = t.CType(self.master, Tk, t.CPtr) tk.focused_widget = t.CType(self, Widget, t.CPtr) prompt_y: t.CInt = self.y + self.h - CHAR_H - 4 if ey >= prompt_y and ey < prompt_y + CHAR_H: rel_x: t.CInt = ex - self.x - 4 - 2 * CHAR_W if rel_x < 0: rel_x = 0 new_pos: t.CInt = (rel_x + CHAR_W / 2) / CHAR_W if new_pos > self.input_len: new_pos = self.input_len self.cursor_pos = new_pos return 1 else: if self.focused: self.focused = 0 tk2: Tk | t.CPtr = t.CType(self.master, Tk, t.CPtr) tk2.focused_widget = None return 1 elif etype == window.EVENT_TYPE_KEY: if self.focused: ebtn2: t.CUInt8T = evt.button if ebtn2 == window.KEY_STATE_RELEASE: return 0 ch: t.CInt = ex if ch == 8: if self.cursor_pos > 0: i: t.CInt = self.cursor_pos while i < self.input_len: self.input_buf[i - 1] = self.input_buf[i] i += 1 self.input_len -= 1 self.cursor_pos -= 1 self.input_buf[self.input_len] = 0 return 1 elif ch == 13: self._exec_input() self.input_len = 0 self.cursor_pos = 0 for i in range(80): self.input_buf[i] = 0 return 1 elif ch == window.VK_LEFT: if self.cursor_pos > 0: self.cursor_pos -= 1 return 1 elif ch == window.VK_RIGHT: if self.cursor_pos < self.input_len: self.cursor_pos += 1 return 1 elif ch == window.VK_UP: if self.history_count > 0: if self.history_idx < self.history_count: self.history_idx += 1 hi: t.CInt = self.history_count - self.history_idx if hi < 0: hi = 0 for j in range(80): self.input_buf[j] = self.history[hi][j] self.input_len = 0 while self.input_len < 79 and self.input_buf[self.input_len] != 0: self.input_len += 1 self.cursor_pos = self.input_len return 1 elif ch == window.VK_DOWN: if self.history_count > 0: if self.history_idx > 0: self.history_idx -= 1 if self.history_idx == 0: self.input_len = 0 self.cursor_pos = 0 for j2 in range(80): self.input_buf[j2] = 0 else: hi2: t.CInt = self.history_count - self.history_idx for j3 in range(80): self.input_buf[j3] = self.history[hi2][j3] self.input_len = 0 while self.input_len < 79 and self.input_buf[self.input_len] != 0: self.input_len += 1 self.cursor_pos = self.input_len return 1 elif ch == window.VK_HOME: self.cursor_pos = 0 return 1 elif ch == window.VK_END: self.cursor_pos = self.input_len return 1 elif ch == window.VK_DELETE: if self.cursor_pos < self.input_len: i: t.CInt = self.cursor_pos while i < self.input_len - 1: self.input_buf[i] = self.input_buf[i + 1] i += 1 self.input_len -= 1 self.input_buf[self.input_len] = 0 return 1 elif ch == window.VK_PRIOR: if self.scroll_offset > 0: vis: t.CInt = self._visible_lines() self.scroll_offset -= vis if self.scroll_offset < 0: self.scroll_offset = 0 self._update_scrollbar() return 1 elif ch == window.VK_NEXT: vis2: t.CInt = self._visible_lines() max_off: t.CInt = self.line_count - vis2 if max_off < 0: max_off = 0 if self.scroll_offset < max_off: self.scroll_offset += vis2 if self.scroll_offset > max_off: self.scroll_offset = max_off self._update_scrollbar() return 1 elif ch >= 32 and ch < 127: if self.input_len < 79: i: t.CInt = self.input_len while i > self.cursor_pos: self.input_buf[i] = self.input_buf[i - 1] i -= 1 self.input_buf[self.cursor_pos] = t.CChar(ch) self.input_len += 1 self.cursor_pos += 1 self.input_buf[self.input_len] = 0 return 1 return 0 def _exec_input(self): self.insert("> ") if self.input_len > 0: last: t.CInt = self.line_count - 1 j: t.CInt = 0 while j < self.input_len and j < 77: self.lines[last][2 + j] = self.input_buf[j] j += 1 self.lines[last][2 + j] = 0 if self.history_count < 16: k: t.CInt = 0 while k < 80: self.history[self.history_count][k] = self.input_buf[k] k += 1 self.history_count += 1 else: i: t.CInt = 0 while i < 15: k2: t.CInt = 0 while k2 < 80: self.history[i][k2] = self.history[i + 1][k2] k2 += 1 i += 1 k3: t.CInt = 0 while k3 < 80: self.history[15][k3] = self.input_buf[k3] k3 += 1 self.history_idx = 0 cmd: t.CVoid | t.CPtr = self.on_command if cmd is not None: c.Asm(f"""push r12 push r13 mov r13, {c.AsmInp(cmd, t.ASM_DESCR.REG_ANY)} mov r12, rsp and rsp, -16 call r13 mov rsp, r12 pop r13 pop r12""", op=[t.ASM_DESCR.CLOBBER_MEMORY, t.ASM_DESCR.CLOBBER_RAX, t.ASM_DESCR.CLOBBER_RCX, t.ASM_DESCR.CLOBBER_RDX, t.ASM_DESCR.CLOBBER_RSI, t.ASM_DESCR.CLOBBER_RDI, t.ASM_DESCR.CLOBBER_R8, t.ASM_DESCR.CLOBBER_R9, t.ASM_DESCR.CLOBBER_R10, t.ASM_DESCR.CLOBBER_R11]) else: self.insert("(no callback)") def insert(self, text: str): start: t.CInt = 0 pos: t.CInt = 0 while True: ch: t.CChar = c.Deref(text + pos) if ch == 0: if pos > start: self._insert_substring(text, start, pos) break if ch == 10: self._insert_substring(text, start, pos) start = pos + 1 pos += 1 def insert_ptr(self, text: t.CConst | t.CChar | t.CPtr): start: t.CInt = 0 pos: t.CInt = 0 while True: ch: t.CChar = c.Deref(text + pos) if ch == 0: if pos > start: self._insert_substring(text, start, pos) break if ch == 10: self._insert_substring(text, start, pos) start = pos + 1 pos += 1 def _insert_substring(self, text: t.CConst | t.CChar | t.CPtr, start: t.CInt, end: t.CInt): if self.line_count >= self.max_lines: i: t.CInt = 0 while i < self.max_lines - 1: j: t.CInt = 0 while j <= 79: self.lines[i][j] = self.lines[i + 1][j] j += 1 i += 1 self.line_count -= 1 if self.scroll_offset > 0: self.scroll_offset -= 1 idx: t.CInt = self.line_count j: t.CInt = 0 k: t.CInt = start while k < end and j < 79: ch: t.CChar = c.Deref(text + k) if ch == 9: tab_stop: t.CInt = 8 - (j % 8) while tab_stop > 0 and j < 79: self.lines[idx][j] = 32 j += 1 tab_stop -= 1 elif ch == 13: pass else: self.lines[idx][j] = ch j += 1 k += 1 self.lines[idx][j] = 0 self.line_count += 1 vis: t.CInt = self._visible_lines() if self.line_count > vis: self.scroll_offset = self.line_count - vis else: self.scroll_offset = 0 self._update_scrollbar() def delete(self, start: t.CInt, end: t.CInt): self.line_count = 0 self.scroll_offset = 0 self._update_scrollbar() def get(self) -> t.CConst | t.CChar | t.CPtr: return c.Addr(self.input_buf[0]) def config(self, text_color: t.CUInt32T = None, bg_color: t.CUInt32T = None, prompt_color: t.CUInt32T = None, cursor_color: t.CUInt32T = None, border_color: t.CUInt32T = None): if text_color is not None: self.text_color = text_color if bg_color is not None: self.bg_color = bg_color if prompt_color is not None: self.prompt_color = prompt_color if cursor_color is not None: self.cursor_color = cursor_color if border_color is not None: self.border_color = border_color def _visible_lines(self) -> t.CInt: return (self.h - CHAR_H - 4) / CHAR_H def _update_scrollbar(self): vis: t.CInt = self._visible_lines() if self.line_count <= vis: self.scroll_offset = 0 else: max_off: t.CInt = self.line_count - vis if self.scroll_offset > max_off: self.scroll_offset = max_off if self.yscrollbar is not None: sb: Scrollbar | t.CPtr = t.CType(self.yscrollbar, Scrollbar, t.CPtr) if self.line_count <= vis: sb.set(0, 1000) else: first: t.CInt = self.scroll_offset * 1000 / self.line_count last: t.CInt = (self.scroll_offset + vis) * 1000 / self.line_count sb.set(first, last) @t.Object class Tk: win_id: t.CInt buf: UINT32PTR bw: t.CInt bh: t.CInt style: t.CInt bg_color: t.CUInt32T widgets: t.CArray[Widget | t.CPtr, MAX_WIDGETS] widget_count: t.CInt focused_widget: Widget | t.CPtr _win_x: t.CInt _win_y: t.CInt _title: t.CArray[t.CChar, 32] resizable_w: t.CInt resizable_h: t.CInt _grid_rows: t.CInt _grid_cols: t.CInt def __init__(self): self.widget_count = 0 self.focused_widget = None self.win_id = -1 self._win_x = 0 self._win_y = 0 self.style = 0 self.bg_color = t.CUInt32T(0xFF231919) self.resizable_w = 1 self.resizable_h = 1 self._grid_rows = 0 self._grid_cols = 0 if _g_layout_init == 0: i: t.CInt = 0 while i < MAX_WIDGETS: _g_wl_type[i] = 0 _g_wl_side[i] = 0 _g_wl_fill[i] = 0 _g_wl_expand[i] = 0 _g_wl_padx[i] = 0 _g_wl_pady[i] = 0 _g_wl_row[i] = 0 _g_wl_col[i] = 0 _g_wl_rowspan[i] = 0 _g_wl_colspan[i] = 1 _g_wl_sticky[i] = 0 i += 1 _g_layout_init = 1 j: t.CInt = 0 while j < 32: self._title[j] = 0 j += 1 def pack(self, idx: t.CInt, side: t.CInt, fill: t.CInt, expand: t.CInt, padx: t.CInt, pady: t.CInt): _g_wl_type[idx] = 2 _g_wl_side[idx] = side _g_wl_fill[idx] = fill _g_wl_expand[idx] = expand _g_wl_padx[idx] = padx _g_wl_pady[idx] = pady def grid(self, idx: t.CInt, row: t.CInt, col: t.CInt, rowspan: t.CInt, colspan: t.CInt, sticky: t.CInt): _g_wl_type[idx] = 3 _g_wl_row[idx] = row _g_wl_col[idx] = col _g_wl_rowspan[idx] = rowspan _g_wl_colspan[idx] = colspan _g_wl_sticky[idx] = sticky def title(self, text: str): i: t.CInt = 0 while i < 31: ch: t.CChar = text[i] if ch == 0: break self._title[i] = ch i += 1 self._title[i] = 0 if self.win_id >= 0: window.set_title(self.win_id, c.Addr(self._title[0])) def set_style(self, s: t.CInt): self.style = s def resizable(self, rw: t.CInt, rh: t.CInt): self.resizable_w = rw self.resizable_h = rh if self.win_id >= 0: window.set_resizable(self.win_id, rw, rh) def geometry(self, x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt): self._win_x = x self._win_y = y self.bw = w self.bh = h - TITLE_H - 2 if self.win_id < 0: self.win_id = window.create_window(x, y, w, h, c.Addr(self._title[0]), self.style) self.buf = window.get_winbuf(self.win_id) window.set_resizable(self.win_id, self.resizable_w, self.resizable_h) else: window.set_geometry(self.win_id, x, y, w, h) self.buf = window.get_winbuf(self.win_id) def setup(self, x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt, title: t.CConst | t.CChar | t.CPtr, style: t.CInt): self._win_x = x self._win_y = y self.win_id = window.create_window(x, y, w, h, title, style) self.buf = window.get_winbuf(self.win_id) self.bw = w self.bh = h - TITLE_H - 2 self.style = style self.bg_color = t.CUInt32T(0xFF231919) def redraw(self): self._do_layout() self.buf = window.get_winbuf(self.win_id) sz: t.CInt = window.get_win_size(self.win_id) if sz != 0: self.bw = sz >> 16 self.bh = sz & 0xFFFF if not self.buf: return if self.bw <= 0 or self.bh <= 0: return _fill_rect(self.buf, self.bw, self.bh, 0, 0, self.bw, self.bh, self.bg_color) i: t.CInt = 0 while i < self.widget_count: w: Widget | t.CPtr = self.widgets[i] if w.visible: w.draw(self.buf, self.bw, self.bh) i += 1 window.flush(self.win_id) def _do_layout(self): self._grid_rows = 0 self._grid_cols = 0 cav_x: t.CInt = 0 cav_y: t.CInt = 0 cav_w: t.CInt = self.bw cav_h: t.CInt = self.bh i: t.CInt = 0 while i < self.widget_count: w: Widget | t.CPtr = self.widgets[i] lt: t.CInt = _g_wl_type[i] if lt == 2: side: t.CInt = _g_wl_side[i] fill: t.CInt = _g_wl_fill[i] expand: t.CInt = _g_wl_expand[i] px: t.CInt = _g_wl_padx[i] py: t.CInt = _g_wl_pady[i] ww: t.CInt = w.w wh: t.CInt = w.h if side == PACK_TOP: if fill == FILL_X or fill == FILL_BOTH: ww = cav_w - 2 * px if expand: if fill == FILL_Y or fill == FILL_BOTH: wh = cav_h - 2 * py w.x = cav_x + px w.y = cav_y + py w.w = ww w.h = wh cav_y += wh + 2 * py cav_h -= wh + 2 * py elif side == PACK_BOTTOM: if fill == FILL_X or fill == FILL_BOTH: ww = cav_w - 2 * px if expand: if fill == FILL_Y or fill == FILL_BOTH: wh = cav_h - 2 * py w.x = cav_x + px w.y = cav_y + cav_h - wh - py w.w = ww w.h = wh cav_h -= wh + 2 * py elif side == PACK_LEFT: if fill == FILL_Y or fill == FILL_BOTH: wh = cav_h - 2 * py if expand: if fill == FILL_X or fill == FILL_BOTH: ww = cav_w - 2 * px w.x = cav_x + px w.y = cav_y + py w.w = ww w.h = wh cav_x += ww + 2 * px cav_w -= ww + 2 * px elif side == PACK_RIGHT: if fill == FILL_Y or fill == FILL_BOTH: wh = cav_h - 2 * py if expand: if fill == FILL_X or fill == FILL_BOTH: ww = cav_w - 2 * px w.x = cav_x + cav_w - ww - px w.y = cav_y + py w.w = ww w.h = wh cav_w -= ww + 2 * px elif lt == 3: r: t.CInt = _g_wl_row[i] + _g_wl_rowspan[i] c: t.CInt = _g_wl_col[i] + _g_wl_colspan[i] if r > self._grid_rows: self._grid_rows = r if c > self._grid_cols: self._grid_cols = c i += 1 i = 0 while i < self.widget_count: w: Widget | t.CPtr = self.widgets[i] if _g_wl_type[i] == 3: if self._grid_rows <= 0: self._grid_rows = 1 if self._grid_cols <= 0: self._grid_cols = 1 cell_w: t.CInt = self.bw / self._grid_cols cell_h: t.CInt = self.bh / self._grid_rows wx: t.CInt = _g_wl_col[i] * cell_w wy: t.CInt = _g_wl_row[i] * cell_h ww: t.CInt = _g_wl_colspan[i] * cell_w wh: t.CInt = _g_wl_rowspan[i] * cell_h sticky: t.CInt = _g_wl_sticky[i] if sticky == 0: ox: t.CInt = (ww - w.w) / 2 oy: t.CInt = (wh - w.h) / 2 w.x = wx + ox w.y = wy + oy else: w.x = wx w.y = wy if (sticky & 1) != 0: w.w = ww if (sticky & 2) != 0: w.h = wh i += 1 def update(self): self.redraw() def winfo_exists(self) -> t.CInt: if self.win_id < 0: return 0 return window.is_active(self.win_id) def mainloop(self): self.redraw() evt: window.InputEvent while window.is_active(self.win_id): result: t.CInt = window.poll_event(c.Addr(evt)) if result != 0: if evt.win_id == self.win_id: need_redraw: t.CInt = 0 etype: t.CUInt32T = evt.type if etype == window.EVENT_TYPE_PING: window.flush(self.win_id) elif etype == window.EVENT_TYPE_RESIZE: self.buf = window.get_winbuf(self.win_id) sz: t.CInt = window.get_win_size(self.win_id) self.bw = sz >> 16 self.bh = sz & 0xFFFF need_redraw = 1 elif etype == window.EVENT_TYPE_KEY or etype == window.EVENT_TYPE_KEY_RELEASE: if self.focused_widget is not None: handled: t.CInt = self.focused_widget.handle_event(c.Addr(evt)) if handled: need_redraw = 1 else: if etype == window.EVENT_TYPE_HOVER: i: t.CInt = self.widget_count - 1 while i >= 0: w: Widget | t.CPtr = self.widgets[i] if w.visible: handled2: t.CInt = w.handle_event(c.Addr(evt)) if handled2: need_redraw = 1 break i -= 1 else: i: t.CInt = 0 while i < self.widget_count: w: Widget | t.CPtr = self.widgets[i] if w.visible: handled2: t.CInt = w.handle_event(c.Addr(evt)) if handled2: need_redraw = 1 i += 1 if need_redraw: self.redraw() else: process.yield_cpu()