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 e.Key = KeyEnter
775 case vk_esc:
776 switch {
777 case input_mode&InputEsc != 0:
778 e.Key = KeyEsc
779 case input_mode&InputAlt != 0:
780 alt_mode_esc = true
781 return Event{}, false
782 }
783 case vk_space:
784 if ctrlpressed {
785 // manual return here, because KeyCtrlSpace is zero
786 e.Key = KeyCtrlSpace
787 return e, true
788 } else {
789 e.Key = KeySpace
790 }
791 }
792
793 if e.Key != 0 {
794 return e, true
795 }
796 }
797
798 if ctrlpressed {
799 if Key(r.unicode_char) >= KeyCtrlA && Key(r.unicode_char) <= KeyCtrlRsqBracket {
800 e.Key = Key(r.unicode_char)
801 if input_mode&InputAlt != 0 && e.Key == KeyEsc {
802 alt_mode_esc = true
803 return Event{}, false
804 }
805 return e, true
806 }
807 switch r.virtual_key_code {
808 case 192, 50:
809 // manual return here, because KeyCtrl2 is zero
810 e.Key = KeyCtrl2
811 return e, true
812 case 51:
813 if input_mode&InputAlt != 0 {
814 alt_mode_esc = true
815 return Event{}, false
816 }
817 e.Key = KeyCtrl3
818 case 52:
819 e.Key = KeyCtrl4
820 case 53:
821 e.Key = KeyCtrl5
822 case 54:
823 e.Key = KeyCtrl6
824 case 189, 191, 55:
825 e.Key = KeyCtrl7
826 case 8, 56:
827 e.Key = KeyCtrl8
828 }
829
830 if e.Key != 0 {
831 return e, true
832 }
833 }
834
835 if r.unicode_char != 0 {
836 e.Ch = rune(r.unicode_char)
837 return e, true
838 }
839
840 return Event{}, false
841}
842
843func input_event_producer() {
844 var r input_record
845 var err error
846 var last_button Key
847 var last_button_pressed Key
848 var last_state = dword(0)
849 var last_x, last_y = -1, -1
850 handles := []syscall.Handle{in, interrupt}
851 for {
852 err = wait_for_multiple_objects(handles)
853 if err != nil {
854 input_comm <- Event{Type: EventError, Err: err}
855 }
856
857 select {
858 case <-cancel_comm:
859 cancel_done_comm <- true
860 return
861 default:
862 }
863
864 err = read_console_input(in, &r)
865 if err != nil {
866 input_comm <- Event{Type: EventError, Err: err}
867 }
868
869 switch r.event_type {
870 case key_event:
871 kr := (*key_event_record)(unsafe.Pointer(&r.event))
872 ev, ok := key_event_record_to_event(kr)
873 if ok {
874 for i := 0; i < int(kr.repeat_count); i++ {
875 input_comm <- ev
876 }
877 }
878 case window_buffer_size_event:
879 sr := *(*window_buffer_size_record)(unsafe.Pointer(&r.event))
880 input_comm <- Event{
881 Type: EventResize,
882 Width: int(sr.size.x),
883 Height: int(sr.size.y),
884 }
885 case mouse_event:
886 mr := *(*mouse_event_record)(unsafe.Pointer(&r.event))
887 ev := Event{Type: EventMouse}
888 switch mr.event_flags {
889 case 0, 2:
890 // single or double click
891 cur_state := mr.button_state
892 switch {
893 case last_state&mouse_lmb == 0 && cur_state&mouse_lmb != 0:
894 last_button = MouseLeft
895 last_button_pressed = last_button
896 case last_state&mouse_rmb == 0 && cur_state&mouse_rmb != 0:
897 last_button = MouseRight
898 last_button_pressed = last_button
899 case last_state&mouse_mmb == 0 && cur_state&mouse_mmb != 0:
900 last_button = MouseMiddle
901 last_button_pressed = last_button
902 case last_state&mouse_lmb != 0 && cur_state&mouse_lmb == 0:
903 last_button = MouseRelease
904 case last_state&mouse_rmb != 0 && cur_state&mouse_rmb == 0:
905 last_button = MouseRelease
906 case last_state&mouse_mmb != 0 && cur_state&mouse_mmb == 0:
907 last_button = MouseRelease
908 default:
909 last_state = cur_state
910 continue
911 }
912 last_state = cur_state
913 ev.Key = last_button
914 last_x, last_y = int(mr.mouse_pos.x), int(mr.mouse_pos.y)
915 ev.MouseX = last_x
916 ev.MouseY = last_y
917 case 1:
918 // mouse motion
919 x, y := int(mr.mouse_pos.x), int(mr.mouse_pos.y)
920 if last_state != 0 && (last_x != x || last_y != y) {
921 ev.Key = last_button_pressed
922 ev.Mod = ModMotion
923 ev.MouseX = x
924 ev.MouseY = y
925 last_x, last_y = x, y
926 } else {
927 ev.Type = EventNone
928 }
929 case 4:
930 // mouse wheel
931 n := int16(mr.button_state >> 16)
932 if n > 0 {
933 ev.Key = MouseWheelUp
934 } else {
935 ev.Key = MouseWheelDown
936 }
937 last_x, last_y = int(mr.mouse_pos.x), int(mr.mouse_pos.y)
938 ev.MouseX = last_x
939 ev.MouseY = last_y
940 default:
941 ev.Type = EventNone
942 }
943 if ev.Type != EventNone {
944 input_comm <- ev
945 }
946 }
947 }
948}