aboutsummaryrefslogblamecommitdiffstats
path: root/vendor/github.com/nsf/termbox-go/termbox_windows.go
blob: f7dad7b8a5fbf45f3d6c019e36f7a49a497f957b (plain) (tree)


































































































































                                                                                                
                                      














































































































































































                                                                                                           
                                      























































































































































































































                                                                                     







                                         
                                                         
                                                                                    



























































































































































































































                                                                                                
                                   
                                 
                                   




































                                                                                     
                                                     
                                               

                                                         



                                                                                           
                                                                         

                                                                                           
                                                                         

                                                                                           






                                                                                           




                                                              






















                                                                                         
                                 







                                                                                         



                         
package termbox

import "syscall"
import "unsafe"
import "unicode/utf16"
import "github.com/mattn/go-runewidth"

type (
    wchar     uint16
    short     int16
    dword     uint32
    word      uint16
    char_info struct {
        char wchar
        attr word
    }
    coord struct {
        x short
        y short
    }
    small_rect struct {
        left   short
        top    short
        right  short
        bottom short
    }
    console_screen_buffer_info struct {
        size                coord
        cursor_position     coord
        attributes          word
        window              small_rect
        maximum_window_size coord
    }
    console_cursor_info struct {
        size    dword
        visible int32
    }
    input_record struct {
        event_type word
        _          [2]byte
        event      [16]byte
    }
    key_event_record struct {
        key_down          int32
        repeat_count      word
        virtual_key_code  word
        virtual_scan_code word
        unicode_char      wchar
        control_key_state dword
    }
    window_buffer_size_record struct {
        size coord
    }
    mouse_event_record struct {
        mouse_pos         coord
        button_state      dword
        control_key_state dword
        event_flags       dword
    }
)

const (
    mouse_lmb = 0x1
    mouse_rmb = 0x2
    mouse_mmb = 0x4 | 0x8 | 0x10
)

func (this coord) uintptr() uintptr {
    return uintptr(*(*int32)(unsafe.Pointer(&this)))
}

var kernel32 = syscall.NewLazyDLL("kernel32.dll")
var is_cjk = runewidth.IsEastAsian()

var (
    proc_set_console_active_screen_buffer = kernel32.NewProc("SetConsoleActiveScreenBuffer")
    proc_set_console_screen_buffer_size   = kernel32.NewProc("SetConsoleScreenBufferSize")
    proc_create_console_screen_buffer     = kernel32.NewProc("CreateConsoleScreenBuffer")
    proc_get_console_screen_buffer_info   = kernel32.NewProc("GetConsoleScreenBufferInfo")
    proc_write_console_output             = kernel32.NewProc("WriteConsoleOutputW")
    proc_write_console_output_character   = kernel32.NewProc("WriteConsoleOutputCharacterW")
    proc_write_console_output_attribute   = kernel32.NewProc("WriteConsoleOutputAttribute")
    proc_set_console_cursor_info          = kernel32.NewProc("SetConsoleCursorInfo")
    proc_set_console_cursor_position      = kernel32.NewProc("SetConsoleCursorPosition")
    proc_get_console_cursor_info          = kernel32.NewProc("GetConsoleCursorInfo")
    proc_read_console_input               = kernel32.NewProc("ReadConsoleInputW")
    proc_get_console_mode                 = kernel32.NewProc("GetConsoleMode")
    proc_set_console_mode                 = kernel32.NewProc("SetConsoleMode")
    proc_fill_console_output_character    = kernel32.NewProc("FillConsoleOutputCharacterW")
    proc_fill_console_output_attribute    = kernel32.NewProc("FillConsoleOutputAttribute")
    proc_create_event                     = kernel32.NewProc("CreateEventW")
    proc_wait_for_multiple_objects        = kernel32.NewProc("WaitForMultipleObjects")
    proc_set_event                        = kernel32.NewProc("SetEvent")
)

func set_console_active_screen_buffer(h syscall.Handle) (err error) {
    r0, _, e1 := syscall.Syscall(proc_set_console_active_screen_buffer.Addr(),
        1, uintptr(h), 0, 0)
    if int(r0) == 0 {
        if e1 != 0 {
            err = error(e1)
        } else {
            err = syscall.EINVAL
        }
    }
    return
}

