1package termbox
2
3import "math"
4import "syscall"
5import "unsafe"
6import "unicode/utf16"
7import "github.com/mattn/go-runewidth"
8
9type (
10	wchar     uint16
11	short     int16
12	dword     uint32
13	word      uint16
14	char_info struct {
15		char wchar
16		attr word
17	}
18	coord struct {
19		x short
20		y short
21	}
22	small_rect struct {
23		left   short
24		top    short
25		right  short
26		bottom short
27	}
28	console_screen_buffer_info struct {
29		size                coord
30		cursor_position     coord
31		attributes          word
32		window              small_rect
33		maximum_window_size coord
34	}
35	console_cursor_info struct {
36		size    dword
37		visible int32
38	}
39	input_record struct {
40		event_type word
41		_          [2]byte
42		event      [16]byte
43	}
44	key_event_record struct {
45		key_down          int32
46		repeat_count      word
47		virtual_key_code  word
48		virtual_scan_code word
49		unicode_char      wchar
50		control_key_state dword
51	}
52	window_buffer_size_record struct {
53		size coord
54	}
55	mouse_event_record struct {
56		mouse_pos         coord
57		button_state      dword
58		control_key_state dword
59		event_flags       dword
60	}
61	console_font_info struct {
62		font      uint32
63		font_size coord
64	}
65)
66
67const (
68	mouse_lmb = 0x1
69	mouse_rmb = 0x2
70	mouse_mmb = 0x4 | 0x8 | 0x10
71	SM_CXMIN  = 28
72	SM_CYMIN  = 29
73)
74
75func (this coord) uintptr() uintptr {
76	return uintptr(*(*int32)(unsafe.Pointer(&this)))
77}
78
79func (this *small_rect) uintptr() uintptr {
80	return uintptr(unsafe.Pointer(this))
81}
82
83var kernel32 = syscall.NewLazyDLL("kernel32.dll")
84var moduser32 = syscall.NewLazyDLL("user32.dll")
85var is_cjk = runewidth.IsEastAsian()
86
87var (
88	proc_set_console_active_screen_buffer = kernel32.NewProc("SetConsoleActiveScreenBuffer")
89	proc_set_console_screen_buffer_size   = kernel32.NewProc("SetConsoleScreenBufferSize")
90	proc_set_console_window_info          = kernel32.NewProc("SetConsoleWindowInfo")
91	proc_create_console_screen_buffer     = kernel32.NewProc("CreateConsoleScreenBuffer")
92	proc_get_console_screen_buffer_info   = kernel32.NewProc("GetConsoleScreenBufferInfo")
93	proc_write_console_output             = kernel32.NewProc("WriteConsoleOutputW")
94	proc_write_console_output_character   = kernel32.NewProc("WriteConsoleOutputCharacterW")
95	proc_write_console_output_attribute   = kernel32.NewProc("WriteConsoleOutputAttribute")
96	proc_set_console_cursor_info          = kernel32.NewProc("SetConsoleCursorInfo")
97	proc_set_console_cursor_position      = kernel32.NewProc("SetConsoleCursorPosition")
98	proc_get_console_cursor_info          = kernel32.NewProc("GetConsoleCursorInfo")
99	proc_read_console_input               = kernel32.NewProc("ReadConsoleInputW")
100	proc_get_console_mode                 = kernel32.NewProc("GetConsoleMode")
101	proc_set_console_mode                 = kernel32.NewProc("SetConsoleMode")
102	proc_fill_console_output_character    = kernel32.NewProc("FillConsoleOutputCharacterW")
103	proc_fill_console_output_attribute    = kernel32.NewProc("FillConsoleOutputAttribute")
104	proc_create_event                     = kernel32.NewProc("CreateEventW")
105	proc_wait_for_multiple_objects        = kernel32.NewProc("WaitForMultipleObjects")
106	proc_set_event                        = kernel32.NewProc("SetEvent")
107	proc_get_current_console_font         = kernel32.NewProc("GetCurrentConsoleFont")
108	get_system_metrics                    = moduser32.NewProc("GetSystemMetrics")
109)
110
111func set_console_active_screen_buffer(h syscall.Handle) (err error) {
112	r0, _, e1 := syscall.Syscall(proc_set_console_active_screen_buffer.Addr(),
113		1, uintptr(h), 0, 0)
114	if int(r0) == 0 {
115		if e1 != 0 {
116			err = error(e1)
117		} else {
118			err = syscall.EINVAL
119		}
120	}
121	return
122}
123
124func set_console_screen_buffer_size(h syscall.Handle, size coord) (err error) {
125	r0, _, e1 := syscall.Syscall(proc_set_console_screen_buffer_size.Addr(),
126		2, uintptr(h), size.uintptr(), 0)
127	if int(r0) == 0 {
128		if e1 != 0 {
129			err = error(e1)
130		} else {
131			err = syscall.EINVAL
132		}
133	}
134	return
135}
136
137func set_console_window_info(h syscall.Handle, window *small_rect) (err error) {
138	var absolute uint32
139	absolute = 1
140	r0, _, e1 := syscall.Syscall(proc_set_console_window_info.Addr(),
141		3, uintptr(h), uintptr(absolute), window.uintptr())
142	if int(r0) == 0 {
143		if e1 != 0 {
144			err = error(e1)
145		} else {
146			err = syscall.EINVAL
147		}
148	}
149	return
150}
151
152func create_console_screen_buffer() (h syscall.Handle, err error) {
153	r0, _, e1 := syscall.Syscall6(proc_create_console_screen_buffer.Addr(),
154		5, uintptr(generic_read|generic_write), 0, 0, console_textmode_buffer, 0, 0)
155	if int(r0) == 0 {
156		if e1 != 0 {
157			err = error(e1)
158		} else {
159			err = syscall.EINVAL
160		}
161	}
162	return syscall.Handle(r0), err
163}
164
165func get_console_screen_buffer_info(h syscall.Handle, info *console_screen_buffer_info) (err error) {
166	r0, _, e1 := syscall.Syscall(proc_get_console_screen_buffer_info.Addr(),
167		2, uintptr(h), uintptr(unsafe.Pointer(info)), 0)
168	if int(r0) == 0 {
169		if e1 != 0 {
170			err = error(e1)
171		} else {
172			err = syscall.EINVAL
173		}
174	}
175	return
176}
177
178func write_console_output(h syscall.Handle, chars []char_info, dst small_rect) (err error) {
179	tmp_coord = coord{dst.right - dst.left + 1, dst.bottom - dst.top + 1}
180	tmp_rect = dst
181	r0, _, e1 := syscall.Syscall6(proc_write_console_output.Addr(),
182		5, uintptr(h), uintptr(unsafe.Pointer(&chars[0])), tmp_coord.uintptr(),
183		tmp_coord0.uintptr(), uintptr(unsafe.Pointer(&tmp_rect)), 0)
184	if int(r0) == 0 {
185		if e1 != 0 {
186			err = error(e1)
187		} else {
188			err = syscall.EINVAL
189		}
190	}
191	return
192}
193
194func write_console_output_character(h syscall.Handle, chars []wchar, pos coord) (err error) {
195	r0, _, e1 := syscall.Syscall6(proc_write_console_output_character.Addr(),
196		5, uintptr(h), uintptr(unsafe.Pointer(&chars[0])), uintptr(len(chars)),
197		pos.uintptr(), uintptr(unsafe.Pointer(&tmp_arg)), 0)
198	if int(r0) == 0 {
199		if e1 != 0 {
200			err = error(e1)
201		} else {
202			err = syscall.EINVAL
203		}
204	}
205	return
206}
207
208func write_console_output_attribute(h syscall.Handle, attrs []word, pos coord) (err error) {
209	r0, _, e1 := syscall.Syscall6(proc_write_console_output_attribute.Addr(),
210		5, uintptr(h), uintptr(unsafe.Pointer(&attrs[0])), uintptr(len(attrs)),
211		pos.uintptr(), uintptr(unsafe.Pointer(&tmp_arg)), 0)
212	if int(r0) == 0 {
213		if e1 != 0 {
214			err = error(e1)
215		} else {
216			err = syscall.EINVAL
217		}
218	}
219	return
220}
221
222func set_console_cursor_info(h syscall.Handle, info *console_cursor_info) (err error) {
223	r0, _, e1 := syscall.Syscall(proc_set_console_cursor_info.Addr(),
224		2, uintptr(h), uintptr(unsafe.Pointer(info)), 0)
225	if int(r0) == 0 {
226		if e1 != 0 {
227			err = error(e1)
228		} else {
229			err = syscall.EINVAL
230		}
231	}
232	return
233}
234
235func get_console_cursor_info(h syscall.Handle, info *console_cursor_info) (err error) {
236	r0, _, e1 := syscall.Syscall(proc_get_console_cursor_info.Addr(),
237		2, uintptr(h), uintptr(unsafe.Pointer(info)), 0)
238	if int(r0) == 0 {
239		if e1 != 0 {
240			err = error(e1)
241		} else {
242			err = syscall.EINVAL
243		}
244	}
245	return
246}
247
248func set_console_cursor_position(h syscall.Handle, pos coord) (err error) {
249	r0, _, e1 := syscall.Syscall(proc_set_console_cursor_position.Addr(),
250		2, uintptr(h), pos.uintptr(), 0)
251	if int(r0) == 0 {
252		if e1 != 0 {
253			err = error(e1)
254		} else {
255			err = syscall.EINVAL
256		}
257	}
258	return
259}
260
261func read_console_input(h syscall.Handle, record *input_record) (err error) {
262	r0, _, e1 := syscall.Syscall6(proc_read_console_input.Addr(),
263		4, uintptr(h), uintptr(unsafe.Pointer(record)), 1, uintptr(unsafe.Pointer(&tmp_arg)), 0, 0)
264	if int(r0) == 0 {
265		if e1 != 0 {
266			err = error(e1)
267		} else {
268			err = syscall.EINVAL
269		}
270	}
271	return
272}
273
274func get_console_mode(h syscall.Handle, mode *dword) (err error) {
275	r0, _, e1 := syscall.Syscall(proc_get_console_mode.Addr(),
276		2, uintptr(h), uintptr(unsafe.Pointer(mode)), 0)
277	if int(r0) == 0 {
278		if e1 != 0 {
279			err = error(e1)
280		} else {
281			err = syscall.EINVAL
282		}
283	}
284	return
285}
286
287func set_console_mode(h syscall.Handle, mode dword) (err error) {
288	r0, _, e1 := syscall.Syscall(proc_set_console_mode.Addr(),
289		2, uintptr(h), uintptr(mode), 0)
290	if int(r0) == 0 {
291		if e1 != 0 {
292			err = error(e1)
293		} else {
294			err = syscall.EINVAL
295		}
296	}
297	return
298}
299
300func fill_console_output_character(h syscall.Handle, char wchar, n int) (err error) {
301	tmp_coord = coord{0, 0}
302	r0, _, e1 := syscall.Syscall6(proc_fill_console_output_character.Addr(),
303		5, uintptr(h), uintptr(char), uintptr(n), tmp_coord.uintptr(),
304		uintptr(unsafe.Pointer(&tmp_arg)), 0)
305	if int(r0) == 0 {
306		if e1 != 0 {
307			err = error(e1)
308		} else {
309			err = syscall.EINVAL
310		}
311	}
312	return
313}
314
315func fill_console_output_attribute(h syscall.Handle, attr word, n int) (err error) {
316	tmp_coord = coord{0, 0}
317	r0, _, e1 := syscall.Syscall6(proc_fill_console_output_attribute.Addr(),
318		5, uintptr(h), uintptr(attr), uintptr(n), tmp_coord.uintptr(),
319		uintptr(unsafe.Pointer(&tmp_arg)), 0)
320	if int(r0) == 0 {
321		if e1 != 0 {
322			err = error(e1)
323		} else {
324			err = syscall.EINVAL
325		}
326	}
327	return
328}
329
330func create_event() (out syscall.Handle, err error) {
331	r0, _, e1 := syscall.Syscall6(proc_create_event.Addr(),
332		4, 0, 0, 0, 0, 0, 0)
333	if int(r0) == 0 {
334		if e1 != 0 {
335			err = error(e1)
336		} else {
337			err = syscall.EINVAL
338		}
339	}
340	return syscall.Handle(r0), err
341}
342
343func wait_for_multiple_objects(objects []syscall.Handle) (err error) {
344	r0, _, e1 := syscall.Syscall6(proc_wait_for_multiple_objects.Addr(),
345		4, uintptr(len(objects)), uintptr(unsafe.Pointer(&objects[0])),
346		0, 0xFFFFFFFF, 0, 0)
347	if uint32(r0) == 0xFFFFFFFF {
348		if e1 != 0 {
349			err = error(e1)
350		} else {
351			err = syscall.EINVAL
352		}
353	}
354	return
355}
356
357func set_event(ev syscall.Handle) (err error) {
358	r0, _, e1 := syscall.Syscall(proc_set_event.Addr(),
359		1, uintptr(ev), 0, 0)
360	if int(r0) == 0 {
361		if e1 != 0 {
362			err = error(e1)
363		} else {
364			err = syscall.EINVAL
365		}
366	}
367	return
368}
369
370func get_current_console_font(h syscall.Handle, info *console_font_info) (err error) {
371	r0, _, e1 := syscall.Syscall(proc_get_current_console_font.Addr(),
372		3, uintptr(h), 0, uintptr(unsafe.Pointer(info)))
373	if int(r0) == 0 {
374		if e1 != 0 {
375			err = error(e1)
376		} else {
377			err = syscall.EINVAL
378		}
379	}
380	return
381}
382
383type diff_msg struct {
384	pos   short
385	lines short
386	chars []char_info
387}
388
389type input_event struct {
390	event Event
391	err   error
392}
393
394var (
395	orig_cursor_info console_cursor_info
396	orig_size        coord
397	orig_window      small_rect
398	orig_mode        dword
399	orig_screen      syscall.Handle
400	back_buffer      cellbuf
401	front_buffer     cellbuf
402	term_size        coord
403	input_mode       = InputEsc
404	cursor_x         = cursor_hidden
405	cursor_y         = cursor_hidden
406	foreground       = ColorDefault
407	background       = ColorDefault
408	in               syscall.Handle
409	out              syscall.Handle
410	interrupt        syscall.Handle
411	charbuf          []char_info
412	diffbuf          []diff_msg
413	beg_x            = -1
414	beg_y            = -1
415	beg_i            = -1
416	input_comm       = make(chan Event)
417	interrupt_comm   = make(chan struct{})
418	cancel_comm      = make(chan bool, 1)
419	cancel_done_comm = make(chan bool)
420	alt_mode_esc     = false
421
422	// these ones just to prevent heap allocs at all costs
423	tmp_info   console_screen_buffer_info
424	tmp_arg    dword
425	tmp_coord0 = coord{0, 0}
426	tmp_coord  = coord{0, 0}
427	tmp_rect   = small_rect{0, 0, 0, 0}
428	tmp_finfo  console_font_info
429)
430
431func get_cursor_position(out syscall.Handle) coord {
432	err := get_console_screen_buffer_info(out, &tmp_info)
433	if err != nil {
434		panic(err)
435	}
436	return tmp_info.cursor_position
437}
438
439func get_term_size(out syscall.Handle) (coord, small_rect) {
440	err := get_console_screen_buffer_info(out, &tmp_info)
441	if err != nil {
442		panic(err)
443	}
444	return tmp_info.size, tmp_info.window
445}
446
447func get_win_min_size(out syscall.Handle) coord {
448	x, _, err := get_system_metrics.Call(SM_CXMIN)
449	y, _, err := get_system_metrics.Call(SM_CYMIN)
450
451	if x == 0 || y == 0 {
452		if err != nil {
453			panic(err)
454		}
455	}
456
457	err1 := get_current_console_font(out, &tmp_finfo)
458	if err1 != nil {
459		panic(err1)
460	}
461
462	return coord{
463		x: short(math.Ceil(float64(x) / float64(tmp_finfo.font_size.x))),
464		y: short(math.Ceil(float64(y) / float64(tmp_finfo.font_size.y))),
465	}
466}
467
468func get_win_size(out syscall.Handle) coord {
469	err := get_console_screen_buffer_info(out, &tmp_info)
470	if err != nil {
471		panic(err)
472	}
473
474	min_size := get_win_min_size(out)
475
476	size := coord{
477		x: tmp_info.window.right - tmp_info.window.left + 1,
478		y: tmp_info.window.bottom - tmp_info.window.top + 1,
479	}
480
481	if size.x < min_size.x {
482		size.x = min_size.x
483	}
484
485	if size.y < min_size.y {
486		size.y = min_size.y
487	}
488
489	return size
490}
491
492func fix_win_size(out syscall.Handle, size coord) (err error) {
493	window := small_rect{}
494	window.top = 0
495	window.bottom = size.y - 1
496	window.left = 0
497	window.right = size.x - 1
498	return set_console_window_info(out, &window)
499}
500
501func update_size_maybe() {
502	size := get_win_size(out)
503	if size.x != term_size.x || size.y != term_size.y {
504		set_console_screen_buffer_size(out, size)
505		fix_win_size(out, size)
506		term_size = size
507		back_buffer.resize(int(size.x), int(size.y))
508		front_buffer.resize(int(size.x), int(size.y))
509		front_buffer.clear()
510		clear()
511
512		area := int(size.x) * int(size.y)
513		if cap(charbuf) < area {
514			charbuf = make([]char_info, 0, area)
515		}
516	}
517}
518
519var color_table_bg = []word{
520	0, // default (black)
521	0, // black
522	background_red,
523	background_green,
524	background_red | background_green, // yellow
525	background_blue,
526	background_red | background_blue,   // magenta
527	background_green | background_blue, // cyan
528	background_red | background_blue | background_green, // white
529}
530
531var color_table_fg = []word{
532	foreground_red | foreground_blue | foreground_green, // default (white)
533	0,
534	foreground_red,
535	foreground_green,
536	foreground_red | foreground_green, // yellow
537	foreground_blue,
538	foreground_red | foreground_blue,   // magenta
539	foreground_green | foreground_blue, // cyan
540	foreground_red | foreground_blue | foreground_green, // white
541}
542
543const (
544	replacement_char = '\uFFFD'
545	max_rune         = '\U0010FFFF'
546	surr1            = 0xd800
547	surr2            = 0xdc00
548	surr3            = 0xe000
549	surr_self        = 0x10000
550)
551
552func append_diff_line(y int) int {
553	n := 0
554	for x := 0; x < front_buffer.width; {
555		cell_offset := y*front_buffer.width + x
556		back := &back_buffer.cells[cell_offset]
557		front := &front_buffer.cells[cell_offset]
558		attr, char := cell_to_char_info(*back)
559		charbuf = append(charbuf, char_info{attr: attr, char: char[0]})
560		*front = *back
561		n++
562		w := runewidth.RuneWidth(back.Ch)
563		if w == 0 || w == 2 && runewidth.IsAmbiguousWidth(back.Ch) {
564			w = 1
565		}
566		x += w
567		// If not CJK, fill trailing space with whitespace
568		if !is_cjk && w == 2 {
569			charbuf = append(charbuf, char_info{attr: attr, char: ' '})
570		}
571	}
572	return n
573}
574
575// compares 'back_buffer' with 'front_buffer' and prepares all changes in the form of
576// 'diff_msg's in the 'diff_buf'
577func prepare_diff_messages() {
578	// clear buffers
579	diffbuf = diffbuf[:0]
580	charbuf = charbuf[:0]
581
582	var diff diff_msg
583	gbeg := 0
584	for y := 0; y < front_buffer.height; y++ {
585		same := true
586		line_offset := y * front_buffer.width
587		for x := 0; x < front_buffer.width; x++ {
588			cell_offset := line_offset + x
589			back := &back_buffer.cells[cell_offset]
590			front := &front_buffer.cells[cell_offset]
591			if *back != *front {
592				same = false
593				break
594			}
595		}
596		if same && diff.lines > 0 {
597			diffbuf = append(diffbuf, diff)
598			diff = diff_msg{}
599		}
600		if !same {
601			beg := len(charbuf)
602			end := beg + append_diff_line(y)
603			if diff.lines == 0 {
604				diff.pos = short(y)
605				gbeg = beg
606			}
607			diff.lines++
608			diff.chars = charbuf[gbeg:end]
609		}
610	}
611	if diff.lines > 0 {
612		diffbuf = append(diffbuf, diff)
613		diff = diff_msg{}
614	}
615}
616
617func get_ct(table []word, idx int) word {
618	idx = idx & 0x0F
619	if idx >= len(table) {
620		idx = len(table) - 1
621	}
622	return table[idx]
623}
624
625func cell_to_char_info(c Cell) (attr word, wc [2]wchar) {
626	attr = get_ct(color_table_fg, int(c.Fg)) | get_ct(color_table_bg, int(c.Bg))
627	if c.Fg&AttrReverse|c.Bg&AttrReverse != 0 {
628		attr = (attr&0xF0)>>4 | (attr&0x0F)<<4
629	}
630	if c.Fg&AttrBold != 0 {
631		attr |= foreground_intensity
632	}
633	if c.Bg&AttrBold != 0 {
634		attr |= background_intensity
635	}
636
637	r0, r1 := utf16.EncodeRune(c.Ch)
638	if r0 == 0xFFFD {
639		wc[0] = wchar(c.Ch)
640		wc[1] = ' '
641	} else {
642		wc[0] = wchar(r0)
643		wc[1] = wchar(r1)
644	}
645	return
646}
647
648func move_cursor(x, y int) {
649	err := set_console_cursor_position(out, coord{short(x), short(y)})
650	if err != nil {
651		panic(err)
652	}
653}
654
655func show_cursor(visible bool) {
656	var v int32
657	if visible {
658		v = 1
659	}
660
661	var info console_cursor_info
662	info.size = 100
663	info.visible = v
664	err := set_console_cursor_info(out, &info)
665	if err != nil {
666		panic(err)
667	}
668}
669
670func clear() {
671	var err error
672	attr, char := cell_to_char_info(Cell{
673		' ',
674		foreground,
675		background,
676	})
677
678	area := int(term_size.x) * int(term_size.y)
679	err = fill_console_output_attribute(out, attr, area)
680	if err != nil {
681		panic(err)
682	}
683	err = fill_console_output_character(out, char[0], area)
684	if err != nil {
685		panic(err)
686	}
687	if !is_cursor_hidden(cursor_x, cursor_y) {
688		move_cursor(cursor_x, cursor_y)
689	}
690}
691
692func key_event_record_to_event(r *key_event_record) (Event, bool) {
693	if r.key_down == 0 {
694		return Event{}, false
695	}
696
697	e := Event{Type: EventKey}
698	if input_mode&InputAlt != 0 {
699		if alt_mode_esc {
700			e.Mod = ModAlt
701			alt_mode_esc = false
702		}
703		if r.control_key_state&(left_alt_pressed|right_alt_pressed) != 0 {
704			e.Mod = ModAlt
705		}
706	}
707
708	ctrlpressed := r.control_key_state&(left_ctrl_pressed|right_ctrl_pressed) != 0
709
710	if r.virtual_key_code >= vk_f1 && r.virtual_key_code <= vk_f12 {
711		switch r.virtual_key_code {
712		case vk_f1:
713			e.Key = KeyF1
714		case vk_f2:
715			e.Key = KeyF2
716		case vk_f3:
717			e.Key = KeyF3
718		case vk_f4:
719			e.Key = KeyF4
720		case vk_f5:
721			e.Key = KeyF5
722		case vk_f6:
723			e.Key = KeyF6
724		case vk_f7:
725			e.Key = KeyF7
726		case vk_f8:
727			e.Key = KeyF8
728		case vk_f9:
729			e.Key = KeyF9
730		case vk_f10:
731			e.Key = KeyF10
732		case vk_f11:
733			e.Key = KeyF11
734		case vk_f12:
735			e.Key = KeyF12
736		default:
737			panic("unreachable")
738		}
739
740		return e, true
741	}
742
743	if r.virtual_key_code <= vk_delete {
744		switch r.virtual_key_code {
745		case vk_insert:
746			e.Key = KeyInsert
747		case vk_delete:
748			e.Key = KeyDelete
749		case vk_home:
750			e.Key = KeyHome
751		case vk_end:
752			e.Key = KeyEnd
753		case vk_pgup:
754			e.Key = KeyPgup
755		case vk_pgdn:
756			e.Key = KeyPgdn
757		case vk_arrow_up:
758			e.Key = KeyArrowUp
759		case vk_arrow_down:
760			e.Key = KeyArrowDown
761		case vk_arrow_left:
762			e.Key = KeyArrowLeft
763		case vk_arrow_right:
764			e.Key = KeyArrowRight
765		case vk_backspace:
766			if ctrlpressed {
767				e.Key = KeyBackspace2
768			} else {
769				e.Key = KeyBackspace
770			}
771		case vk_tab:
772			e.Key = KeyTab
773		case vk_enter:
774			if ctrlpressed {
775				e.Key = KeyCtrlJ
776			} else {
777				e.Key = KeyEnter
778			}
779		case vk_esc:
780			switch {
781			case input_mode&InputEsc != 0:
782				e.Key = KeyEsc
783			case input_mode&InputAlt != 0:
784				alt_mode_esc = true
785				return Event{}, false
786			}
787		case vk_space:
788			if ctrlpressed {
789				// manual return here, because KeyCtrlSpace is zero
790				e.Key = KeyCtrlSpace
791				return e, true
792			} else {
793				e.Key = KeySpace
794			}
795		}
796
797		if e.Key != 0 {
798			return e, true
799		}
800	}
801
802	if ctrlpressed {
803		if Key(r.unicode_char) >= KeyCtrlA && Key(r.unicode_char) <= KeyCtrlRsqBracket {
804			e.Key = Key(r.unicode_char)
805			if input_mode&InputAlt != 0 && e.Key == KeyEsc {
806				alt_mode_esc = true
807				return Event{}, false
808			}
809			return e, true
810		}
811		switch r.virtual_key_code {
812		case 192, 50:
813			// manual return here, because KeyCtrl2 is zero
814			e.Key = KeyCtrl2
815			return e, true
816		case 51:
817			if input_mode&InputAlt != 0 {
818				alt_mode_esc = true
819				return Event{}, false
820			}
821			e.Key = KeyCtrl3
822		case 52:
823			e.Key = KeyCtrl4
824		case 53:
825			e.Key = KeyCtrl5
826		case 54:
827			e.Key = KeyCtrl6
828		case 189, 191, 55:
829			e.Key = KeyCtrl7
830		case 8, 56:
831			e.Key = KeyCtrl8
832		}
833
834		if e.Key != 0 {
835			return e, true
836		}
837	}
838
839	if r.unicode_char != 0 {
840		e.Ch = rune(r.unicode_char)
841		return e, true
842	}
843
844	return Event{}, false
845}
846
847func input_event_producer() {
848	var r input_record
849	var err error
850	var last_button Key
851	var last_button_pressed Key
852	var last_state = dword(0)
853	var last_x, last_y = -1, -1
854	handles := []syscall.Handle{in, interrupt}
855	for {
856		err = wait_for_multiple_objects(handles)
857		if err != nil {
858			input_comm <- Event{Type: EventError, Err: err}
859		}
860
861		select {
862		case <-cancel_comm:
863			cancel_done_comm <- true
864			return
865		default:
866		}
867
868		err = read_console_input(in, &r)
869		if err != nil {
870			input_comm <- Event{Type: EventError, Err: err}
871		}
872
873		switch r.event_type {
874		case key_event:
875			kr := (*key_event_record)(unsafe.Pointer(&r.event))
876			ev, ok := key_event_record_to_event(kr)
877			if ok {
878				for i := 0; i < int(kr.repeat_count); i++ {
879					input_comm <- ev
880				}
881			}
882		case window_buffer_size_event:
883			sr := *(*window_buffer_size_record)(unsafe.Pointer(&r.event))
884			input_comm <- Event{
885				Type:   EventResize,
886				Width:  int(sr.size.x),
887				Height: int(sr.size.y),
888			}
889		case mouse_event:
890			mr := *(*mouse_event_record)(unsafe.Pointer(&r.event))
891			ev := Event{Type: EventMouse}
892			switch mr.event_flags {
893			case 0, 2:
894				// single or double click
895				cur_state := mr.button_state
896				switch {
897				case last_state&mouse_lmb == 0 && cur_state&mouse_lmb != 0:
898					last_button = MouseLeft
899					last_button_pressed = last_button
900				case last_state&mouse_rmb == 0 && cur_state&mouse_rmb != 0:
901					last_button = MouseRight
902					last_button_pressed = last_button
903				case last_state&mouse_mmb == 0 && cur_state&mouse_mmb != 0:
904					last_button = MouseMiddle
905					last_button_pressed = last_button
906				case last_state&mouse_lmb != 0 && cur_state&mouse_lmb == 0:
907					last_button = MouseRelease
908				case last_state&mouse_rmb != 0 && cur_state&mouse_rmb == 0:
909					last_button = MouseRelease
910				case last_state&mouse_mmb != 0 && cur_state&mouse_mmb == 0:
911					last_button = MouseRelease
912				default:
913					last_state = cur_state
914					continue
915				}
916				last_state = cur_state
917				ev.Key = last_button
918				last_x, last_y = int(mr.mouse_pos.x), int(mr.mouse_pos.y)
919				ev.MouseX = last_x
920				ev.MouseY = last_y
921			case 1:
922				// mouse motion
923				x, y := int(mr.mouse_pos.x), int(mr.mouse_pos.y)
924				if last_state != 0 && (last_x != x || last_y != y) {
925					ev.Key = last_button_pressed
926					ev.Mod = ModMotion
927					ev.MouseX = x
928					ev.MouseY = y
929					last_x, last_y = x, y
930				} else {
931					ev.Type = EventNone
932				}
933			case 4:
934				// mouse wheel
935				n := int16(mr.button_state >> 16)
936				if n > 0 {
937					ev.Key = MouseWheelUp
938				} else {
939					ev.Key = MouseWheelDown
940				}
941				last_x, last_y = int(mr.mouse_pos.x), int(mr.mouse_pos.y)
942				ev.MouseX = last_x
943				ev.MouseY = last_y
944			default:
945				ev.Type = EventNone
946			}
947			if ev.Type != EventNone {
948				input_comm <- ev
949			}
950		}
951	}
952}
953