mirror of https://github.com/EasyDarwin/EasyDarwin
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
214 lines
4.8 KiB
214 lines
4.8 KiB
// +build !windows |
|
|
|
package keyboard |
|
|
|
import ( |
|
"os" |
|
"os/signal" |
|
"syscall" |
|
"unicode/utf8" |
|
"strings" |
|
"fmt" |
|
"runtime" |
|
"unsafe" |
|
|
|
"golang.org/x/sys/unix" |
|
) |
|
|
|
type ( |
|
input_event struct { |
|
data []byte |
|
err error |
|
} |
|
) |
|
|
|
var ( |
|
out *os.File |
|
in int |
|
|
|
// term specific keys |
|
keys []string |
|
|
|
// termbox inner state |
|
orig_tios unix.Termios |
|
|
|
sigio = make(chan os.Signal, 1) |
|
quit = make(chan int) |
|
inbuf = make([]byte, 0, 128) |
|
input_buf = make(chan input_event) |
|
) |
|
|
|
func fcntl(cmd int, arg int) error { |
|
_, _, e := syscall.Syscall(unix.SYS_FCNTL, uintptr(in), uintptr(cmd), uintptr(arg)) |
|
if e != 0 { |
|
return e |
|
} |
|
|
|
return nil |
|
} |
|
|
|
func ioctl(cmd int, termios *unix.Termios) error { |
|
r, _, e := syscall.Syscall(unix.SYS_IOCTL, out.Fd(), uintptr(cmd), uintptr(unsafe.Pointer(termios))) |
|
if r != 0 { |
|
return os.NewSyscallError("SYS_IOCTL", e) |
|
} |
|
return nil |
|
} |
|
|
|
func parse_escape_sequence(buf []byte) (size int, event keyEvent) { |
|
bufstr := string(buf) |
|
for i, key := range keys { |
|
if strings.HasPrefix(bufstr, key) { |
|
event.rune = 0 |
|
event.key = Key(0xFFFF - i) |
|
size = len(key) |
|
return |
|
} |
|
} |
|
return 0, event |
|
} |
|
|
|
func extract_event(inbuf []byte) int { |
|
if len(inbuf) == 0 { |
|
return 0 |
|
} |
|
|
|
if inbuf[0] == '\033' { |
|
// possible escape sequence |
|
if size, event := parse_escape_sequence(inbuf); size != 0 { |
|
input_comm <- event |
|
return size |
|
} |
|
|
|
// it's not a recognized escape sequence, then return Esc |
|
input_comm <- keyEvent{key: KeyEsc} |
|
return len(inbuf) |
|
} |
|
|
|
// if we're here, this is not an escape sequence and not an alt sequence |
|
// so, it's a FUNCTIONAL KEY or a UNICODE character |
|
|
|
// first of all check if it's a functional key |
|
if Key(inbuf[0]) <= KeySpace || Key(inbuf[0]) == KeyBackspace2 { |
|
input_comm <- keyEvent{key: Key(inbuf[0])} |
|
return 1 |
|
} |
|
|
|
// the only possible option is utf8 rune |
|
if r, n := utf8.DecodeRune(inbuf); r != utf8.RuneError { |
|
input_comm <- keyEvent{rune: r} |
|
return n |
|
} |
|
|
|
return 0 |
|
} |
|
|
|
// Wait for an event and return it. This is a blocking function call. |
|
func inputEventsProducer() { |
|
// try to extract event from input buffer, return on success |
|
size := extract_event(inbuf) |
|
if size != 0 { |
|
copy(inbuf, inbuf[size:]) |
|
inbuf = inbuf[:len(inbuf)-size] |
|
} |
|
|
|
for { |
|
select { |
|
case ev := <-input_buf: |
|
if ev.err != nil { |
|
input_comm <- keyEvent{err: ev.err} |
|
return |
|
} |
|
|
|
inbuf = append(inbuf, ev.data...) |
|
size = extract_event(inbuf) |
|
if size != 0 { |
|
copy(inbuf, inbuf[size:]) |
|
inbuf = inbuf[:len(inbuf)-size] |
|
} |
|
case <-quit: |
|
return |
|
} |
|
} |
|
} |
|
|
|
func initConsole() (err error) { |
|
out, err = os.OpenFile("/dev/tty", unix.O_WRONLY, 0) |
|
if err != nil { |
|
return |
|
} |
|
in, err = syscall.Open("/dev/tty", unix.O_RDONLY, 0) |
|
if err != nil { |
|
return |
|
} |
|
|
|
err = setup_term() |
|
if err != nil { |
|
return fmt.Errorf("Error while reading terminfo data: %v", err) |
|
} |
|
|
|
signal.Notify(sigio, unix.SIGIO) |
|
|
|
err = fcntl(unix.F_SETFL, unix.O_ASYNC|unix.O_NONBLOCK) |
|
if err != nil { |
|
return |
|
} |
|
err = fcntl(unix.F_SETOWN, unix.Getpid()) |
|
if runtime.GOOS != "darwin" && err != nil { |
|
return |
|
} |
|
|
|
err = ioctl(ioctl_GETATTR, &orig_tios) |
|
if err != nil { |
|
return |
|
} |
|
|
|
tios := orig_tios |
|
tios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | |
|
unix.ISTRIP | unix.INLCR | unix.IGNCR | |
|
unix.ICRNL | unix.IXON |
|
tios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | |
|
unix.ISIG | unix.IEXTEN |
|
tios.Cflag &^= unix.CSIZE | unix.PARENB |
|
tios.Cflag |= unix.CS8 |
|
tios.Cc[unix.VMIN] = 1 |
|
tios.Cc[unix.VTIME] = 0 |
|
|
|
err = ioctl(ioctl_SETATTR, &tios) |
|
if err != nil { |
|
return err |
|
} |
|
|
|
go func() { |
|
buf := make([]byte, 128) |
|
for { |
|
select { |
|
case <-sigio: |
|
for { |
|
n, err := syscall.Read(in, buf) |
|
if err == unix.EAGAIN || err == unix.EWOULDBLOCK { |
|
break |
|
} |
|
select { |
|
case input_buf <- input_event{buf[:n], err}: |
|
continue |
|
case <-quit: |
|
return |
|
} |
|
} |
|
case <-quit: |
|
return |
|
} |
|
} |
|
}() |
|
|
|
go inputEventsProducer() |
|
return |
|
} |
|
|
|
func releaseConsole() { |
|
quit <- 1 |
|
ioctl(ioctl_SETATTR, &orig_tios) |
|
out.Close() |
|
unix.Close(in) |
|
}
|
|
|