func set_console_screen_buffer_size(h syscall.Handle, size coord) (err error) {
    r0, _, e1 := syscall.Syscall(proc_set_console_screen_buffer_size.Addr(),
        2, uintptr(h), size.uintptr(), 0)
    if int(r0) == 0 {
        if e1 != 0 {
            err = error(e1)
        } else {
            err = syscall.EINVAL
        }
    }
    return
}

func create_console_screen_buffer() (h syscall.Handle, err error) {
    r0, _, e1 := syscall.Syscall6(proc_create_console_screen_buffer.Addr(),
        5, uintptr(generic_read|generic_write), 0, 0, console_textmode_buffer, 0, 0)
    if int(r0) == 0 {
        if e1 != 0 {
            err = error(e1)
        } else {
            err = syscall.EINVAL
        }
    }
    return syscall.Handle(r0), err
}

func get_console_screen_buffer_info(h syscall.Handle, info *console_screen_buffer_info) (err error) {
    r0, _, e1 := syscall.Syscall(proc_get_console_screen_buffer_info.Addr(),
        2, uintptr(h), uintptr(unsafe.Pointer(info)), 0)
    if int(r0) == 0 {
        if e1 != 0 {
            err = error(e1)
        } else {
            err = syscall.EINVAL
        }
    }
    return
}

func write_console_output(h syscall.Handle, chars []char_info, dst small_rect) (err error) {
    tmp_coord = coord{dst.right - dst.left + 1, dst.bottom - dst.top + 1}
    tmp_rect = dst
    r0, _, e1 := syscall.Syscall6(proc_write_console_output.Addr(),
        5, uintptr(h), uintptr(unsafe.Pointer(&chars[0])), tmp_coord.uintptr(),
        tmp_coord0.uintptr(), uintptr(unsafe.Pointer(&tmp_rect)), 0)
    if int(r0) == 0 {
        if e1 != 0 {
            err = error(e1)
        } else {
            err = syscall.EINVAL
        }
    }
    return
}

func write_console_output_character(h syscall.Handle, chars []wchar, pos coord) (err error) {
    r0, _, e1 := syscall.Syscall6(proc_write_console_output_character.Addr(),
        5, uintptr(h), uintptr(unsafe.Pointer(&chars[0])), uintptr(len(chars)),
        pos.uintptr(), uintptr(unsafe.Pointer(&tmp_arg)), 0)
    if int(r0) == 0 {
        if e1 != 0 {
            err = error(e1)
        } else {
            err = syscall.EINVAL
        }
    }
    return
}

func write_console_output_attribute(h syscall.Handle, attrs []word, pos coord) (err error) {
    r0, _, e1 := syscall.Syscall6(proc_write_console_output_attribute.Addr(),
        5, uintptr(h), uintptr(unsafe.Pointer(&attrs[0])), uintptr(len(attrs)),
        pos.uintptr(), uintptr(unsafe.Pointer(&tmp_arg)), 0)
    if int(r0) == 0 {
        if e1 != 0 {
            err = error(e1)
        } else {
            err = syscall.EINVAL
        }
    }
    return
}

func set_console_cursor_info(h syscall.Handle, info *console_cursor_info) (err error) {
    r0, _, e1 := syscall.Syscall(proc_set_console_cursor_info.Addr(),
        2, uintptr(h), uintptr(unsafe.Pointer(info)), 0)
    if int(r0) == 0 {
        if e1 != 0 {
            err = error(e1)
        } else {
            err = syscall.EINVAL
        }
    }
    return
}

func get_console_cursor_info(h syscall.Handle, info *console_cursor_info) (err error) {
    r0, _, e1 := syscall.Syscall(proc_get_console_cursor_info.Addr(),
        2, uintptr(h), uintptr(unsafe.Pointer(info)), 0)
    if int(r0) == 0 {
        if e1 != 0 {
            err = error(e1)
        } else {
            err = syscall.EINVAL
        }
    }
    return
}

func set_console_cursor_position(h syscall.Handle, pos coord) (err error) {
    r0, _, e1 := syscall.Syscall(proc_set_console_cursor_position.Addr(),
        2, uintptr(h), pos.uintptr(), 0)
    if int(r0) == 0 {
        if e1 != 0 {
            err = error(e1)
        } else {
            err = syscall.EINVAL
        }
    }
    return
}

func read_console_input(h syscall.Handle, record *input_record) (err error) {
    r0, _, e1 := syscall.Syscall6(proc_read_console_input.Addr(),
        4, uintptr(h), uintptr(unsafe.Pointer(record)), 1, uintptr(unsafe.Pointer(&tmp_arg)), 0, 0)
    if int(r0) == 0 {
        if e1 != 0 {
            err = error(e1)
        } else {
            err = syscall.EINVAL
        }
    }
    return
}

