2017-06-15 11:40:39 +00:00
|
|
|
package termbox
|
|
|
|
|
2018-05-02 16:09:45 +00:00
|
|
|
import "math"
|
2017-06-15 11:40:39 +00:00
|
|
|
import "syscall"
|
|
|
|
import "unsafe"
|
|
|
|
import "unicode/utf16"
|
2019-08-14 13:56:32 +00:00
|
|
|
import "github.com/mattn/go-runewidth"
|
2017-06-15 11:40:39 +00:00
|
|
|
|
|
|
|
type (
|
|
|
|
wchar uint16
|
|
|
|
short int16
|
|
|
|
dword uint32
|
|
|
|
word uint16
|
|
|
|
char_info struct {
|
|
|
|
char wchar
|
|
|
|
attr word
|
|
|
|
}
|
|
|
|
coord struct {
|
|
|
|
x short
|
|
|
|
y short
|
|
|
|
}
|
|
|
|
small_rect struct {
|
|
|
|
left short
|
|
|
|
top short
|
|
|
|
right short
|
|
|
|
bottom short
|
|
|
|
}
|
|
|
|
console_screen_buffer_info struct {
|
|
|
|
size coord
|
|
|
|
cursor_position coord
|
|
|
|
attributes word
|
|
|
|
window small_rect
|
|
|
|
maximum_window_size coord
|
|
|
|
}
|
|
|
|
console_cursor_info struct {
|
|
|
|
size dword
|
|
|
|
visible int32
|
|
|
|
}
|
|
|
|
input_record struct {
|
|
|
|
event_type word
|
|
|
|
_ [2]byte
|
|
|
|
event [16]byte
|
|
|
|
}
|
|
|
|
key_event_record struct {
|
|
|
|
key_down int32
|
|
|
|
repeat_count word
|
|
|
|
virtual_key_code word
|
|
|
|
virtual_scan_code word
|
|
|
|
unicode_char wchar
|
|
|
|
control_key_state dword
|
|
|
|
}
|
|
|
|
window_buffer_size_record struct {
|
|
|
|
size coord
|
|
|
|
}
|
|
|
|
mouse_event_record struct {
|
|
|
|
mouse_pos coord
|
|
|
|
button_state dword
|
|
|
|
control_key_state dword
|
|
|
|
event_flags dword
|
|
|
|
}
|
2018-05-02 16:09:45 +00:00
|
|
|
console_font_info struct {
|
|
|
|
font uint32
|
|
|
|
font_size coord
|
|
|
|
}
|
2017-06-15 11:40:39 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
mouse_lmb = 0x1
|
|
|
|
mouse_rmb = 0x2
|
|
|
|
mouse_mmb = 0x4 | 0x8 | 0x10
|
2018-01-16 13:20:59 +00:00
|
|
|
SM_CXMIN = 28
|
|
|
|
SM_CYMIN = 29
|
2017-06-15 11:40:39 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func (this coord) uintptr() uintptr {
|
|
|
|
return uintptr(*(*int32)(unsafe.Pointer(&this)))
|
|
|
|
}
|
|
|
|
|
2018-11-24 15:31:25 +00:00
|
|
|
func (this *small_rect) uintptr() uintptr {
|
|
|
|
return uintptr(unsafe.Pointer(this))
|
|
|
|
}
|
|
|
|
|
2017-06-15 11:40:39 +00:00
|
|
|
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
2018-01-16 13:20:59 +00:00
|
|
|
var moduser32 = syscall.NewLazyDLL("user32.dll")
|
2017-06-15 11:40:39 +00:00
|
|
|
var is_cjk = runewidth.IsEastAsian()
|
|
|
|
|
|
|
|
var (
|
|
|
|
proc_set_console_active_screen_buffer = kernel32.NewProc("SetConsoleActiveScreenBuffer")
|
|
|
|
proc_set_console_screen_buffer_size = kernel32.NewProc("SetConsoleScreenBufferSize")
|
2018-11-24 15:31:25 +00:00
|
|
|
proc_set_console_window_info = kernel32.NewProc("SetConsoleWindowInfo")
|
2017-06-15 11:40:39 +00:00
|
|
|
proc_create_console_screen_buffer = kernel32.NewProc("CreateConsoleScreenBuffer")
|
|
|
|
proc_get_console_screen_buffer_info = kernel32.NewProc("GetConsoleScreenBufferInfo")
|
|
|
|
proc_write_console_output = kernel32.NewProc("WriteConsoleOutputW")
|
|
|
|
proc_write_console_output_character = kernel32.NewProc("WriteConsoleOutputCharacterW")
|
|
|
|
proc_write_console_output_attribute = kernel32.NewProc("WriteConsoleOutputAttribute")
|
|
|
|
proc_set_console_cursor_info = kernel32.NewProc("SetConsoleCursorInfo")
|
|
|
|
proc_set_console_cursor_position = kernel32.NewProc("SetConsoleCursorPosition")
|
|
|
|
proc_get_console_cursor_info = kernel32.NewProc("GetConsoleCursorInfo")
|
|
|
|
proc_read_console_input = kernel32.NewProc("ReadConsoleInputW")
|
|
|
|
proc_get_console_mode = kernel32.NewProc("GetConsoleMode")
|
|
|
|
proc_set_console_mode = kernel32.NewProc("SetConsoleMode")
|
|
|
|
proc_fill_console_output_character = kernel32.NewProc("FillConsoleOutputCharacterW")
|
|
|
|
proc_fill_console_output_attribute = kernel32.NewProc("FillConsoleOutputAttribute")
|
|
|
|
proc_create_event = kernel32.NewProc("CreateEventW")
|
|
|
|
proc_wait_for_multiple_objects = kernel32.NewProc("WaitForMultipleObjects")
|
|
|
|
proc_set_event = kernel32.NewProc("SetEvent")
|
2018-05-02 16:09:45 +00:00
|
|
|
proc_get_current_console_font = kernel32.NewProc("GetCurrentConsoleFont")
|
2018-01-16 13:20:59 +00:00
|
|
|
get_system_metrics = moduser32.NewProc("GetSystemMetrics")
|
2017-06-15 11:40:39 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func set_console_active_screen_buffer(h syscall.Handle) (err error) {
|
|
|
|
r0, _, e1 := syscall.Syscall(proc_set_console_active_screen_buffer.Addr(),
|
|
|
|
1, uintptr(h), 0, 0)
|
|
|
|
if int(r0) == 0 {
|
|
|
|
if e1 != 0 {
|
|
|
|
err = error(e1)
|
|
|
|
} else {
|
|
|
|
err = syscall.EINVAL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func set_console_screen_buffer_size(h syscall.Handle, size coord) (err error) {
|
|
|
|
r0, _, e1 := syscall.Syscall(proc_set_console_screen_buffer_size.Addr(),
|
|
|
|
2, uintptr(h), size.uintptr(), 0)
|
|
|
|
if int(r0) == 0 {
|
|
|
|
if e1 != 0 {
|
|
|
|
err = error(e1)
|
|
|
|
} else {
|
|
|
|
err = syscall.EINVAL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-11-24 15:31:25 +00:00
|
|
|
func set_console_window_info(h syscall.Handle, window *small_rect) (err error) {
|
|
|
|
var absolute uint32
|
|
|
|
absolute = 1
|
|
|
|
r0, _, e1 := syscall.Syscall(proc_set_console_window_info.Addr(),
|
|
|
|
3, uintptr(h), uintptr(absolute), window.uintptr())
|
|
|
|
if int(r0) == 0 {
|
|
|
|
if e1 != 0 {
|
|
|
|
err = error(e1)
|
|
|
|
} else {
|
|
|
|
err = syscall.EINVAL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-06-15 11:40:39 +00:00
|
|
|
func create_console_screen_buffer() (h syscall.Handle, err error) {
|
|
|
|
r0, _, e1 := syscall.Syscall6(proc_create_console_screen_buffer.Addr(),
|
|
|
|
5, uintptr(generic_read|generic_write), 0, 0, console_textmode_buffer, 0, 0)
|
|
|
|
if int(r0) == 0 {
|
|
|
|
if e1 != 0 {
|
|
|
|
err = error(e1)
|
|
|
|
} else {
|
|
|
|
err = syscall.EINVAL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return syscall.Handle(r0), err
|
|
|
|
}
|
|
|
|
|
|
|
|
func get_console_screen_buffer_info(h syscall.Handle, info *console_screen_buffer_info) (err error) {
|
|
|
|
r0, _, e1 := syscall.Syscall(proc_get_console_screen_buffer_info.Addr(),
|
|
|
|
2, uintptr(h), uintptr(unsafe.Pointer(info)), 0)
|
|
|
|
if int(r0) == 0 {
|
|
|
|
if e1 != 0 {
|
|
|
|
err = error(e1)
|
|
|
|
} else {
|
|
|
|
err = syscall.EINVAL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func write_console_output(h syscall.Handle, chars []char_info, dst small_rect) (err error) {
|
|
|
|
tmp_coord = coord{dst.right - dst.left + 1, dst.bottom - dst.top + 1}
|
|
|
|
tmp_rect = dst
|
|
|
|
r0, _, e1 := syscall.Syscall6(proc_write_console_output.Addr(),
|
|
|
|
5, uintptr(h), uintptr(unsafe.Pointer(&chars[0])), tmp_coord.uintptr(),
|
|
|
|
tmp_coord0.uintptr(), uintptr(unsafe.Pointer(&tmp_rect)), 0)
|
|
|
|
if int(r0) == 0 {
|
|
|
|
if e1 != 0 {
|
|
|
|
err = error(e1)
|
|
|
|
} else {
|
|
|
|
err = syscall.EINVAL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func write_console_output_character(h syscall.Handle, chars []wchar, pos coord) (err error) {
|
|
|
|
r0, _, e1 := syscall.Syscall6(proc_write_console_output_character.Addr(),
|
|
|
|
5, uintptr(h), uintptr(unsafe.Pointer(&chars[0])), uintptr(len(chars)),
|
|
|
|
pos.uintptr(), uintptr(unsafe.Pointer(&tmp_arg)), 0)
|
|
|
|
if int(r0) == 0 {
|
|
|
|
if e1 != 0 {
|
|
|
|
err = error(e1)
|
|
|
|
} else {
|
|
|
|
err = syscall.EINVAL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func write_console_output_attribute(h syscall.Handle, attrs []word, pos coord) (err error) {
|
|
|
|
r0, _, e1 := syscall.Syscall6(proc_write_console_output_attribute.Addr(),
|
|
|
|
5, uintptr(h), uintptr(unsafe.Pointer(&attrs[0])), uintptr(len(attrs)),
|
|
|
|
pos.uintptr(), uintptr(unsafe.Pointer(&tmp_arg)), 0)
|
|
|
|
if int(r0) == 0 {
|
|
|
|
if e1 != 0 {
|
|
|
|
err = error(e1)
|
|
|
|
} else {
|
|
|
|
err = syscall.EINVAL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func set_console_cursor_info(h syscall.Handle, info *console_cursor_info) (err error) {
|
|
|
|
r0, _, e1 := syscall.Syscall(proc_set_console_cursor_info.Addr(),
|
|
|
|
2, uintptr(h), uintptr(unsafe.Pointer(info)), 0)
|
|
|
|
if int(r0) == 0 {
|
|
|
|
if e1 != 0 {
|
|
|
|
err = error(e1)
|
|
|
|
} else {
|
|
|
|
err = syscall.EINVAL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func get_console_cursor_info(h syscall.Handle, info *console_cursor_info) (err error) {
|
|
|
|
r0, _, e1 := syscall.Syscall(proc_get_console_cursor_info.Addr(),
|
|
|
|
2, uintptr(h), uintptr(unsafe.Pointer(info)), 0)
|
|
|
|
if int(r0) == 0 {
|
|
|
|
if e1 != 0 {
|
|
|
|
err = error(e1)
|
|
|
|
} else {
|
|
|
|
err = syscall.EINVAL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func set_console_cursor_position(h syscall.Handle, pos coord) (err error) {
|
|
|
|
r0, _, e1 := syscall.Syscall(proc_set_console_cursor_position.Addr(),
|
|
|
|
2, uintptr(h), pos.uintptr(), 0)
|
|
|
|
if int(r0) == 0 {
|
|
|
|
if e1 != 0 {
|
|
|
|
err = error(e1)
|
|
|
|
} else {
|
|
|
|
err = syscall.EINVAL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func read_console_input(h syscall.Handle, record *input_record) (err error) {
|
|
|
|
r0, _, e1 := syscall.Syscall6(proc_read_console_input.Addr(),
|
|
|
|
4, uintptr(h), uintptr(unsafe.Pointer(record)), 1, uintptr(unsafe.Pointer(&tmp_arg)), 0, 0)
|
|
|
|
if int(r0) == 0 {
|
|
|
|
if e1 != 0 {
|
|
|
|
err = error(e1)
|
|
|
|
} else {
|
|
|
|
err = syscall.EINVAL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func get_console_mode(h syscall.Handle, mode *dword) (err error) {
|
|
|
|
r0, _, e1 := syscall.Syscall(proc_get_console_mode.Addr(),
|
|
|
|
2, uintptr(h), uintptr(unsafe.Pointer(mode)), 0)
|
|
|
|
if int(r0) == 0 {
|
|
|
|
if e1 != 0 {
|
|
|
|
err = error(e1)
|
|
|
|
} else {
|
|
|
|
err = syscall.EINVAL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func set_console_mode(h syscall.Handle, mode dword) (err error) {
|
|
|
|
r0, _, e1 := syscall.Syscall(proc_set_console_mode.Addr(),
|
|
|
|
2, uintptr(h), uintptr(mode), 0)
|
|
|
|
if int(r0) == 0 {
|
|
|
|
if e1 != 0 {
|
|
|
|
err = error(e1)
|
|
|
|
} else {
|
|
|
|
err = syscall.EINVAL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func fill_console_output_character(h syscall.Handle, char wchar, n int) (err error) {
|
2018-11-24 15:31:25 +00:00
|
|
|
tmp_coord = coord{0, 0}
|
2017-06-15 11:40:39 +00:00
|
|
|
r0, _, e1 := syscall.Syscall6(proc_fill_console_output_character.Addr(),
|
|
|
|
5, uintptr(h), uintptr(char), uintptr(n), tmp_coord.uintptr(),
|
|
|
|
uintptr(unsafe.Pointer(&tmp_arg)), 0)
|
|
|
|
if int(r0) == 0 {
|
|
|
|
if e1 != 0 {
|
|
|
|
err = error(e1)
|
|
|
|
} else {
|
|
|
|
err = syscall.EINVAL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func fill_console_output_attribute(h syscall.Handle, attr word, n int) (err error) {
|
2018-11-24 15:31:25 +00:00
|
|
|
tmp_coord = coord{0, 0}
|
2017-06-15 11:40:39 +00:00
|
|
|
r0, _, e1 := syscall.Syscall6(proc_fill_console_output_attribute.Addr(),
|
|
|
|
5, uintptr(h), uintptr(attr), uintptr(n), tmp_coord.uintptr(),
|
|
|
|
uintptr(unsafe.Pointer(&tmp_arg)), 0)
|
|
|
|
if int(r0) == 0 {
|
|
|
|
if e1 != 0 {
|
|
|
|
err = error(e1)
|
|
|
|
} else {
|
|
|
|
err = syscall.EINVAL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func create_event() (out syscall.Handle, err error) {
|
|
|
|
r0, _, e1 := syscall.Syscall6(proc_create_event.Addr(),
|
|
|
|
4, 0, 0, 0, 0, 0, 0)
|
|
|
|
if int(r0) == 0 {
|
|
|
|
if e1 != 0 {
|
|
|
|
err = error(e1)
|
|
|
|
} else {
|
|
|
|
err = syscall.EINVAL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return syscall.Handle(r0), err
|
|
|
|
}
|
|
|
|
|
|
|
|
func wait_for_multiple_objects(objects []syscall.Handle) (err error) {
|
|
|
|
r0, _, e1 := syscall.Syscall6(proc_wait_for_multiple_objects.Addr(),
|
|
|
|
4, uintptr(len(objects)), uintptr(unsafe.Pointer(&objects[0])),
|
|
|
|
0, 0xFFFFFFFF, 0, 0)
|
|
|
|
if uint32(r0) == 0xFFFFFFFF {
|
|
|
|
if e1 != 0 {
|
|
|
|
err = error(e1)
|
|
|
|
} else {
|
|
|
|
err = syscall.EINVAL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func set_event(ev syscall.Handle) (err error) {
|
|
|
|
r0, _, e1 := syscall.Syscall(proc_set_event.Addr(),
|
|
|
|
1, uintptr(ev), 0, 0)
|
|
|
|
if int(r0) == 0 {
|
|
|
|
if e1 != 0 {
|
|
|
|
err = error(e1)
|
|
|
|
} else {
|
|
|
|
err = syscall.EINVAL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-05-02 16:09:45 +00:00
|
|
|
func get_current_console_font(h syscall.Handle, info *console_font_info) (err error) {
|
|
|
|
r0, _, e1 := syscall.Syscall(proc_get_current_console_font.Addr(),
|
|
|
|
3, uintptr(h), 0, uintptr(unsafe.Pointer(info)))
|
|
|
|
if int(r0) == 0 {
|
|
|
|
if e1 != 0 {
|
|
|
|
err = error(e1)
|
|
|
|
} else {
|
|
|
|
err = syscall.EINVAL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-06-15 11:40:39 +00:00
|
|
|
type diff_msg struct {
|
|
|
|
pos short
|
|
|
|
lines short
|
|
|
|
chars []char_info
|
|
|
|
}
|
|
|
|
|
|
|
|
type input_event struct {
|
|
|
|
event Event
|
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
orig_cursor_info console_cursor_info
|
|
|
|
orig_size coord
|
2018-11-24 15:31:25 +00:00
|
|
|
orig_window small_rect
|
2017-06-15 11:40:39 +00:00
|
|
|
orig_mode dword
|
|
|
|
orig_screen syscall.Handle
|
|
|
|
back_buffer cellbuf
|
|
|
|
front_buffer cellbuf
|
|
|
|
term_size coord
|
|
|
|
input_mode = InputEsc
|
|
|
|
cursor_x = cursor_hidden
|
|
|
|
cursor_y = cursor_hidden
|
|
|
|
foreground = ColorDefault
|
|
|
|
background = ColorDefault
|
|
|
|
in syscall.Handle
|
|
|
|
out syscall.Handle
|
|
|
|
interrupt syscall.Handle
|
|
|
|
charbuf []char_info
|
|
|
|
diffbuf []diff_msg
|
|
|
|
beg_x = -1
|
|
|
|
beg_y = -1
|
|
|
|
beg_i = -1
|
|
|
|
input_comm = make(chan Event)
|
|
|
|
interrupt_comm = make(chan struct{})
|
|
|
|
cancel_comm = make(chan bool, 1)
|
|
|
|
cancel_done_comm = make(chan bool)
|
|
|
|
alt_mode_esc = false
|
|
|
|
|
|
|
|
// these ones just to prevent heap allocs at all costs
|
|
|
|
tmp_info console_screen_buffer_info
|
|
|
|
tmp_arg dword
|
|
|
|
tmp_coord0 = coord{0, 0}
|
|
|
|
tmp_coord = coord{0, 0}
|
|
|
|
tmp_rect = small_rect{0, 0, 0, 0}
|
2018-05-02 16:09:45 +00:00
|
|
|
tmp_finfo console_font_info
|
2017-06-15 11:40:39 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func get_cursor_position(out syscall.Handle) coord {
|
|
|
|
err := get_console_screen_buffer_info(out, &tmp_info)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return tmp_info.cursor_position
|
|
|
|
}
|
|
|
|
|
2018-11-24 15:31:25 +00:00
|
|
|
func get_term_size(out syscall.Handle) (coord, small_rect) {
|
2017-06-15 11:40:39 +00:00
|
|
|
err := get_console_screen_buffer_info(out, &tmp_info)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2018-11-24 15:31:25 +00:00
|
|
|
return tmp_info.size, tmp_info.window
|
2017-06-15 11:40:39 +00:00
|
|
|
}
|
|
|
|
|
2018-01-16 13:20:59 +00:00
|
|
|
func get_win_min_size(out syscall.Handle) coord {
|
|
|
|
x, _, err := get_system_metrics.Call(SM_CXMIN)
|
|
|
|
y, _, err := get_system_metrics.Call(SM_CYMIN)
|
|
|
|
|
|
|
|
if x == 0 || y == 0 {
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-02 16:09:45 +00:00
|
|
|
err1 := get_current_console_font(out, &tmp_finfo)
|
|
|
|
if err1 != nil {
|
|
|
|
panic(err1)
|
|
|
|
}
|
|
|
|
|
2018-01-16 13:20:59 +00:00
|
|
|
return coord{
|
2018-05-02 16:09:45 +00:00
|
|
|
x: short(math.Ceil(float64(x) / float64(tmp_finfo.font_size.x))),
|
|
|
|
y: short(math.Ceil(float64(y) / float64(tmp_finfo.font_size.y))),
|
2018-01-16 13:20:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-15 11:40:39 +00:00
|
|
|
func get_win_size(out syscall.Handle) coord {
|
|
|
|
err := get_console_screen_buffer_info(out, &tmp_info)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2018-01-16 13:20:59 +00:00
|
|
|
|
|
|
|
min_size := get_win_min_size(out)
|
|
|
|
|
|
|
|
size := coord{
|
2017-06-15 11:40:39 +00:00
|
|
|
x: tmp_info.window.right - tmp_info.window.left + 1,
|
|
|
|
y: tmp_info.window.bottom - tmp_info.window.top + 1,
|
|
|
|
}
|
2018-01-16 13:20:59 +00:00
|
|
|
|
|
|
|
if size.x < min_size.x {
|
|
|
|
size.x = min_size.x
|
|
|
|
}
|
|
|
|
|
|
|
|
if size.y < min_size.y {
|
|
|
|
size.y = min_size.y
|
|
|
|
}
|
|
|
|
|
|
|
|
return size
|
2017-06-15 11:40:39 +00:00
|
|
|
}
|
|
|
|
|
2018-11-24 15:31:25 +00:00
|
|
|
func fix_win_size(out syscall.Handle, size coord) (err error) {
|
|
|
|
window := small_rect{}
|
|
|
|
window.top = 0
|
|
|
|
window.bottom = size.y - 1
|
|
|
|
window.left = 0
|
|
|
|
window.right = size.x - 1
|
|
|
|
return set_console_window_info(out, &window)
|
|
|
|
}
|
|
|
|
|
2017-06-15 11:40:39 +00:00
|
|
|
func update_size_maybe() {
|
2018-05-02 16:09:45 +00:00
|
|
|
size := get_win_size(out)
|
2017-06-15 11:40:39 +00:00
|
|
|
if size.x != term_size.x || size.y != term_size.y {
|
2018-05-02 16:09:45 +00:00
|
|
|
set_console_screen_buffer_size(out, size)
|
2018-11-24 15:31:25 +00:00
|
|
|
fix_win_size(out, size)
|
2017-06-15 11:40:39 +00:00
|
|
|
term_size = size
|
|
|
|
back_buffer.resize(int(size.x), int(size.y))
|
|
|
|
front_buffer.resize(int(size.x), int(size.y))
|
|
|
|
front_buffer.clear()
|
|
|
|
clear()
|
|
|
|
|
|
|
|
area := int(size.x) * int(size.y)
|
|
|
|
if cap(charbuf) < area {
|
|
|
|
charbuf = make([]char_info, 0, area)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var color_table_bg = []word{
|
|
|
|
0, // default (black)
|
|
|
|
0, // black
|
|
|
|
background_red,
|
|
|
|
background_green,
|
|
|
|
background_red | background_green, // yellow
|
|
|
|
background_blue,
|
2018-11-24 15:31:25 +00:00
|
|
|
background_red | background_blue, // magenta
|
|
|
|
background_green | background_blue, // cyan
|
2017-06-15 11:40:39 +00:00
|
|
|
background_red | background_blue | background_green, // white
|
|
|
|
}
|
|
|
|
|
|
|
|
var color_table_fg = []word{
|
|
|
|
foreground_red | foreground_blue | foreground_green, // default (white)
|
|
|
|
0,
|
|
|
|
foreground_red,
|
|
|
|
foreground_green,
|
|
|
|
foreground_red | foreground_green, // yellow
|
|
|
|
foreground_blue,
|
2018-11-24 15:31:25 +00:00
|
|
|
foreground_red | foreground_blue, // magenta
|
|
|
|
foreground_green | foreground_blue, // cyan
|
2017-06-15 11:40:39 +00:00
|
|
|
foreground_red | foreground_blue | foreground_green, // white
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
replacement_char = '\uFFFD'
|
|
|
|
max_rune = '\U0010FFFF'
|
|
|
|
surr1 = 0xd800
|
|
|
|
surr2 = 0xdc00
|
|
|
|
surr3 = 0xe000
|
|
|
|
surr_self = 0x10000
|
|
|
|
)
|
|
|
|
|
|
|
|
func append_diff_line(y int) int {
|
|
|
|
n := 0
|
|
|
|
for x := 0; x < front_buffer.width; {
|
|
|
|
cell_offset := y*front_buffer.width + x
|
|
|
|
back := &back_buffer.cells[cell_offset]
|
|
|
|
front := &front_buffer.cells[cell_offset]
|
|
|
|
attr, char := cell_to_char_info(*back)
|
|
|
|
charbuf = append(charbuf, char_info{attr: attr, char: char[0]})
|
|
|
|
*front = *back
|
|
|
|
n++
|
|
|
|
w := runewidth.RuneWidth(back.Ch)
|
|
|
|
if w == 0 || w == 2 && runewidth.IsAmbiguousWidth(back.Ch) {
|
|
|
|
w = 1
|
|
|
|
}
|
|
|
|
x += w
|
|
|
|
// If not CJK, fill trailing space with whitespace
|
|
|
|
if !is_cjk && w == 2 {
|
|
|
|
charbuf = append(charbuf, char_info{attr: attr, char: ' '})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
|
|
|
// compares 'back_buffer' with 'front_buffer' and prepares all changes in the form of
|
|
|
|
// 'diff_msg's in the 'diff_buf'
|
|
|
|
func prepare_diff_messages() {
|
|
|
|
// clear buffers
|
|
|
|
diffbuf = diffbuf[:0]
|
|
|
|
charbuf = charbuf[:0]
|
|
|
|
|
|
|
|
var diff diff_msg
|
|
|
|
gbeg := 0
|
|
|
|
for y := 0; y < front_buffer.height; y++ {
|
|
|
|
same := true
|
|
|
|
line_offset := y * front_buffer.width
|
|
|
|
for x := 0; x < front_buffer.width; x++ {
|
|
|
|
cell_offset := line_offset + x
|
|
|
|
back := &back_buffer.cells[cell_offset]
|
|
|
|
front := &front_buffer.cells[cell_offset]
|
|
|
|
if *back != *front {
|
|
|
|
same = false
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if same && diff.lines > 0 {
|
|
|
|
diffbuf = append(diffbuf, diff)
|
|
|
|
diff = diff_msg{}
|
|
|
|
}
|
|
|
|
if !same {
|
|
|
|
beg := len(charbuf)
|
|
|
|
end := beg + append_diff_line(y)
|
|
|
|
if diff.lines == 0 {
|
|
|
|
diff.pos = short(y)
|
|
|
|
gbeg = beg
|
|
|
|
}
|
|
|
|
diff.lines++
|
|
|
|
diff.chars = charbuf[gbeg:end]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if diff.lines > 0 {
|
|
|
|
diffbuf = append(diffbuf, diff)
|
|
|
|
diff = diff_msg{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func get_ct(table []word, idx int) word {
|
|
|
|
idx = idx & 0x0F
|
|
|
|
if idx >= len(table) {
|
|
|
|
idx = len(table) - 1
|
|
|
|
}
|
|
|
|
return table[idx]
|
|
|
|
}
|
|
|
|
|
|
|
|
func cell_to_char_info(c Cell) (attr word, wc [2]wchar) {
|
|
|
|
attr = get_ct(color_table_fg, int(c.Fg)) | get_ct(color_table_bg, int(c.Bg))
|
|
|
|
if c.Fg&AttrReverse|c.Bg&AttrReverse != 0 {
|
|
|
|
attr = (attr&0xF0)>>4 | (attr&0x0F)<<4
|
|
|
|
}
|
|
|
|
if c.Fg&AttrBold != 0 {
|
|
|
|
attr |= foreground_intensity
|
|
|
|
}
|
|
|
|
if c.Bg&AttrBold != 0 {
|
|
|
|
attr |= background_intensity
|
|
|
|
}
|
|
|
|
|
|
|
|
r0, r1 := utf16.EncodeRune(c.Ch)
|
|
|
|
if r0 == 0xFFFD {
|
|
|
|
wc[0] = wchar(c.Ch)
|
|
|
|
wc[1] = ' '
|
|
|
|
} else {
|
|
|
|
wc[0] = wchar(r0)
|
|
|
|
wc[1] = wchar(r1)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func move_cursor(x, y int) {
|
|
|
|
err := set_console_cursor_position(out, coord{short(x), short(y)})
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func show_cursor(visible bool) {
|
|
|
|
var v int32
|
|
|
|
if visible {
|
|
|
|
v = 1
|
|
|
|
}
|
|
|
|
|
|
|
|
var info console_cursor_info
|
|
|
|
info.size = 100
|
|
|
|
info.visible = v
|
|
|
|
err := set_console_cursor_info(out, &info)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func clear() {
|
|
|
|
var err error
|
|
|
|
attr, char := cell_to_char_info(Cell{
|
|
|
|
' ',
|
|
|
|
foreground,
|
|
|
|
background,
|
|
|
|
})
|
|
|
|
|
|
|
|
area := int(term_size.x) * int(term_size.y)
|
|
|
|
err = fill_console_output_attribute(out, attr, area)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
err = fill_console_output_character(out, char[0], area)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
if !is_cursor_hidden(cursor_x, cursor_y) {
|
|
|
|
move_cursor(cursor_x, cursor_y)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func key_event_record_to_event(r *key_event_record) (Event, bool) {
|
|
|
|
if r.key_down == 0 {
|
|
|
|
return Event{}, false
|
|
|
|
}
|
|
|
|
|
|
|
|
e := Event{Type: EventKey}
|
|
|
|
if input_mode&InputAlt != 0 {
|
|
|
|
if alt_mode_esc {
|
|
|
|
e.Mod = ModAlt
|
|
|
|
alt_mode_esc = false
|
|
|
|
}
|
|
|
|
if r.control_key_state&(left_alt_pressed|right_alt_pressed) != 0 {
|
|
|
|
e.Mod = ModAlt
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ctrlpressed := r.control_key_state&(left_ctrl_pressed|right_ctrl_pressed) != 0
|
|
|
|
|
|
|
|
if r.virtual_key_code >= vk_f1 && r.virtual_key_code <= vk_f12 {
|
|
|
|
switch r.virtual_key_code {
|
|
|
|
case vk_f1:
|
|
|
|
e.Key = KeyF1
|
|
|
|
case vk_f2:
|
|
|
|
e.Key = KeyF2
|
|
|
|
case vk_f3:
|
|
|
|
e.Key = KeyF3
|
|
|
|
case vk_f4:
|
|
|
|
e.Key = KeyF4
|
|
|
|
case vk_f5:
|
|
|
|
e.Key = KeyF5
|
|
|
|
case vk_f6:
|
|
|
|
e.Key = KeyF6
|
|
|
|
case vk_f7:
|
|
|
|
e.Key = KeyF7
|
|
|
|
case vk_f8:
|
|
|
|
e.Key = KeyF8
|
|
|
|
case vk_f9:
|
|
|
|
e.Key = KeyF9
|
|
|
|
case vk_f10:
|
|
|
|
e.Key = KeyF10
|
|
|
|
case vk_f11:
|
|
|
|
e.Key = KeyF11
|
|
|
|
case vk_f12:
|
|
|
|
e.Key = KeyF12
|
|
|
|
default:
|
|
|
|
panic("unreachable")
|
|
|
|
}
|
|
|
|
|
|
|
|
return e, true
|
|
|
|
}
|
|
|
|
|
|
|
|
if r.virtual_key_code <= vk_delete {
|
|
|
|
switch r.virtual_key_code {
|
|
|
|
case vk_insert:
|
|
|
|
e.Key = KeyInsert
|
|
|
|
case vk_delete:
|
|
|
|
e.Key = KeyDelete
|
|
|
|
case vk_home:
|
|
|
|
e.Key = KeyHome
|
|
|
|
case vk_end:
|
|
|
|
e.Key = KeyEnd
|
|
|
|
case vk_pgup:
|
|
|
|
e.Key = KeyPgup
|
|
|
|
case vk_pgdn:
|
|
|
|
e.Key = KeyPgdn
|
|
|
|
case vk_arrow_up:
|
|
|
|
e.Key = KeyArrowUp
|
|
|
|
case vk_arrow_down:
|
|
|
|
e.Key = KeyArrowDown
|
|
|
|
case vk_arrow_left:
|
|
|
|
e.Key = KeyArrowLeft
|
|
|
|
case vk_arrow_right:
|
|
|
|
e.Key = KeyArrowRight
|
|
|
|
case vk_backspace:
|
|
|
|
if ctrlpressed {
|
|
|
|
e.Key = KeyBackspace2
|
|
|
|
} else {
|
|
|
|
e.Key = KeyBackspace
|
|
|
|
}
|
|
|
|
case vk_tab:
|
|
|
|
e.Key = KeyTab
|
|
|
|
case vk_enter:
|
|
|
|
e.Key = KeyEnter
|
|
|
|
case vk_esc:
|
|
|
|
switch {
|
|
|
|
case input_mode&InputEsc != 0:
|
|
|
|
e.Key = KeyEsc
|
|
|
|
case input_mode&InputAlt != 0:
|
|
|
|
alt_mode_esc = true
|
|
|
|
return Event{}, false
|
|
|
|
}
|
|
|
|
case vk_space:
|
|
|
|
if ctrlpressed {
|
|
|
|
// manual return here, because KeyCtrlSpace is zero
|
|
|
|
e.Key = KeyCtrlSpace
|
|
|
|
return e, true
|
|
|
|
} else {
|
|
|
|
e.Key = KeySpace
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.Key != 0 {
|
|
|
|
return e, true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ctrlpressed {
|
|
|
|
if Key(r.unicode_char) >= KeyCtrlA && Key(r.unicode_char) <= KeyCtrlRsqBracket {
|
|
|
|
e.Key = Key(r.unicode_char)
|
|
|
|
if input_mode&InputAlt != 0 && e.Key == KeyEsc {
|
|
|
|
alt_mode_esc = true
|
|
|
|
return Event{}, false
|
|
|
|
}
|
|
|
|
return e, true
|
|
|
|
}
|
|
|
|
switch r.virtual_key_code {
|
|
|
|
case 192, 50:
|
|
|
|
// manual return here, because KeyCtrl2 is zero
|
|
|
|
e.Key = KeyCtrl2
|
|
|
|
return e, true
|
|
|
|
case 51:
|
|
|
|
if input_mode&InputAlt != 0 {
|
|
|
|
alt_mode_esc = true
|
|
|
|
return Event{}, false
|
|
|
|
}
|
|
|
|
e.Key = KeyCtrl3
|
|
|
|
case 52:
|
|
|
|
e.Key = KeyCtrl4
|
|
|
|
case 53:
|
|
|
|
e.Key = KeyCtrl5
|
|
|
|
case 54:
|
|
|
|
e.Key = KeyCtrl6
|
|
|
|
case 189, 191, 55:
|
|
|
|
e.Key = KeyCtrl7
|
|
|
|
case 8, 56:
|
|
|
|
e.Key = KeyCtrl8
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.Key != 0 {
|
|
|
|
return e, true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if r.unicode_char != 0 {
|
|
|
|
e.Ch = rune(r.unicode_char)
|
|
|
|
return e, true
|
|
|
|
}
|
|
|
|
|
|
|
|
return Event{}, false
|
|
|
|
}
|
|
|
|
|
|
|
|
func input_event_producer() {
|
|
|
|
var r input_record
|
|
|
|
var err error
|
|
|
|
var last_button Key
|
|
|
|
var last_button_pressed Key
|
|
|
|
var last_state = dword(0)
|
|
|
|
var last_x, last_y = -1, -1
|
|
|
|
handles := []syscall.Handle{in, interrupt}
|
|
|
|
for {
|
|
|
|
err = wait_for_multiple_objects(handles)
|
|
|
|
if err != nil {
|
|
|
|
input_comm <- Event{Type: EventError, Err: err}
|
|
|
|
}
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-cancel_comm:
|
|
|
|
cancel_done_comm <- true
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
|
|
|
|
err = read_console_input(in, &r)
|
|
|
|
if err != nil {
|
|
|
|
input_comm <- Event{Type: EventError, Err: err}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch r.event_type {
|
|
|
|
case key_event:
|
|
|
|
kr := (*key_event_record)(unsafe.Pointer(&r.event))
|
|
|
|
ev, ok := key_event_record_to_event(kr)
|
|
|
|
if ok {
|
|
|
|
for i := 0; i < int(kr.repeat_count); i++ {
|
|
|
|
input_comm <- ev
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case window_buffer_size_event:
|
|
|
|
sr := *(*window_buffer_size_record)(unsafe.Pointer(&r.event))
|
|
|
|
input_comm <- Event{
|
|
|
|
Type: EventResize,
|
|
|
|
Width: int(sr.size.x),
|
|
|
|
Height: int(sr.size.y),
|
|
|
|
}
|
|
|
|
case mouse_event:
|
|
|
|
mr := *(*mouse_event_record)(unsafe.Pointer(&r.event))
|
|
|
|
ev := Event{Type: EventMouse}
|
|
|
|
switch mr.event_flags {
|
|
|
|
case 0, 2:
|
|
|
|
// single or double click
|
|
|
|
cur_state := mr.button_state
|
|
|
|
switch {
|
|
|
|
case last_state&mouse_lmb == 0 && cur_state&mouse_lmb != 0:
|
|
|
|
last_button = MouseLeft
|
|
|
|
last_button_pressed = last_button
|
|
|
|
case last_state&mouse_rmb == 0 && cur_state&mouse_rmb != 0:
|
|
|
|
last_button = MouseRight
|
|
|
|
last_button_pressed = last_button
|
|
|
|
case last_state&mouse_mmb == 0 && cur_state&mouse_mmb != 0:
|
|
|
|
last_button = MouseMiddle
|
|
|
|
last_button_pressed = last_button
|
|
|
|
case last_state&mouse_lmb != 0 && cur_state&mouse_lmb == 0:
|
|
|
|
last_button = MouseRelease
|
|
|
|
case last_state&mouse_rmb != 0 && cur_state&mouse_rmb == 0:
|
|
|
|
last_button = MouseRelease
|
|
|
|
case last_state&mouse_mmb != 0 && cur_state&mouse_mmb == 0:
|
|
|
|
last_button = MouseRelease
|
|
|
|
default:
|
|
|
|
last_state = cur_state
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
last_state = cur_state
|
|
|
|
ev.Key = last_button
|
|
|
|
last_x, last_y = int(mr.mouse_pos.x), int(mr.mouse_pos.y)
|
|
|
|
ev.MouseX = last_x
|
|
|
|
ev.MouseY = last_y
|
|
|
|
case 1:
|
|
|
|
// mouse motion
|
|
|
|
x, y := int(mr.mouse_pos.x), int(mr.mouse_pos.y)
|
|
|
|
if last_state != 0 && (last_x != x || last_y != y) {
|
|
|
|
ev.Key = last_button_pressed
|
|
|
|
ev.Mod = ModMotion
|
|
|
|
ev.MouseX = x
|
|
|
|
ev.MouseY = y
|
|
|
|
last_x, last_y = x, y
|
|
|
|
} else {
|
|
|
|
ev.Type = EventNone
|
|
|
|
}
|
|
|
|
case 4:
|
|
|
|
// mouse wheel
|
|
|
|
n := int16(mr.button_state >> 16)
|
|
|
|
if n > 0 {
|
|
|
|
ev.Key = MouseWheelUp
|
|
|
|
} else {
|
|
|
|
ev.Key = MouseWheelDown
|
|
|
|
}
|
|
|
|
last_x, last_y = int(mr.mouse_pos.x), int(mr.mouse_pos.y)
|
|
|
|
ev.MouseX = last_x
|
|
|
|
ev.MouseY = last_y
|
|
|
|
default:
|
|
|
|
ev.Type = EventNone
|
|
|
|
}
|
|
|
|
if ev.Type != EventNone {
|
|
|
|
input_comm <- ev
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|