916 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			916 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package termbox
 | |
| 
 | |
| import "math"
 | |
| import "syscall"
 | |
| import "unsafe"
 | |
| import "unicode/utf16"
 | |
| import "github.com/mattn/go-runewidth"
 | |
| 
 | |
| 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
 | |
| 	}
 | |
| 	console_font_info struct {
 | |
| 		font      uint32
 | |
| 		font_size coord
 | |
| 	}
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	mouse_lmb = 0x1
 | |
| 	mouse_rmb = 0x2
 | |
| 	mouse_mmb = 0x4 | 0x8 | 0x10
 | |
| 	SM_CXMIN  = 28
 | |
| 	SM_CYMIN  = 29
 | |
| )
 | |
| 
 | |
| func (this coord) uintptr() uintptr {
 | |
| 	return uintptr(*(*int32)(unsafe.Pointer(&this)))
 | |
| }
 | |
| 
 | |
| var kernel32 = syscall.NewLazyDLL("kernel32.dll")
 | |
| var moduser32 = syscall.NewLazyDLL("user32.dll")
 | |
| var is_cjk = runewidth.IsEastAsian()
 | |
| 
 | |
| var (
 | |
| 	proc_set_console_active_screen_buffer = kernel32.NewProc("SetConsoleActiveScreenBuffer")
 | |
| 	proc_set_console_screen_buffer_size   = kernel32.NewProc("SetConsoleScreenBufferSize")
 | |
| 	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")
 | |
| 	proc_get_current_console_font         = kernel32.NewProc("GetCurrentConsoleFont")
 | |
| 	get_system_metrics                    = moduser32.NewProc("GetSystemMetrics")
 | |
| )
 | |
| 
 | |
| 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
 | |
| }
 | |
| 
 | |
| 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) {
 | |
| 	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) {
 | |
| 	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
 | |
| }
 | |
| 
 | |
| 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
 | |
| }
 | |
| 
 | |
| 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
 | |
| 	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}
 | |
| 	tmp_finfo  console_font_info
 | |
| )
 | |
| 
 | |
| 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
 | |
| }
 | |
| 
 | |
| func get_term_size(out syscall.Handle) coord {
 | |
| 	err := get_console_screen_buffer_info(out, &tmp_info)
 | |
| 	if err != nil {
 | |
| 		panic(err)
 | |
| 	}
 | |
| 	return tmp_info.size
 | |
| }
 | |
| 
 | |
| 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)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	err1 := get_current_console_font(out, &tmp_finfo)
 | |
| 	if err1 != nil {
 | |
| 		panic(err1)
 | |
| 	}
 | |
| 
 | |
| 	return coord{
 | |
| 		x: short(math.Ceil(float64(x) / float64(tmp_finfo.font_size.x))),
 | |
| 		y: short(math.Ceil(float64(y) / float64(tmp_finfo.font_size.y))),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func get_win_size(out syscall.Handle) coord {
 | |
| 	err := get_console_screen_buffer_info(out, &tmp_info)
 | |
| 	if err != nil {
 | |
| 		panic(err)
 | |
| 	}
 | |
| 
 | |
| 	min_size := get_win_min_size(out)
 | |
| 
 | |
| 	size := coord{
 | |
| 		x: tmp_info.window.right - tmp_info.window.left + 1,
 | |
| 		y: tmp_info.window.bottom - tmp_info.window.top + 1,
 | |
| 	}
 | |
| 
 | |
| 	if size.x < min_size.x {
 | |
| 		size.x = min_size.x
 | |
| 	}
 | |
| 
 | |
| 	if size.y < min_size.y {
 | |
| 		size.y = min_size.y
 | |
| 	}
 | |
| 
 | |
| 	return size
 | |
| }
 | |
| 
 | |
| func update_size_maybe() {
 | |
| 	size := get_win_size(out)
 | |
| 	if size.x != term_size.x || size.y != term_size.y {
 | |
| 		set_console_screen_buffer_size(out, size)
 | |
| 		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,
 | |
| 	background_red | background_blue,                    // magenta
 | |
| 	background_green | background_blue,                  // cyan
 | |
| 	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,
 | |
| 	foreground_red | foreground_blue,                    // magenta
 | |
| 	foreground_green | foreground_blue,                  // cyan
 | |
| 	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
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 |