func get_console_mode(h syscall.Handle, mode *dword) (err error) {
    r0, _, e1 := syscall.Syscall(proc_get_console_mode.Addr(),
        2, uintptr(h), uintptr(unsafe.Pointer(mode)), 0)
    if int(r0) == 0 {
        if e1 != 0 {
            err = error(e1)
        } else {
            err = syscall.EINVAL
        }
    }
    return
}

func set_console_mode(h syscall.Handle, mode dword) (err error) {
    r0, _, e1 := syscall.Syscall(proc_set_console_mode.Addr(),
        2, uintptr(h), uintptr(mode), 0)
    if int(r0) == 0 {
        if e1 != 0 {
            err = error(e1)
        } else {
            err = syscall.EINVAL
        }
    }
    return
}

func fill_console_output_character(h syscall.Handle, char wchar, n int) (err error) {
    r0, _, e1 := syscall.Syscall6(proc_fill_console_output_character.Addr(),
        5, uintptr(h), uintptr(char), uintptr(n), tmp_coord.uintptr(),
        uintptr(unsafe.Pointer(&tmp_arg)), 0)
    if int(r0) == 0 {
        if e1 != 0 {
            err = error(e1)
        } else {
            err = syscall.EINVAL
        }
    }
    return
}

func fill_console_output_attribute(h syscall.Handle, attr word, n int) (err error) {
    r0, _, e1 := syscall.Syscall6(proc_fill_console_output_attribute.Addr(),
        5, uintptr(h), uintptr(attr), uintptr(n), tmp_coord.uintptr(),
        uintptr(unsafe.Pointer(&tmp_arg)), 0)
    if int(r0) == 0 {
        if e1 != 0 {
            err = error(e1)
        } else {
            err = syscall.EINVAL
        }
    }
    return
}

func create_event() (out syscall.Handle, err error) {
    r0, _, e1 := syscall.Syscall6(proc_create_event.Addr(),
        4, 0, 0, 0, 0, 0, 0)
    if int(r0) == 0 {
        if e1 != 0 {
            err = error(e1)
        } else {
            err = syscall.EINVAL
        }
    }
    return syscall.Handle(r0), err
}

func wait_for_multiple_objects(objects []syscall.Handle) (err error) {
    r0, _, e1 := syscall.Syscall6(proc_wait_for_multiple_objects.Addr(),
        4, uintptr(len(objects)), uintptr(unsafe.Pointer(&objects[0])),
        0, 0xFFFFFFFF, 0, 0)
    if uint32(r0) == 0xFFFFFFFF {
        if e1 != 0 {
            err = error(e1)
        } else {
            err = syscall.EINVAL
        }
    }
    return
}

func set_event(ev syscall.Handle) (err error) {
    r0, _, e1 := syscall.Syscall(proc_set_event.Addr(),
        1, uintptr(ev), 0, 0)
    if int(r0) == 0 {
        if e1 != 0 {
            err = error(e1)
        } else {
            err = syscall.EINVAL
        }
    }
    return
}

type diff_msg struct {
    pos   short
    lines short
    chars []char_info
}

type input_event struct {
    event Event
    err   error
}

var (
    orig_cursor_info console_cursor_info
    orig_size        coord
    orig_mode        dword
    orig_screen      syscall.Handle
    back_buffer      cellbuf
    front_buffer     cellbuf
    term_size        coord
    input_mode       = InputEsc
    cursor_x         = cursor_hidden
    cursor_y         = cursor_hidden
    foreground       = ColorDefault
    background       = ColorDefault
    in               syscall.Handle
    out              syscall.Handle
    interrupt        syscall.Handle
    charbuf          []char_info
    diffbuf          []diff_msg
    beg_x            = -1
    beg_y            = -1
    beg_i            = -1
    input_comm       = make(chan Event)
    interrupt_comm   = make(chan struct{})
    cancel_comm      = make(chan bool, 1)
    cancel_done_comm = make(chan bool)
    alt_mode_esc     = false

    // these ones just to prevent heap allocs at all costs
    tmp_info   console_screen_buffer_info
    tmp_arg    dword
    tmp_coord0 = coord{0, 0}
    tmp_coord  = coord{0, 0}
    tmp_rect   = small_rect{0, 0, 0, 0}
)

