2017-01-14 06:07:43 +00:00
|
|
|
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
|
2015-04-09 04:21:36 +00:00
|
|
|
// Use of this source code is governed by a MIT license that can
|
|
|
|
// be found in the LICENSE file.
|
|
|
|
|
2015-04-08 16:06:47 +00:00
|
|
|
package termui
|
|
|
|
|
2015-08-19 19:22:53 +00:00
|
|
|
import (
|
2019-01-24 13:23:30 +00:00
|
|
|
"fmt"
|
2015-04-08 16:06:47 +00:00
|
|
|
|
2018-09-06 23:36:14 +00:00
|
|
|
tb "github.com/nsf/termbox-go"
|
2015-04-08 16:06:47 +00:00
|
|
|
)
|
|
|
|
|
2018-09-06 23:36:14 +00:00
|
|
|
/*
|
2018-11-29 02:19:34 +00:00
|
|
|
List of events:
|
2018-09-06 23:36:14 +00:00
|
|
|
mouse events:
|
|
|
|
<MouseLeft> <MouseRight> <MouseMiddle>
|
|
|
|
<MouseWheelUp> <MouseWheelDown>
|
|
|
|
keyboard events:
|
2019-01-24 04:12:10 +00:00
|
|
|
any uppercase or lowercase letter like j or J
|
2018-09-06 23:36:14 +00:00
|
|
|
<C-d> etc
|
|
|
|
<M-d> etc
|
|
|
|
<Up> <Down> <Left> <Right>
|
|
|
|
<Insert> <Delete> <Home> <End> <Previous> <Next>
|
|
|
|
<Backspace> <Tab> <Enter> <Escape> <Space>
|
|
|
|
<C-<Space>> etc
|
|
|
|
terminal events:
|
2019-01-25 11:41:35 +00:00
|
|
|
<Resize>
|
|
|
|
|
|
|
|
keyboard events that do not work:
|
|
|
|
<C-->
|
|
|
|
<C-2> <C-~>
|
|
|
|
<C-h>
|
|
|
|
<C-i>
|
|
|
|
<C-m>
|
|
|
|
<C-[> <C-3>
|
|
|
|
<C-\\>
|
|
|
|
<C-]>
|
|
|
|
<C-/> <C-_>
|
|
|
|
<C-8>
|
2018-09-06 23:36:14 +00:00
|
|
|
*/
|
|
|
|
|
2019-01-25 11:41:35 +00:00
|
|
|
type EventType uint
|
2018-09-06 23:36:14 +00:00
|
|
|
|
|
|
|
const (
|
|
|
|
KeyboardEvent EventType = iota
|
|
|
|
MouseEvent
|
|
|
|
ResizeEvent
|
|
|
|
)
|
|
|
|
|
2015-04-08 16:06:47 +00:00
|
|
|
type Event struct {
|
2018-09-06 23:36:14 +00:00
|
|
|
Type EventType
|
|
|
|
ID string
|
|
|
|
Payload interface{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mouse payload.
|
|
|
|
type Mouse struct {
|
|
|
|
Drag bool
|
|
|
|
X int
|
|
|
|
Y int
|
|
|
|
}
|
|
|
|
|
|
|
|
// Resize payload.
|
|
|
|
type Resize struct {
|
|
|
|
Width int
|
|
|
|
Height int
|
|
|
|
}
|
|
|
|
|
2018-11-29 23:15:57 +00:00
|
|
|
// PollEvents gets events from termbox, converts them, then sends them to each of its channels.
|
|
|
|
func PollEvents() <-chan Event {
|
2018-11-29 02:19:34 +00:00
|
|
|
ch := make(chan Event)
|
2018-09-06 23:36:14 +00:00
|
|
|
go func() {
|
2018-11-29 23:15:57 +00:00
|
|
|
for {
|
|
|
|
ch <- convertTermboxEvent(tb.PollEvent())
|
|
|
|
}
|
2018-09-06 23:36:14 +00:00
|
|
|
}()
|
2018-11-29 02:19:34 +00:00
|
|
|
return ch
|
2018-09-06 23:36:14 +00:00
|
|
|
}
|
2015-09-18 15:41:44 +00:00
|
|
|
|
2019-01-25 11:41:35 +00:00
|
|
|
var keyboardMap = map[tb.Key]string{
|
|
|
|
tb.KeyF1: "<F1>",
|
|
|
|
tb.KeyF2: "<F2>",
|
|
|
|
tb.KeyF3: "<F3>",
|
|
|
|
tb.KeyF4: "<F4>",
|
|
|
|
tb.KeyF5: "<F5>",
|
|
|
|
tb.KeyF6: "<F6>",
|
|
|
|
tb.KeyF7: "<F7>",
|
|
|
|
tb.KeyF8: "<F8>",
|
|
|
|
tb.KeyF9: "<F9>",
|
|
|
|
tb.KeyF10: "<F10>",
|
|
|
|
tb.KeyF11: "<F11>",
|
|
|
|
tb.KeyF12: "<F12>",
|
|
|
|
tb.KeyInsert: "<Insert>",
|
|
|
|
tb.KeyDelete: "<Delete>",
|
|
|
|
tb.KeyHome: "<Home>",
|
|
|
|
tb.KeyEnd: "<End>",
|
|
|
|
tb.KeyPgup: "<PageUp>",
|
|
|
|
tb.KeyPgdn: "<PageDown>",
|
|
|
|
tb.KeyArrowUp: "<Up>",
|
|
|
|
tb.KeyArrowDown: "<Down>",
|
|
|
|
tb.KeyArrowLeft: "<Left>",
|
|
|
|
tb.KeyArrowRight: "<Right>",
|
|
|
|
|
|
|
|
tb.KeyCtrlSpace: "<C-<Space>>", // tb.KeyCtrl2 tb.KeyCtrlTilde
|
|
|
|
tb.KeyCtrlA: "<C-a>",
|
|
|
|
tb.KeyCtrlB: "<C-b>",
|
|
|
|
tb.KeyCtrlC: "<C-c>",
|
|
|
|
tb.KeyCtrlD: "<C-d>",
|
|
|
|
tb.KeyCtrlE: "<C-e>",
|
|
|
|
tb.KeyCtrlF: "<C-f>",
|
|
|
|
tb.KeyCtrlG: "<C-g>",
|
|
|
|
tb.KeyBackspace: "<C-<Backspace>>", // tb.KeyCtrlH
|
|
|
|
tb.KeyTab: "<Tab>", // tb.KeyCtrlI
|
|
|
|
tb.KeyCtrlJ: "<C-j>",
|
|
|
|
tb.KeyCtrlK: "<C-k>",
|
|
|
|
tb.KeyCtrlL: "<C-l>",
|
|
|
|
tb.KeyEnter: "<Enter>", // tb.KeyCtrlM
|
|
|
|
tb.KeyCtrlN: "<C-n>",
|
|
|
|
tb.KeyCtrlO: "<C-o>",
|
|
|
|
tb.KeyCtrlP: "<C-p>",
|
|
|
|
tb.KeyCtrlQ: "<C-q>",
|
|
|
|
tb.KeyCtrlR: "<C-r>",
|
|
|
|
tb.KeyCtrlS: "<C-s>",
|
|
|
|
tb.KeyCtrlT: "<C-t>",
|
|
|
|
tb.KeyCtrlU: "<C-u>",
|
|
|
|
tb.KeyCtrlV: "<C-v>",
|
|
|
|
tb.KeyCtrlW: "<C-w>",
|
|
|
|
tb.KeyCtrlX: "<C-x>",
|
|
|
|
tb.KeyCtrlY: "<C-y>",
|
|
|
|
tb.KeyCtrlZ: "<C-z>",
|
|
|
|
tb.KeyEsc: "<Escape>", // tb.KeyCtrlLsqBracket tb.KeyCtrl3
|
|
|
|
tb.KeyCtrl4: "<C-4>", // tb.KeyCtrlBackslash
|
|
|
|
tb.KeyCtrl5: "<C-5>", // tb.KeyCtrlRsqBracket
|
|
|
|
tb.KeyCtrl6: "<C-6>",
|
|
|
|
tb.KeyCtrl7: "<C-7>", // tb.KeyCtrlSlash tb.KeyCtrlUnderscore
|
|
|
|
tb.KeySpace: "<Space>",
|
|
|
|
tb.KeyBackspace2: "<Backspace>", // tb.KeyCtrl8:
|
|
|
|
}
|
|
|
|
|
2018-09-06 23:36:14 +00:00
|
|
|
// convertTermboxKeyboardEvent converts a termbox keyboard event to a more friendly string format.
|
|
|
|
// Combines modifiers into the string instead of having them as additional fields in an event.
|
|
|
|
func convertTermboxKeyboardEvent(e tb.Event) Event {
|
2019-01-24 13:23:30 +00:00
|
|
|
ID := "%s"
|
2018-09-06 23:36:14 +00:00
|
|
|
if e.Mod == tb.ModAlt {
|
2019-01-24 13:23:30 +00:00
|
|
|
ID = "<M-%s>"
|
2015-09-18 15:41:44 +00:00
|
|
|
}
|
|
|
|
|
2019-01-24 13:23:30 +00:00
|
|
|
if e.Ch != 0 {
|
|
|
|
ID = fmt.Sprintf(ID, string(e.Ch))
|
|
|
|
} else {
|
2019-01-25 11:41:35 +00:00
|
|
|
converted, ok := keyboardMap[e.Key]
|
|
|
|
if !ok {
|
|
|
|
converted = ""
|
2015-09-18 15:41:44 +00:00
|
|
|
}
|
2019-01-25 11:41:35 +00:00
|
|
|
ID = fmt.Sprintf(ID, converted)
|
2015-09-18 15:41:44 +00:00
|
|
|
}
|
|
|
|
|
2018-09-06 23:36:14 +00:00
|
|
|
return Event{
|
|
|
|
Type: KeyboardEvent,
|
2019-01-24 13:23:30 +00:00
|
|
|
ID: ID,
|
2018-01-11 18:01:45 +00:00
|
|
|
}
|
2017-05-27 10:11:46 +00:00
|
|
|
}
|
|
|
|
|
2019-01-25 11:41:35 +00:00
|
|
|
var mouseButtonMap = map[tb.Key]string{
|
|
|
|
tb.MouseLeft: "<MouseLeft>",
|
|
|
|
tb.MouseMiddle: "<MouseMiddle>",
|
|
|
|
tb.MouseRight: "<MouseRight>",
|
|
|
|
tb.MouseRelease: "<MouseRelease>",
|
|
|
|
tb.MouseWheelUp: "<MouseWheelUp>",
|
|
|
|
tb.MouseWheelDown: "<MouseWheelDown>",
|
|
|
|
}
|
2015-09-18 15:41:44 +00:00
|
|
|
|
2019-01-25 11:41:35 +00:00
|
|
|
func convertTermboxMouseEvent(e tb.Event) Event {
|
2018-09-06 23:36:14 +00:00
|
|
|
converted, ok := mouseButtonMap[e.Key]
|
|
|
|
if !ok {
|
|
|
|
converted = "Unknown_Mouse_Button"
|
2015-09-18 15:41:44 +00:00
|
|
|
}
|
2019-01-25 11:41:35 +00:00
|
|
|
Drag := e.Mod == tb.ModMotion
|
2018-09-06 23:36:14 +00:00
|
|
|
return Event{
|
|
|
|
Type: MouseEvent,
|
|
|
|
ID: converted,
|
|
|
|
Payload: Mouse{
|
|
|
|
X: e.MouseX,
|
|
|
|
Y: e.MouseY,
|
|
|
|
Drag: Drag,
|
|
|
|
},
|
2015-09-18 15:41:44 +00:00
|
|
|
}
|
2015-10-07 18:25:59 +00:00
|
|
|
}
|
2017-01-17 08:11:16 +00:00
|
|
|
|
2018-09-06 23:36:14 +00:00
|
|
|
// convertTermboxEvent turns a termbox event into a termui event.
|
|
|
|
func convertTermboxEvent(e tb.Event) Event {
|
|
|
|
if e.Type == tb.EventError {
|
|
|
|
panic(e.Err)
|
2016-08-13 11:43:04 +00:00
|
|
|
}
|
2018-09-06 23:36:14 +00:00
|
|
|
switch e.Type {
|
|
|
|
case tb.EventKey:
|
2019-01-25 11:41:35 +00:00
|
|
|
return convertTermboxKeyboardEvent(e)
|
2018-09-06 23:36:14 +00:00
|
|
|
case tb.EventMouse:
|
2019-01-25 11:41:35 +00:00
|
|
|
return convertTermboxMouseEvent(e)
|
2018-09-06 23:36:14 +00:00
|
|
|
case tb.EventResize:
|
2019-01-25 11:41:35 +00:00
|
|
|
return Event{
|
2018-09-06 23:36:14 +00:00
|
|
|
Type: ResizeEvent,
|
|
|
|
ID: "<Resize>",
|
|
|
|
Payload: Resize{
|
|
|
|
Width: e.Width,
|
|
|
|
Height: e.Height,
|
|
|
|
},
|
2015-10-07 18:25:59 +00:00
|
|
|
}
|
2015-09-18 15:41:44 +00:00
|
|
|
}
|
2019-01-25 11:41:35 +00:00
|
|
|
return Event{}
|
2015-10-13 16:45:03 +00:00
|
|
|
}
|