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}