func get_cursor_position(out syscall.Handle) coord {
    err := get_console_screen_buffer_info(out, &tmp_info)
    if err != nil {
        panic(err)
    }
    return tmp_info.cursor_position
}

func get_term_size(out syscall.Handle) coord {
    err := get_console_screen_buffer_info(out, &tmp_info)
    if err != nil {
        panic(err)
    }
    return tmp_info.size
}

func get_win_size(out syscall.Handle) coord {
    err := get_console_screen_buffer_info(out, &tmp_info)
    if err != nil {
        panic(err)
    }
    return coord{
        x: tmp_info.window.right - tmp_info.window.left + 1,
        y: tmp_info.window.bottom - tmp_info.window.top + 1,
    }
}

func update_size_maybe() {
    size := get_term_size(out)
    if size.x != term_size.x || size.y != term_size.y {
        term_size = size
        back_buffer.resize(int(size.x), int(size.y))
        front_buffer.resize(int(size.x), int(size.y))
        front_buffer.clear()
        clear()

        area := int(size.x) * int(size.y)
        if cap(charbuf) < area {
            charbuf = make([]char_info, 0, area)
        }
    }
}

var color_table_bg = []word{
    0, // default (black)
    0, // black
    background_red,
    background_green,
    background_red | background_green, // yellow
    background_blue,
    background_red | background_blue,                    // magenta
    background_green | background_blue,                  // cyan
    background_red | background_blue | background_green, // white
}

var color_table_fg = []word{
    foreground_red | foreground_blue | foreground_green, // default (white)
    0,
    foreground_red,
    foreground_green,
    foreground_red | foreground_green, // yellow
    foreground_blue,
    foreground_red | foreground_blue,                    // magenta
    foreground_green | foreground_blue,                  // cyan
    foreground_red | foreground_blue | foreground_green, // white
}

const (
    replacement_char = '\uFFFD'
    max_rune         = '\U0010FFFF'
    surr1            = 0xd800
    surr2            = 0xdc00
    surr3            = 0xe000
    surr_self        = 0x10000
)

func append_diff_line(y int) int {
    n := 0
    for x := 0; x < front_buffer.width; {
        cell_offset := y*front_buffer.width + x
        back := &back_buffer.cells[cell_offset]
        front := &front_buffer.cells[cell_offset]
        attr, char := cell_to_char_info(*back)
        charbuf = append(charbuf, char_info{attr: attr, char: char[0]})
        *front = *back
        n++
        w := runewidth.RuneWidth(back.Ch)
        if w == 0 || w == 2 && runewidth.IsAmbiguousWidth(back.Ch) {
            w = 1
        }
        x += w
        // If not CJK, fill trailing space with whitespace
        if !is_cjk && w == 2 {
            charbuf = append(charbuf, char_info{attr: attr, char: ' '})
        }
    }
    return n
}

// compares 'back_buffer' with 'front_buffer' and prepares all changes in the form of
// 'diff_msg's in the 'diff_buf'
func prepare_diff_messages() {
    // clear buffers
    diffbuf = diffbuf[:0]
    charbuf = charbuf[:0]

    var diff diff_msg
    gbeg := 0
    for y := 0; y < front_buffer.height; y++ {
        same := true
        line_offset := y * front_buffer.width
        for x := 0; x < front_buffer.width; x++ {
            cell_offset := line_offset + x
            back := &back_buffer.cells[cell_offset]
            front := &front_buffer.cells[cell_offset]
            if *back != *front {
                same = false
                break
            }
        }
        if same && diff.lines > 0 {
            diffbuf = append(diffbuf, diff)
            diff = diff_msg{}
        }
        if !same {
            beg := len(charbuf)
            end := beg + append_diff_line(y)
            if diff.lines == 0 {
                diff.pos = short(y)
                gbeg = beg
            }
            diff.lines++
            diff.chars = charbuf[gbeg:end]
        }
    }
    if diff.lines > 0 {
        diffbuf = append(diffbuf, diff)
        diff = diff_msg{}
    }
}

func get_ct(table []word, idx int) word {
    idx = idx & 0x0F
    if idx >= len(table) {
        idx = len(table) - 1
    }
    return table[idx]
}

