aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/vlib/term/ui/input_windows.c.v
diff options
context:
space:
mode:
Diffstat (limited to 'v_windows/v/vlib/term/ui/input_windows.c.v')
-rw-r--r--v_windows/v/vlib/term/ui/input_windows.c.v326
1 files changed, 326 insertions, 0 deletions
diff --git a/v_windows/v/vlib/term/ui/input_windows.c.v b/v_windows/v/vlib/term/ui/input_windows.c.v
new file mode 100644
index 0000000..bd9782d
--- /dev/null
+++ b/v_windows/v/vlib/term/ui/input_windows.c.v
@@ -0,0 +1,326 @@
+// 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 (
+ 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 ui.ctx_ptr != 0 {
+ if ui.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(ui.ctx_ptr.stdin_handle, ui.stdin_at_startup)
+ }
+ load_title()
+ os.flush()
+}
+
+pub fn init(cfg Config) &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, &ui.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 {
+ ctx.hide_cursor()
+ ctx.flush()
+ }
+
+ if ctx.cfg.window_title != '' {
+ print('\x1b]0;$ctx.cfg.window_title\x07')
+ }
+
+ unsafe {
+ x := &ui.ctx_ptr
+ *x = ctx
+ }
+ C.atexit(restore_terminal_state)
+ for code in ctx.cfg.reset {
+ os.signal_opt(code, fn (_ os.Signal) {
+ mut c := ui.ctx_ptr
+ if c != 0 {
+ c.cleanup()
+ }
+ exit(0)
+ }) or {}
+ }
+
+ ctx.stdin_handle = stdin_handle
+ ctx.stdout_handle = stdout_handle
+ return ctx
+}
+
+pub fn (mut ctx Context) run() ? {
+ 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.sleep(sleep_len * time.microsecond)
+ }
+ 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++
+ }
+ }
+}
+
+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[0], ui.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 := Modifiers{}
+ if e.dwControlKeyState & (0x1 | 0x2) != 0 {
+ modifiers.set(.alt)
+ }
+ if e.dwControlKeyState & (0x4 | 0x8) != 0 {
+ modifiers.set(.ctrl)
+ }
+ if e.dwControlKeyState & 0x10 != 0 {
+ modifiers.set(.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 + 1
+ y := int(e.dwMousePosition.Y) - sb_info.srWindow.Top + 1
+ mut modifiers := Modifiers{}
+ if e.dwControlKeyState & (0x1 | 0x2) != 0 {
+ modifiers.set(.alt)
+ }
+ if e.dwControlKeyState & (0x4 | 0x8) != 0 {
+ modifiers.set(.ctrl)
+ }
+ if e.dwControlKeyState & 0x10 != 0 {
+ modifiers.set(.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 {}
+ }
+ }
+}
+
+[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')
+}