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