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