term.ui: native Windows console implementation (#8359)
							parent
							
								
									2ada7b730e
								
							
						
					
					
						commit
						e233911a7b
					
				|  | @ -505,6 +505,9 @@ fn event(e &tui.Event, x voidptr) { | |||
| 			.escape { | ||||
| 				exit(0) | ||||
| 			} | ||||
| 			.enter { | ||||
| 				buffer.put('\n') | ||||
| 			} | ||||
| 			.backspace { | ||||
| 				buffer.del(-1) | ||||
| 			} | ||||
|  | @ -552,8 +555,8 @@ fn event(e &tui.Event, x voidptr) { | |||
| 					if e.code == .s { | ||||
| 						a.save() | ||||
| 					} | ||||
| 				} else if e.modifiers in [tui.shift, 0] { | ||||
| 					buffer.put(e.ascii.str()) | ||||
| 				} else if e.modifiers in [tui.shift, 0] && e.code != .null { | ||||
| 					buffer.put(e.ascii.ascii_str()) | ||||
| 				} | ||||
| 			} | ||||
| 			else { | ||||
|  |  | |||
|  | @ -2,31 +2,33 @@ module term | |||
| 
 | ||||
| import os | ||||
| 
 | ||||
| pub struct Coord16 { | ||||
| pub: | ||||
| 	x i16 | ||||
| 	y i16 | ||||
| [typedef] | ||||
| struct C.COORD { | ||||
| 	X i16 | ||||
| 	Y i16 | ||||
| } | ||||
| 
 | ||||
| struct SmallRect { | ||||
| 	left   i16 | ||||
| 	top    i16 | ||||
| 	right  i16 | ||||
| 	bottom i16 | ||||
| [typedef] | ||||
| struct C.SMALL_RECT { | ||||
| 	Left   u16 | ||||
| 	Top    u16 | ||||
| 	Right  u16 | ||||
| 	Bottom u16 | ||||
| } | ||||
| 
 | ||||
| // win: CONSOLE_SCREEN_BUFFER_INFO
 | ||||
| // https://docs.microsoft.com/en-us/windows/console/console-screen-buffer-info-str
 | ||||
| struct ConsoleScreenBufferInfo { | ||||
| 	dw_size                Coord16 | ||||
| 	dw_cursor_position     Coord16 | ||||
| 	w_attributes           u16 | ||||
| 	sr_window              SmallRect | ||||
| 	dw_maximum_window_size Coord16 | ||||
| [typedef] | ||||
| struct C.CONSOLE_SCREEN_BUFFER_INFO { | ||||
| 	dwSize              C.COORD | ||||
| 	dwCursorPosition    C.COORD | ||||
| 	wAttributes         u16 | ||||
| 	srWindow            C.SMALL_RECT | ||||
| 	dwMaximumWindowSize C.COORD | ||||
| } | ||||
| 
 | ||||
| // ref - https://docs.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo
 | ||||
| fn C.GetConsoleScreenBufferInfo(handle os.HANDLE, info &ConsoleScreenBufferInfo) bool | ||||
| fn C.GetConsoleScreenBufferInfo(handle os.HANDLE, info &C.CONSOLE_SCREEN_BUFFER_INFO) bool | ||||
| 
 | ||||
| // ref - https://docs.microsoft.com/en-us/windows/console/setconsoletitle
 | ||||
| fn C.SetConsoleTitle(title &u16) bool | ||||
|  | @ -34,10 +36,10 @@ fn C.SetConsoleTitle(title &u16) bool | |||
| // get_terminal_size returns a number of colums and rows of terminal window.
 | ||||
| pub fn get_terminal_size() (int, int) { | ||||
| 	if is_atty(1) > 0 && os.getenv('TERM') != 'dumb' { | ||||
| 		info := ConsoleScreenBufferInfo{} | ||||
| 		info := C.CONSOLE_SCREEN_BUFFER_INFO{} | ||||
| 		if C.GetConsoleScreenBufferInfo(C.GetStdHandle(C.STD_OUTPUT_HANDLE), &info) { | ||||
| 			columns := int(info.sr_window.right - info.sr_window.left + 1) | ||||
| 			rows := int(info.sr_window.bottom - info.sr_window.top + 1) | ||||
| 			columns := int(info.srWindow.Right - info.srWindow.Left + 1) | ||||
| 			rows := int(info.srWindow.Bottom - info.srWindow.Top + 1) | ||||
| 			return columns, rows | ||||
| 		} | ||||
| 	} | ||||
|  | @ -48,10 +50,10 @@ pub fn get_terminal_size() (int, int) { | |||
| pub fn get_cursor_position() Coord { | ||||
| 	mut res := Coord{} | ||||
| 	if is_atty(1) > 0 && os.getenv('TERM') != 'dumb' { | ||||
| 		info := ConsoleScreenBufferInfo{} | ||||
| 		info := C.CONSOLE_SCREEN_BUFFER_INFO{} | ||||
| 		if C.GetConsoleScreenBufferInfo(C.GetStdHandle(C.STD_OUTPUT_HANDLE), &info) { | ||||
| 			res.x = info.dw_cursor_position.x | ||||
| 			res.y = info.dw_cursor_position.y | ||||
| 			res.x = info.dwCursorPosition.X | ||||
| 			res.y = info.dwCursorPosition.Y | ||||
| 		} | ||||
| 	} | ||||
| 	return res | ||||
|  |  | |||
|  | @ -0,0 +1,84 @@ | |||
| module ui | ||||
| 
 | ||||
| import os | ||||
| 
 | ||||
| union C.Event { | ||||
| 	KeyEvent              C.KEY_EVENT_RECORD | ||||
| 	MouseEvent            C.MOUSE_EVENT_RECORD | ||||
| 	WindowBufferSizeEvent C.WINDOW_BUFFER_SIZE_RECORD | ||||
| 	MenuEvent             C.MENU_EVENT_RECORD | ||||
| 	FocusEvent            C.FOCUS_EVENT_RECORD | ||||
| } | ||||
| 
 | ||||
| [typedef] | ||||
| struct C.INPUT_RECORD { | ||||
| 	EventType u16 | ||||
| 	Event     C.Event | ||||
| } | ||||
| 
 | ||||
| union C.uChar { | ||||
| 	UnicodeChar rune | ||||
| 	AsciiChar   byte | ||||
| } | ||||
| 
 | ||||
| [typedef] | ||||
| struct C.KEY_EVENT_RECORD { | ||||
| 	bKeyDown          int | ||||
| 	wRepeatCount      u16 | ||||
| 	wVirtualKeyCode   u16 | ||||
| 	wVirtualScanCode  u16 | ||||
| 	uChar             C.uChar | ||||
| 	dwControlKeyState u32 | ||||
| } | ||||
| 
 | ||||
| [typedef] | ||||
| struct C.MOUSE_EVENT_RECORD { | ||||
| 	dwMousePosition   C.COORD | ||||
| 	dwButtonState     u32 | ||||
| 	dwControlKeyState u32 | ||||
| 	dwEventFlags      u32 | ||||
| } | ||||
| 
 | ||||
| [typedef] | ||||
| struct C.WINDOW_BUFFER_SIZE_RECORD { | ||||
| 	dwSize C.COORD | ||||
| } | ||||
| 
 | ||||
| [typedef] | ||||
| struct C.MENU_EVENT_RECORD { | ||||
| 	dwCommandId u32 | ||||
| } | ||||
| 
 | ||||
| [typedef] | ||||
| struct C.FOCUS_EVENT_RECORD { | ||||
| 	bSetFocus int | ||||
| } | ||||
| 
 | ||||
| [typedef] | ||||
| struct C.COORD { | ||||
| 	X i16 | ||||
| 	Y i16 | ||||
| } | ||||
| 
 | ||||
| [typedef] | ||||
| struct C.SMALL_RECT { | ||||
| 	Left   u16 | ||||
| 	Top    u16 | ||||
| 	Right  u16 | ||||
| 	Bottom u16 | ||||
| } | ||||
| 
 | ||||
| [typedef] | ||||
| struct C.CONSOLE_SCREEN_BUFFER_INFO { | ||||
| 	dwSize              C.COORD | ||||
| 	dwCursorPosition    C.COORD | ||||
| 	wAttributes         u16 | ||||
| 	srWindow            C.SMALL_RECT | ||||
| 	dwMaximumWindowSize C.COORD | ||||
| } | ||||
| 
 | ||||
| fn C.ReadConsoleInput() bool | ||||
| 
 | ||||
| fn C.GetNumberOfConsoleInputEvents() bool | ||||
| 
 | ||||
| fn C.GetConsoleScreenBufferInfo(handle os.HANDLE, info &C.CONSOLE_SCREEN_BUFFER_INFO) bool | ||||
|  | @ -1,4 +1,4 @@ | |||
| // Copyright (c) 2020 Raúl Hernández. All rights reserved.
 | ||||
| // Copyright (c) 2020-2021 Raúl Hernández. All rights reserved.
 | ||||
| // Use of this source code is governed by an MIT license
 | ||||
| // that can be found in the LICENSE file.
 | ||||
| module ui | ||||
|  | @ -108,6 +108,18 @@ pub enum KeyCode { | |||
| 	f10                  = 299 | ||||
| 	f11                  = 300 | ||||
| 	f12                  = 301 | ||||
| 	f13                  = 302 | ||||
| 	f14                  = 303 | ||||
| 	f15                  = 304 | ||||
| 	f16                  = 305 | ||||
| 	f17                  = 306 | ||||
| 	f18                  = 307 | ||||
| 	f19                  = 308 | ||||
| 	f20                  = 309 | ||||
| 	f21                  = 310 | ||||
| 	f22                  = 311 | ||||
| 	f23                  = 312 | ||||
| 	f24                  = 313 | ||||
| } | ||||
| 
 | ||||
| pub const ( | ||||
|  | @ -164,10 +176,10 @@ pub: | |||
| } | ||||
| 
 | ||||
| pub struct Context { | ||||
| 	ExtraContext // contains fields specific to an implementation
 | ||||
| pub: | ||||
| 	cfg 		  Config | ||||
| mut: | ||||
| 	read_buf      []byte | ||||
| 	print_buf     []byte | ||||
| 	paused        bool | ||||
| 	enable_su     bool | ||||
|  | @ -198,3 +210,38 @@ pub struct Config { | |||
| 	// All kill signals to set up exit listeners on
 | ||||
| 	reset                []int = [1, 2, 3, 4, 6, 7, 8, 9, 11, 13, 14, 15, 19] | ||||
| } | ||||
| 
 | ||||
| [inline] | ||||
| fn (ctx &Context) init() { | ||||
| 	if ctx.cfg.init_fn != voidptr(0) { | ||||
| 		ctx.cfg.init_fn(ctx.cfg.user_data) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| [inline] | ||||
| fn (ctx &Context) frame() { | ||||
| 	if ctx.cfg.frame_fn != voidptr(0) { | ||||
| 		ctx.cfg.frame_fn(ctx.cfg.user_data) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| [inline] | ||||
| fn (ctx &Context) cleanup() { | ||||
| 	if ctx.cfg.cleanup_fn != voidptr(0) { | ||||
| 		ctx.cfg.cleanup_fn(ctx.cfg.user_data) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| [inline] | ||||
| fn (ctx &Context) fail(error string) { | ||||
| 	if ctx.cfg.fail_fn != voidptr(0) { | ||||
| 		ctx.cfg.fail_fn(error) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| [inline] | ||||
| fn (ctx &Context) event(event &Event) { | ||||
| 	if ctx.cfg.event_fn != voidptr(0) { | ||||
| 		ctx.cfg.event_fn(event, ctx.cfg.user_data) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -1,8 +1,14 @@ | |||
| // Copyright (c) 2020 Raúl Hernández. All rights reserved.
 | ||||
| // Copyright (c) 2020-2021 Raúl Hernández. All rights reserved.
 | ||||
| // Use of this source code is governed by an MIT license
 | ||||
| // that can be found in the LICENSE file.
 | ||||
| module ui | ||||
| 
 | ||||
| 
 | ||||
| struct ExtraContext { | ||||
| mut: | ||||
| 	read_buf []byte | ||||
| } | ||||
| 
 | ||||
| const ( | ||||
| 	ctx_ptr = &Context(0) | ||||
| ) | ||||
|  | @ -10,8 +16,8 @@ const ( | |||
| pub fn init(cfg Config) &Context { | ||||
| 	mut ctx := &Context{ | ||||
| 		cfg: cfg, | ||||
| 		read_buf: []byte{ cap: cfg.buffer_size } | ||||
| 	} | ||||
| 	ctx.read_buf = []byte{ cap: cfg.buffer_size } | ||||
| 
 | ||||
| 	// lmao
 | ||||
| 	unsafe { | ||||
|  | @ -22,11 +28,14 @@ pub fn init(cfg Config) &Context { | |||
| 	return ctx | ||||
| } | ||||
| 
 | ||||
| pub fn (mut ctx Context) save_title() { | ||||
| [inline] | ||||
| fn save_title() { | ||||
|     // restore the previously saved terminal title
 | ||||
|     print('\x1b[22;0t') | ||||
| } | ||||
| pub fn (mut ctx Context) load_title() { | ||||
| 
 | ||||
| [inline] | ||||
| fn load_title() { | ||||
|     // restore the previously saved terminal title
 | ||||
|     print('\x1b[23;0t') | ||||
| } | ||||
|  | @ -57,38 +66,3 @@ fn (mut ctx Context) resize_arr(size int) { | |||
| 	mut l := &ctx.read_buf.len | ||||
| 	unsafe { *l = size } | ||||
| } | ||||
| 
 | ||||
| [inline] | ||||
| fn (ctx &Context) init() { | ||||
| 	if ctx.cfg.init_fn != voidptr(0) { | ||||
| 		ctx.cfg.init_fn(ctx.cfg.user_data) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| [inline] | ||||
| fn (ctx &Context) frame() { | ||||
| 	if ctx.cfg.frame_fn != voidptr(0) { | ||||
| 		ctx.cfg.frame_fn(ctx.cfg.user_data) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| [inline] | ||||
| fn (ctx &Context) cleanup() { | ||||
| 	if ctx.cfg.cleanup_fn != voidptr(0) { | ||||
| 		ctx.cfg.cleanup_fn(ctx.cfg.user_data) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| [inline] | ||||
| fn (ctx &Context) fail(error string) { | ||||
| 	if ctx.cfg.fail_fn != voidptr(0) { | ||||
| 		ctx.cfg.fail_fn(error) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| [inline] | ||||
| fn (ctx &Context) event(event &Event) { | ||||
| 	if ctx.cfg.event_fn != voidptr(0) { | ||||
| 		ctx.cfg.event_fn(event, ctx.cfg.user_data) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -1,25 +1,297 @@ | |||
| // Copyright (c) 2020 Raúl Hernández. All rights reserved.
 | ||||
| // Copyright (c) 2020-2021 Raúl Hernández. All rights reserved.
 | ||||
| // Use of this source code is governed by an MIT license
 | ||||
| // that can be found in the LICENSE file.
 | ||||
| module ui | ||||
| 
 | ||||
| import os | ||||
| import time | ||||
| 
 | ||||
| const ( | ||||
| 	not_implemented_yet = "term.input: error: Windows support isn't implemented yet" | ||||
| 	buf_size         = 64 | ||||
| 	ctx_ptr          = &Context(0) | ||||
| 	stdin_at_startup = u32(0) | ||||
| ) | ||||
| 
 | ||||
| struct ExtraContext { | ||||
| mut: | ||||
| 	stdin_handle  C.HANDLE | ||||
| 	stdout_handle C.HANDLE | ||||
| 	read_buf      [buf_size]C.INPUT_RECORD | ||||
| 	mouse_down    MouseButton | ||||
| } | ||||
| 
 | ||||
| fn restore_terminal_state() { | ||||
| 	if ctx_ptr != 0 { | ||||
| 		if ctx_ptr.cfg.use_alternate_buffer { | ||||
| 			// clear the terminal and set the cursor to the origin
 | ||||
| 			print('\x1b[2J\x1b[3J') | ||||
| 			print('\x1b[?1049l') | ||||
| 		} | ||||
| 		C.SetConsoleMode(ctx_ptr.stdin_handle, stdin_at_startup) | ||||
| 	} | ||||
| 	load_title() | ||||
| 	os.flush() | ||||
| } | ||||
| 
 | ||||
| pub fn init(cfg Config) &Context { | ||||
| 	panic(not_implemented_yet) | ||||
| 	return &Context{} | ||||
| 	mut ctx := &Context{ | ||||
| 		cfg: cfg | ||||
| 	} | ||||
| 	// get the standard input handle
 | ||||
| 	stdin_handle := C.GetStdHandle(C.STD_INPUT_HANDLE) | ||||
| 	stdout_handle := C.GetStdHandle(C.STD_OUTPUT_HANDLE) | ||||
| 	if stdin_handle == C.INVALID_HANDLE_VALUE { | ||||
| 		panic('could not get stdin handle') | ||||
| 	} | ||||
| 	// save the current input mode, to be restored on exit
 | ||||
| 	if C.GetConsoleMode(stdin_handle, &stdin_at_startup) == 0 { | ||||
| 		panic('could not get stdin console mode') | ||||
| 	} | ||||
| 
 | ||||
| 	// enable extended input flags (see https://stackoverflow.com/a/46802726)
 | ||||
| 	// 0x80 == C.ENABLE_EXTENDED_FLAGS
 | ||||
| 	if C.SetConsoleMode(stdin_handle, 0x80) == 0 { | ||||
| 		panic('could not set raw input mode') | ||||
| 	} | ||||
| 	// enable window and mouse input events.
 | ||||
| 	if C.SetConsoleMode(stdin_handle, C.ENABLE_WINDOW_INPUT | C.ENABLE_MOUSE_INPUT) == 0 { | ||||
| 		panic('could not set raw input mode') | ||||
| 	} | ||||
| 	// store the current title, so restore_terminal_state can get it back
 | ||||
| 	save_title() | ||||
| 
 | ||||
| 	if ctx.cfg.use_alternate_buffer { | ||||
| 		// switch to the alternate buffer
 | ||||
| 		print('\x1b[?1049h') | ||||
| 		// clear the terminal and set the cursor to the origin
 | ||||
| 		print('\x1b[2J\x1b[3J\x1b[1;1H') | ||||
| 	} | ||||
| 
 | ||||
| 	if ctx.cfg.hide_cursor { | ||||
| 		print('\x1b[?25l') | ||||
| 	} | ||||
| 
 | ||||
| 	if ctx.cfg.window_title != '' { | ||||
| 		print('\x1b]0;$ctx.cfg.window_title\x07') | ||||
| 	} | ||||
| 
 | ||||
| 	unsafe { | ||||
| 		x := &ctx_ptr | ||||
| 		*x = ctx | ||||
| 	} | ||||
| 
 | ||||
| 	C.atexit(restore_terminal_state) | ||||
| 	for code in ctx.cfg.reset { | ||||
| 		os.signal(code, fn() { | ||||
| 			mut c := ctx_ptr | ||||
| 			if c != 0 { | ||||
| 				c.cleanup() | ||||
| 			} | ||||
| 			exit(0) | ||||
| 		}) | ||||
| 	} | ||||
| 
 | ||||
| 	ctx.stdin_handle = stdin_handle | ||||
| 	ctx.stdout_handle = stdout_handle | ||||
| 	return ctx | ||||
| } | ||||
| 
 | ||||
| pub fn (mut ctx Context) run() ? { | ||||
| 	panic(not_implemented_yet) | ||||
| 	frame_time := 1_000_000 / ctx.cfg.frame_rate | ||||
| 	mut init_called := false | ||||
| 	mut sw := time.new_stopwatch(auto_start: false) | ||||
| 	mut sleep_len := 0 | ||||
| 	for { | ||||
| 		if !init_called { | ||||
| 			ctx.init() | ||||
| 			init_called = true | ||||
| 		} | ||||
| 		if sleep_len > 0 { | ||||
| 			time.usleep(sleep_len) | ||||
| 		} | ||||
| 		if !ctx.paused { | ||||
| 			sw.restart() | ||||
| 			if ctx.cfg.event_fn != voidptr(0) { | ||||
| 				ctx.parse_events() | ||||
| 			} | ||||
| 			ctx.frame() | ||||
| 			sw.pause() | ||||
| 			e := sw.elapsed().microseconds() | ||||
| 			sleep_len = frame_time - int(e) | ||||
| 			ctx.frame_count++ | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| pub fn (mut ctx Context) save_title() { | ||||
| 	panic(not_implemented_yet) | ||||
| fn (mut ctx Context) parse_events() { | ||||
| 	nr_events := u32(0) | ||||
| 	if !C.GetNumberOfConsoleInputEvents(ctx.stdin_handle, &nr_events) { | ||||
| 		panic('could not get number of events in stdin') | ||||
| 	} | ||||
| 	if nr_events < 1 { return } | ||||
| 
 | ||||
| 	// print('$nr_events | ')
 | ||||
| 	if !C.ReadConsoleInput(ctx.stdin_handle, ctx.read_buf, buf_size, &nr_events) { | ||||
| 		panic('could not read from stdin') | ||||
| 	} | ||||
| 	for i in 0 .. nr_events { | ||||
| 		// print('E ')
 | ||||
| 		match int(ctx.read_buf[i].EventType) { | ||||
| 			C.KEY_EVENT { | ||||
| 				e := unsafe { ctx.read_buf[i].Event.KeyEvent } | ||||
| 				ch := e.wVirtualKeyCode | ||||
| 				ascii := unsafe { e.uChar.AsciiChar } | ||||
| 				if e.bKeyDown == 0 { continue } // we don't handle key_up events because they don't exist on linux...
 | ||||
| 				// see: https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
 | ||||
| 				code := match int(ch) { | ||||
| 					C.VK_BACK { KeyCode.backspace } | ||||
| 					C.VK_RETURN { KeyCode.enter } | ||||
| 					C.VK_PRIOR { KeyCode.page_up } | ||||
| 					14 ... 20 { KeyCode.null } | ||||
| 					C.VK_NEXT { KeyCode.page_down } | ||||
| 					C.VK_END { KeyCode.end } | ||||
| 					C.VK_HOME { KeyCode.home } | ||||
| 					C.VK_LEFT { KeyCode.left } | ||||
| 					C.VK_UP { KeyCode.up } | ||||
| 					C.VK_RIGHT { KeyCode.right } | ||||
| 					C.VK_DOWN { KeyCode.down } | ||||
| 					C.VK_INSERT { KeyCode.insert } | ||||
| 					C.VK_DELETE { KeyCode.delete } | ||||
| 					65 ... 90 { KeyCode(ch + 32) } // letters
 | ||||
| 					91 ... 93 { KeyCode.null } // special keys
 | ||||
| 					96 ... 105 { KeyCode(ch - 48) } // numpad numbers
 | ||||
| 					112 ... 135 { KeyCode(ch + 178) } // f1 - f24
 | ||||
| 					else { KeyCode(ascii) } | ||||
| 				} | ||||
| 
 | ||||
| 				mut modifiers := u32(0) | ||||
| 				if e.dwControlKeyState & (0x1 | 0x2) != 0 { modifiers |= alt } | ||||
| 				if e.dwControlKeyState & (0x4 | 0x8) != 0 { modifiers |= ctrl } | ||||
| 				if e.dwControlKeyState & 0x10        != 0 { modifiers |= shift } | ||||
| 
 | ||||
| 				mut event := &Event{ | ||||
| 					typ: .key_down | ||||
| 					modifiers: modifiers | ||||
| 					code: code | ||||
| 					ascii: ascii | ||||
| 					width: int(e.dwControlKeyState) | ||||
| 					height: int(e.wVirtualKeyCode) | ||||
| 					utf8: unsafe { e.uChar.UnicodeChar.str() } | ||||
| 				} | ||||
| 				ctx.event(event) | ||||
| 			} | ||||
| 			C.MOUSE_EVENT { | ||||
| 				e := unsafe { ctx.read_buf[i].Event.MouseEvent } | ||||
| 				sb_info := C.CONSOLE_SCREEN_BUFFER_INFO{} | ||||
| 				if !C.GetConsoleScreenBufferInfo(ctx.stdout_handle, &sb_info) { | ||||
| 					panic('could not get screenbuffer info') | ||||
| 				} | ||||
| 				x := e.dwMousePosition.X | ||||
| 				y := int(e.dwMousePosition.Y) - sb_info.srWindow.Top | ||||
| 				mut modifiers := u32(0) | ||||
| 				if e.dwControlKeyState & (0x1 | 0x2) != 0 { modifiers |= alt } | ||||
| 				if e.dwControlKeyState & (0x4 | 0x8) != 0 { modifiers |= ctrl } | ||||
| 				if e.dwControlKeyState & 0x10        != 0 { modifiers |= shift } | ||||
| 				// TODO: handle capslock/numlock/etc?? events exist for those keys
 | ||||
| 				match int(e.dwEventFlags) { | ||||
| 					C.MOUSE_MOVED { | ||||
| 						mut button := match int(e.dwButtonState) { | ||||
| 							0 { MouseButton.unknown } | ||||
| 							1 { MouseButton.left } | ||||
| 							2 { MouseButton.right } | ||||
| 							else { MouseButton.middle } | ||||
| 						} | ||||
| 						typ := if e.dwButtonState == 0 { | ||||
| 							if ctx.mouse_down != .unknown { | ||||
| 								button = ctx.mouse_down | ||||
| 								ctx.mouse_down = .unknown | ||||
| 								EventType.mouse_up | ||||
| 							} else { | ||||
| 								EventType.mouse_move | ||||
| 							} | ||||
| 						} else { | ||||
| 							EventType.mouse_drag | ||||
| 						} | ||||
| 						ctx.event(&Event{ | ||||
| 							typ: typ | ||||
| 							x: x | ||||
| 							y: y | ||||
| 							button: button | ||||
| 							modifiers: modifiers | ||||
| 						}) | ||||
| 					} C.MOUSE_WHEELED { | ||||
| 						ctx.event(&Event{ | ||||
| 							typ: .mouse_scroll | ||||
| 							direction: if i16(e.dwButtonState >> 16) < 0 { Direction.up } else { Direction.down } | ||||
| 							x: x | ||||
| 							y: y | ||||
| 							modifiers: modifiers | ||||
| 						}) | ||||
| 					} 0x0008 /* C.MOUSE_HWHEELED */ { | ||||
| 						ctx.event(&Event{ | ||||
| 							typ: .mouse_scroll | ||||
| 							direction: if i16(e.dwButtonState >> 16) < 0 { Direction.right } else { Direction.left } | ||||
| 							x: x | ||||
| 							y: y | ||||
| 							modifiers: modifiers | ||||
| 						}) | ||||
| 					} 0 /* CLICK */, C.DOUBLE_CLICK { | ||||
| 						button := match int(e.dwButtonState) { | ||||
| 							0 { ctx.mouse_down } | ||||
| 							1 { MouseButton.left } | ||||
| 							2 { MouseButton.right } | ||||
| 							else { MouseButton.middle } | ||||
| 						} | ||||
| 						ctx.mouse_down = button | ||||
| 						ctx.event(&Event{ | ||||
| 							typ: .mouse_down | ||||
| 							x: x | ||||
| 							y: y | ||||
| 							button: button | ||||
| 							modifiers: modifiers | ||||
| 						}) | ||||
| 					} else {} | ||||
| 				} | ||||
| 			} | ||||
| 			C.WINDOW_BUFFER_SIZE_EVENT { | ||||
| 				// e := unsafe { ctx.read_buf[i].Event.WindowBufferSizeEvent }
 | ||||
| 				sb := C.CONSOLE_SCREEN_BUFFER_INFO{} | ||||
| 				if !C.GetConsoleScreenBufferInfo(ctx.stdout_handle, &sb) { | ||||
| 					panic('could not get screenbuffer info') | ||||
| 				} | ||||
| 				w := sb.srWindow.Right - sb.srWindow.Left + 1 | ||||
| 				h := sb.srWindow.Bottom - sb.srWindow.Top + 1 | ||||
| 				utf8 := '($ctx.window_width, $ctx.window_height) -> ($w, $h)' | ||||
| 				if w != ctx.window_width || h != ctx.window_height { | ||||
| 					ctx.window_width, ctx.window_height = w, h | ||||
| 					mut event := &Event{ | ||||
| 						typ: .resized | ||||
| 						width: ctx.window_width | ||||
| 						height: ctx.window_height | ||||
| 						utf8: utf8 | ||||
| 					} | ||||
| 					ctx.event(event) | ||||
| 				} | ||||
| 			} | ||||
| 			// C.MENU_EVENT {
 | ||||
| 			// 	e := unsafe { ctx.read_buf[i].Event.MenuEvent }
 | ||||
| 			// }
 | ||||
| 			// C.FOCUS_EVENT {
 | ||||
| 			// 	e := unsafe { ctx.read_buf[i].Event.FocusEvent }
 | ||||
| 			// }
 | ||||
| 			else {} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| pub fn (mut ctx Context) load_title() { | ||||
| 	panic(not_implemented_yet) | ||||
| [inline] | ||||
| fn save_title() { | ||||
|     // restore the previously saved terminal title
 | ||||
|     print('\x1b[22;0t') | ||||
| } | ||||
| 
 | ||||
| [inline] | ||||
| fn load_title() { | ||||
|     // restore the previously saved terminal title
 | ||||
|     print('\x1b[23;0t') | ||||
| } | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| // Copyright (c) 2020 Raúl Hernández. All rights reserved.
 | ||||
| // Copyright (c) 2020-2021 Raúl Hernández. All rights reserved.
 | ||||
| // Use of this source code is governed by an MIT license
 | ||||
| // that can be found in the LICENSE file.
 | ||||
| module ui | ||||
|  | @ -50,14 +50,14 @@ fn restore_terminal_state() { | |||
| 	mut c := ctx_ptr | ||||
| 	if c != 0 { | ||||
| 		c.paused = true | ||||
| 		c.load_title() | ||||
| 		load_title() | ||||
| 	} | ||||
| 	os.flush() | ||||
| } | ||||
| 
 | ||||
| fn (mut ctx Context) termios_setup() ? { | ||||
| 	// store the current title, so restore_terminal_state can get it back
 | ||||
| 	ctx.save_title() | ||||
| 	save_title() | ||||
| 
 | ||||
| 	if !ctx.cfg.skip_init_checks && !(is_atty(C.STDIN_FILENO) != 0 && is_atty(C.STDOUT_FILENO) != 0) { | ||||
| 		return error('not running under a TTY') | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| // Copyright (c) 2020 Raúl Hernández. All rights reserved.
 | ||||
| // Copyright (c) 2020-2021 Raúl Hernández. All rights reserved.
 | ||||
| // Use of this source code is governed by an MIT license
 | ||||
| // that can be found in the LICENSE file.
 | ||||
| module ui | ||||
|  | @ -35,19 +35,15 @@ pub fn (mut ctx Context) write(s string) { | |||
| [inline] | ||||
| // flush displays the accumulated print buffer to the screen.
 | ||||
| pub fn (mut ctx Context) flush() { | ||||
| 	$if windows { | ||||
| 		// TODO
 | ||||
| 	} $else { | ||||
| 	// TODO: Diff the previous frame against this one, and only render things that changed?
 | ||||
| 	if !ctx.enable_su { | ||||
| 			C.write(C.STDOUT_FILENO, ctx.print_buf.data, ctx.print_buf.len) | ||||
| 		C.write(1, ctx.print_buf.data, ctx.print_buf.len) | ||||
| 	} else { | ||||
| 			C.write(C.STDOUT_FILENO, bsu.str, bsu.len) | ||||
| 			C.write(C.STDOUT_FILENO, ctx.print_buf.data, ctx.print_buf.len) | ||||
| 			C.write(C.STDOUT_FILENO, esu.str, esu.len) | ||||
| 		C.write(1, bsu.str, bsu.len) | ||||
| 		C.write(1, ctx.print_buf.data, ctx.print_buf.len) | ||||
| 		C.write(1, esu.str, esu.len) | ||||
| 	} | ||||
| 	ctx.print_buf.clear() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // bold sets the character state to bold.
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue