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} 949