term.ui: native Windows console implementation (#8359)
parent
2ada7b730e
commit
e233911a7b
|
@ -505,6 +505,9 @@ fn event(e &tui.Event, x voidptr) {
|
||||||
.escape {
|
.escape {
|
||||||
exit(0)
|
exit(0)
|
||||||
}
|
}
|
||||||
|
.enter {
|
||||||
|
buffer.put('\n')
|
||||||
|
}
|
||||||
.backspace {
|
.backspace {
|
||||||
buffer.del(-1)
|
buffer.del(-1)
|
||||||
}
|
}
|
||||||
|
@ -552,8 +555,8 @@ fn event(e &tui.Event, x voidptr) {
|
||||||
if e.code == .s {
|
if e.code == .s {
|
||||||
a.save()
|
a.save()
|
||||||
}
|
}
|
||||||
} else if e.modifiers in [tui.shift, 0] {
|
} else if e.modifiers in [tui.shift, 0] && e.code != .null {
|
||||||
buffer.put(e.ascii.str())
|
buffer.put(e.ascii.ascii_str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -2,31 +2,33 @@ module term
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
pub struct Coord16 {
|
[typedef]
|
||||||
pub:
|
struct C.COORD {
|
||||||
x i16
|
X i16
|
||||||
y i16
|
Y i16
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SmallRect {
|
[typedef]
|
||||||
left i16
|
struct C.SMALL_RECT {
|
||||||
top i16
|
Left u16
|
||||||
right i16
|
Top u16
|
||||||
bottom i16
|
Right u16
|
||||||
|
Bottom u16
|
||||||
}
|
}
|
||||||
|
|
||||||
// win: CONSOLE_SCREEN_BUFFER_INFO
|
// win: CONSOLE_SCREEN_BUFFER_INFO
|
||||||
// https://docs.microsoft.com/en-us/windows/console/console-screen-buffer-info-str
|
// https://docs.microsoft.com/en-us/windows/console/console-screen-buffer-info-str
|
||||||
struct ConsoleScreenBufferInfo {
|
[typedef]
|
||||||
dw_size Coord16
|
struct C.CONSOLE_SCREEN_BUFFER_INFO {
|
||||||
dw_cursor_position Coord16
|
dwSize C.COORD
|
||||||
w_attributes u16
|
dwCursorPosition C.COORD
|
||||||
sr_window SmallRect
|
wAttributes u16
|
||||||
dw_maximum_window_size Coord16
|
srWindow C.SMALL_RECT
|
||||||
|
dwMaximumWindowSize C.COORD
|
||||||
}
|
}
|
||||||
|
|
||||||
// ref - https://docs.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo
|
// 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
|
// ref - https://docs.microsoft.com/en-us/windows/console/setconsoletitle
|
||||||
fn C.SetConsoleTitle(title &u16) bool
|
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.
|
// get_terminal_size returns a number of colums and rows of terminal window.
|
||||||
pub fn get_terminal_size() (int, int) {
|
pub fn get_terminal_size() (int, int) {
|
||||||
if is_atty(1) > 0 && os.getenv('TERM') != 'dumb' {
|
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) {
|
if C.GetConsoleScreenBufferInfo(C.GetStdHandle(C.STD_OUTPUT_HANDLE), &info) {
|
||||||
columns := int(info.sr_window.right - info.sr_window.left + 1)
|
columns := int(info.srWindow.Right - info.srWindow.Left + 1)
|
||||||
rows := int(info.sr_window.bottom - info.sr_window.top + 1)
|
rows := int(info.srWindow.Bottom - info.srWindow.Top + 1)
|
||||||
return columns, rows
|
return columns, rows
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,10 +50,10 @@ pub fn get_terminal_size() (int, int) {
|
||||||
pub fn get_cursor_position() Coord {
|
pub fn get_cursor_position() Coord {
|
||||||
mut res := Coord{}
|
mut res := Coord{}
|
||||||
if is_atty(1) > 0 && os.getenv('TERM') != 'dumb' {
|
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) {
|
if C.GetConsoleScreenBufferInfo(C.GetStdHandle(C.STD_OUTPUT_HANDLE), &info) {
|
||||||
res.x = info.dw_cursor_position.x
|
res.x = info.dwCursorPosition.X
|
||||||
res.y = info.dw_cursor_position.y
|
res.y = info.dwCursorPosition.Y
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res
|
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
|
// Use of this source code is governed by an MIT license
|
||||||
// that can be found in the LICENSE file.
|
// that can be found in the LICENSE file.
|
||||||
module ui
|
module ui
|
||||||
|
@ -108,6 +108,18 @@ pub enum KeyCode {
|
||||||
f10 = 299
|
f10 = 299
|
||||||
f11 = 300
|
f11 = 300
|
||||||
f12 = 301
|
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 (
|
pub const (
|
||||||
|
@ -164,10 +176,10 @@ pub:
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
|
ExtraContext // contains fields specific to an implementation
|
||||||
pub:
|
pub:
|
||||||
cfg Config
|
cfg Config
|
||||||
mut:
|
mut:
|
||||||
read_buf []byte
|
|
||||||
print_buf []byte
|
print_buf []byte
|
||||||
paused bool
|
paused bool
|
||||||
enable_su bool
|
enable_su bool
|
||||||
|
@ -198,3 +210,38 @@ pub struct Config {
|
||||||
// All kill signals to set up exit listeners on
|
// All kill signals to set up exit listeners on
|
||||||
reset []int = [1, 2, 3, 4, 6, 7, 8, 9, 11, 13, 14, 15, 19]
|
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
|
// Use of this source code is governed by an MIT license
|
||||||
// that can be found in the LICENSE file.
|
// that can be found in the LICENSE file.
|
||||||
module ui
|
module ui
|
||||||
|
|
||||||
|
|
||||||
|
struct ExtraContext {
|
||||||
|
mut:
|
||||||
|
read_buf []byte
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ctx_ptr = &Context(0)
|
ctx_ptr = &Context(0)
|
||||||
)
|
)
|
||||||
|
@ -10,8 +16,8 @@ const (
|
||||||
pub fn init(cfg Config) &Context {
|
pub fn init(cfg Config) &Context {
|
||||||
mut ctx := &Context{
|
mut ctx := &Context{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
read_buf: []byte{ cap: cfg.buffer_size }
|
|
||||||
}
|
}
|
||||||
|
ctx.read_buf = []byte{ cap: cfg.buffer_size }
|
||||||
|
|
||||||
// lmao
|
// lmao
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -22,11 +28,14 @@ pub fn init(cfg Config) &Context {
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut ctx Context) save_title() {
|
[inline]
|
||||||
|
fn save_title() {
|
||||||
// restore the previously saved terminal title
|
// restore the previously saved terminal title
|
||||||
print('\x1b[22;0t')
|
print('\x1b[22;0t')
|
||||||
}
|
}
|
||||||
pub fn (mut ctx Context) load_title() {
|
|
||||||
|
[inline]
|
||||||
|
fn load_title() {
|
||||||
// restore the previously saved terminal title
|
// restore the previously saved terminal title
|
||||||
print('\x1b[23;0t')
|
print('\x1b[23;0t')
|
||||||
}
|
}
|
||||||
|
@ -57,38 +66,3 @@ fn (mut ctx Context) resize_arr(size int) {
|
||||||
mut l := &ctx.read_buf.len
|
mut l := &ctx.read_buf.len
|
||||||
unsafe { *l = size }
|
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
|
// Use of this source code is governed by an MIT license
|
||||||
// that can be found in the LICENSE file.
|
// that can be found in the LICENSE file.
|
||||||
module ui
|
module ui
|
||||||
|
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
const (
|
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 {
|
pub fn init(cfg Config) &Context {
|
||||||
panic(not_implemented_yet)
|
mut ctx := &Context{
|
||||||
return &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() ? {
|
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() {
|
fn (mut ctx Context) parse_events() {
|
||||||
panic(not_implemented_yet)
|
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() {
|
[inline]
|
||||||
panic(not_implemented_yet)
|
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
|
// Use of this source code is governed by an MIT license
|
||||||
// that can be found in the LICENSE file.
|
// that can be found in the LICENSE file.
|
||||||
module ui
|
module ui
|
||||||
|
@ -50,14 +50,14 @@ fn restore_terminal_state() {
|
||||||
mut c := ctx_ptr
|
mut c := ctx_ptr
|
||||||
if c != 0 {
|
if c != 0 {
|
||||||
c.paused = true
|
c.paused = true
|
||||||
c.load_title()
|
load_title()
|
||||||
}
|
}
|
||||||
os.flush()
|
os.flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut ctx Context) termios_setup() ? {
|
fn (mut ctx Context) termios_setup() ? {
|
||||||
// store the current title, so restore_terminal_state can get it back
|
// 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) {
|
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')
|
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
|
// Use of this source code is governed by an MIT license
|
||||||
// that can be found in the LICENSE file.
|
// that can be found in the LICENSE file.
|
||||||
module ui
|
module ui
|
||||||
|
@ -35,19 +35,15 @@ pub fn (mut ctx Context) write(s string) {
|
||||||
[inline]
|
[inline]
|
||||||
// flush displays the accumulated print buffer to the screen.
|
// flush displays the accumulated print buffer to the screen.
|
||||||
pub fn (mut ctx Context) flush() {
|
pub fn (mut ctx Context) flush() {
|
||||||
$if windows {
|
// TODO: Diff the previous frame against this one, and only render things that changed?
|
||||||
// TODO
|
if !ctx.enable_su {
|
||||||
} $else {
|
C.write(1, ctx.print_buf.data, ctx.print_buf.len)
|
||||||
// TODO: Diff the previous frame against this one, and only render things that changed?
|
} else {
|
||||||
if !ctx.enable_su {
|
C.write(1, bsu.str, bsu.len)
|
||||||
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(1, esu.str, esu.len)
|
||||||
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)
|
|
||||||
}
|
|
||||||
ctx.print_buf.clear()
|
|
||||||
}
|
}
|
||||||
|
ctx.print_buf.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
// bold sets the character state to bold.
|
// bold sets the character state to bold.
|
||||||
|
|
Loading…
Reference in New Issue