func cell_to_char_info(c Cell) (attr word, wc [2]wchar) {
    attr = get_ct(color_table_fg, int(c.Fg)) | get_ct(color_table_bg, int(c.Bg))
    if c.Fg&AttrReverse|c.Bg&AttrReverse != 0 {
        attr = (attr&0xF0)>>4 | (attr&0x0F)<<4
    }
    if c.Fg&AttrBold != 0 {
        attr |= foreground_intensity
    }
    if c.Bg&AttrBold != 0 {
        attr |= background_intensity
    }

    r0, r1 := utf16.EncodeRune(c.Ch)
    if r0 == 0xFFFD {
        wc[0] = wchar(c.Ch)
        wc[1] = ' '
    } else {
        wc[0] = wchar(r0)
        wc[1] = wchar(r1)
    }
    return
}

func move_cursor(x, y int) {
    err := set_console_cursor_position(out, coord{short(x), short(y)})
    if err != nil {
        panic(err)
    }
}

func show_cursor(visible bool) {
    var v int32
    if visible {
        v = 1
    }

    var info console_cursor_info
    info.size = 100
    info.visible = v
    err := set_console_cursor_info(out, &info)
    if err != nil {
        panic(err)
    }
}

func clear() {
    var err error
    attr, char := cell_to_char_info(Cell{
        ' ',
        foreground,
        background,
    })

    area := int(term_size.x) * int(term_size.y)
    err = fill_console_output_attribute(out, attr, area)
    if err != nil {
        panic(err)
    }
    err = fill_console_output_character(out, char[0], area)
    if err != nil {
        panic(err)
    }
    if !is_cursor_hidden(cursor_x, cursor_y) {
        move_cursor(cursor_x, cursor_y)
    }
}

func key_event_record_to_event(r *key_event_record) (Event, bool) {
    if r.key_down == 0 {
        return Event{}, false
    }

    e := Event{Type: EventKey}
    if input_mode&InputAlt != 0 {
        if alt_mode_esc {
            e.Mod = ModAlt
            alt_mode_esc = false
        }
        if r.control_key_state&(left_alt_pressed|right_alt_pressed) != 0 {
            e.Mod = ModAlt
        }
    }

    ctrlpressed := r.control_key_state&(left_ctrl_pressed|right_ctrl_pressed) != 0

    if r.virtual_key_code >= vk_f1 && r.virtual_key_code <= vk_f12 {
        switch r.virtual_key_code {
        case vk_f1:
            e.Key = KeyF1
        case vk_f2:
            e.Key = KeyF2
        case vk_f3:
            e.Key = KeyF3
        case vk_f4:
            e.Key = KeyF4
        case vk_f5:
            e.Key = KeyF5
        case vk_f6:
            e.Key = KeyF6
        case vk_f7:
            e.Key = KeyF7
        case vk_f8:
            e.Key = KeyF8
        case vk_f9:
            e.Key = KeyF9
        case vk_f10:
            e.Key = KeyF10
        case vk_f11:
            e.Key = KeyF11
        case vk_f12:
            e.Key = KeyF12
        default:
            panic("unreachable")
        }

        return e, true
    }

    if r.virtual_key_code <= vk_delete {
        switch r.virtual_key_code {
        case vk_insert:
            e.Key = KeyInsert
        case vk_delete:
            e.Key = KeyDelete
        case vk_home:
            e.Key = KeyHome
        case vk_end:
            e.Key = KeyEnd
        case vk_pgup:
            e.Key = KeyPgup
        case vk_pgdn:
            e.Key = KeyPgdn
        case vk_arrow_up:
            e.Key = KeyArrowUp
        case vk_arrow_down:
            e.Key = KeyArrowDown
        case vk_arrow_left:
            e.Key = KeyArrowLeft
        case vk_arrow_right:
            e.Key = KeyArrowRight
        case vk_backspace:
            if ctrlpressed {
                e.Key = KeyBackspace2
            } else {
                e.Key = KeyBackspace
            }
        case vk_tab:
            e.Key = KeyTab
        case vk_enter:
            e.Key = KeyEnter
        case vk_esc:
            switch {
            case input_mode&InputEsc != 0:
                e.Key = KeyEsc
            case input_mode&InputAlt != 0:
                alt_mode_esc = true
                return Event{}, false
            }
        case vk_space:
            if ctrlpressed {
                // manual return here, because KeyCtrlSpace is zero
                e.Key = KeyCtrlSpace
                return e, true
            } else {
                e.Key = KeySpace
            }
        }

        if e.Key != 0 {
            return e, true
        }
    }

    if ctrlpressed {
        if Key(r.unicode_char) >= KeyCtrlA && Key(r.unicode_char) <= KeyCtrlRsqBracket {
            e.Key = Key(r.unicode_char)
            if input_mode&InputAlt != 0 && e.Key == KeyEsc {
                alt_mode_esc = true
                return Event{}, false
            }
            return e, true
        }
        switch r.virtual_key_code {
        case 192, 50:
            // manual return here, because KeyCtrl2 is zero
            e.Key = KeyCtrl2
            return e, true
        case 51:
            if input_mode&InputAlt != 0 {
                alt_mode_esc = true
                return Event{}, false
            }
            e.Key = KeyCtrl3
        case 52:
            e.Key = KeyCtrl4
        case 53:
            e.Key = KeyCtrl5
        case 54:
            e.Key = KeyCtrl6
        case 189, 191, 55:
            e.Key = KeyCtrl7
        case 8, 56:
            e.Key = KeyCtrl8
        }

        if e.Key != 0 {
            return e, true
        }
    }

    if r.unicode_char != 0 {
        e.Ch = rune(r.unicode_char)
        return e, true
    }

    return Event{}, false
}

func input_event_producer() {
    var r input_record
    var err error
    var last_button Key
    var last_button_pressed Key
    var last_state = dword(0)
    var last_x, last_y = -1, -1
    handles := []syscall.Handle{in, interrupt}
    for {
        err = wait_for_multiple_objects(handles)
        if err != nil {
            input_comm <- Event{Type: EventError, Err: err}
        }

        select {
        case <-cancel_comm:
            cancel_done_comm <- true
            return
        default:
        }

        err = read_console_input(in, &r)
        if err != nil {
            input_comm <- Event{Type: EventError, Err: err}
        }

        switch r.event_type {
        case key_event:
            kr := (*key_event_record)(unsafe.Pointer(&r.event))
            ev, ok := key_event_record_to_event(kr)
            if ok {
                for i := 0; i < int(kr.repeat_count); i++ {
                    input_comm <- ev
                }
            }
        case window_buffer_size_event:
            sr := *(*window_buffer_size_record)(unsafe.Pointer(&r.event))
            input_comm <- Event{
                Type:   EventResize,
                Width:  int(sr.size.x),
                Height: int(sr.size.y),
            }
        case mouse_event:
            mr := *(*mouse_event_record)(unsafe.Pointer(&r.event))
            ev := Event{Type: EventMouse}
            switch mr.event_flags {
            case 0, 2:
                // single or double click
                cur_state := mr.button_state
                switch {
                case last_state&mouse_lmb == 0 && cur_state&mouse_lmb != 0:
                    last_button = MouseLeft
                    last_button_pressed = last_button
                case last_state&mouse_rmb == 0 && cur_state&mouse_rmb != 0:
                    last_button = MouseRight
                    last_button_pressed = last_button
                case last_state&mouse_mmb == 0 && cur_state&mouse_mmb != 0:
                    last_button = MouseMiddle
                    last_button_pressed = last_button
                case last_state&mouse_lmb != 0 && cur_state&mouse_lmb == 0:
                    last_button = MouseRelease
                case last_state&mouse_rmb != 0 && cur_state&mouse_rmb == 0:
                    last_button = MouseRelease
                case last_state&mouse_mmb != 0 && cur_state&mouse_mmb == 0:
                    last_button = MouseRelease
                default:
                    last_state = cur_state
                    continue
                }
                last_state = cur_state
                ev.Key = last_button
                last_x, last_y = int(mr.mouse_pos.x), int(mr.mouse_pos.y)
                ev.MouseX = last_x
                ev.MouseY = last_y
            case 1:
                // mouse motion
                x, y := int(mr.mouse_pos.x), int(mr.mouse_pos.y)
                if last_state != 0 && (last_x != x || last_y != y) {
                    ev.Key = last_button_pressed
                    ev.Mod = ModMotion
                    ev.MouseX = x
                    ev.MouseY = y
                    last_x, last_y = x, y
                } else {
                    ev.Type = EventNone
                }
            case 4:
                // mouse wheel
                n := int16(mr.button_state >> 16)
                if n > 0 {
                    ev.Key = MouseWheelUp
                } else {
                    ev.Key = MouseWheelDown
                }
                last_x, last_y = int(mr.mouse_pos.x), int(mr.mouse_pos.y)
                ev.MouseX = last_x
                ev.MouseY = last_y
            default:
                ev.Type = EventNone
            }
            if ev.Type != EventNone {
                input_comm <- ev
            }
        }
    }
}