1
0
mirror of https://github.com/gdamore/tcell.git synced 2025-04-24 13:48:51 +08:00

fixes #129 Very high IDLE_WAKE - Power consumption

fixes #164 KeyEscape does not work in Go 1.9 under Linux

This is a complete refactor of the input loop for UNIX systems.
We use a blocking reader on the TTY, and a separate select
loop for timers and other events.  This means that our idle
use should be low now.
This commit is contained in:
Garrett D'Amore 2017-09-23 23:25:24 -07:00
parent 0a0db94084
commit 50f9ed7673
4 changed files with 66 additions and 39 deletions

View File

@ -1,4 +1,4 @@
// Copyright 2016 The TCell Authors
// Copyright 2017 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
@ -20,6 +20,7 @@ import (
"os"
"strconv"
"sync"
"time"
"unicode/utf8"
"golang.org/x/text/transform"
@ -79,6 +80,9 @@ type tScreen struct {
indoneq chan struct{}
keyexist map[Key]bool
keycodes map[string]*tKeyCode
keychan chan []byte
keytimer *time.Timer
keyexpire time.Time
cx int
cy int
mouse []byte
@ -105,6 +109,8 @@ type tScreen struct {
func (t *tScreen) Init() error {
t.evch = make(chan Event, 10)
t.indoneq = make(chan struct{})
t.keychan = make(chan []byte, 10)
t.keytimer = time.NewTimer(time.Millisecond * 50)
t.charset = "UTF-8"
t.charset = getCharset()
@ -164,6 +170,7 @@ func (t *tScreen) Init() error {
t.resize()
t.Unlock()
go t.mainLoop()
go t.inputLoop()
return nil
@ -1234,10 +1241,8 @@ func (t *tScreen) scanInput(buf *bytes.Buffer, expire bool) {
}
}
func (t *tScreen) inputLoop() {
func (t *tScreen) mainLoop() {
buf := &bytes.Buffer{}
chunk := make([]byte, 128)
for {
select {
case <-t.quit:
@ -1252,26 +1257,56 @@ func (t *tScreen) inputLoop() {
t.draw()
t.Unlock()
continue
default:
case <-t.keytimer.C:
// If the timer fired, and the current time
// is after the expiration of the escape sequence,
// then we assume the escape sequence reached it's
// conclusion, and process the chunk independently.
// This lets us detect conflicts such as a lone ESC.
if buf.Len() > 0 {
if time.Now().After(t.keyexpire) {
t.scanInput(buf, true)
}
}
if buf.Len() > 0 {
if !t.keytimer.Stop() {
select {
case <-t.keytimer.C:
default:
}
}
t.keytimer.Reset(time.Millisecond * 50)
}
case chunk := <-t.keychan:
buf.Write(chunk)
t.keyexpire = time.Now().Add(time.Millisecond * 50)
t.scanInput(buf, false)
if !t.keytimer.Stop() {
select {
case <-t.keytimer.C:
default:
}
}
if buf.Len() > 0 {
t.keytimer.Reset(time.Millisecond * 50)
}
}
}
}
func (t *tScreen) inputLoop() {
chunk := make([]byte, 128)
for {
n, e := t.in.Read(chunk)
switch e {
case io.EOF:
// If we timeout waiting for more bytes, then it's
// time to give up on it. Even at 300 baud it takes
// less than 0.5 ms to transmit a whole byte.
if buf.Len() > 0 {
t.scanInput(buf, true)
}
continue
case nil:
default:
close(t.indoneq)
return
}
buf.Write(chunk[:n])
// Now we need to parse the input buffer for events
t.scanInput(buf, false)
t.keychan <- chunk[:n]
}
}

View File

@ -1,6 +1,6 @@
// +build darwin freebsd netbsd openbsd dragonfly
// Copyright 2015 The TCell Authors
// Copyright 2017 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
@ -61,13 +61,6 @@ func (t *tScreen) termioInit() error {
newtios.Cflag &^= syscall.CSIZE | syscall.PARENB
newtios.Cflag |= syscall.CS8
// We wake up at the earliest of 100 msec or when data is received.
// We need to wake up frequently to permit us to exit cleanly and
// close file descriptors on systems like Darwin, where close does
// cause a wakeup. (Probably we could reasonably increase this to
// something like 1 sec or 500 msec.)
newtios.Cc[syscall.VMIN] = 0
newtios.Cc[syscall.VTIME] = 1
tios = uintptr(unsafe.Pointer(&newtios))
ioc = uintptr(syscall.TIOCSETA)

View File

@ -1,6 +1,6 @@
// +build linux
// Copyright 2015 The TCell Authors
// Copyright 2017 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
@ -61,13 +61,13 @@ func (t *tScreen) termioInit() error {
newtios.Cflag &^= syscall.CSIZE | syscall.PARENB
newtios.Cflag |= syscall.CS8
// We wake up at the earliest of 100 msec or when data is received.
// We need to wake up frequently to permit us to exit cleanly and
// close file descriptors on systems like Darwin, where close does
// cause a wakeup. (Probably we could reasonably increase this to
// something like 1 sec or 500 msec.)
newtios.Cc[syscall.VMIN] = 0
newtios.Cc[syscall.VTIME] = 1
// This is setup for blocking reads. In the past we attempted to
// use non-blocking reads, but now a separate input loop and timer
// copes with the problems we had on some systems (BSD/Darwin)
// where close hung forever.
newtios.Cc[syscall.VMIN] = 1
newtios.Cc[syscall.VTIME] = 0
tios = uintptr(unsafe.Pointer(&newtios))
// Well this kind of sucks, because we don't have TCSETSF, but only

View File

@ -1,6 +1,6 @@
// +build solaris
// Copyright 2015 The TCell Authors
// Copyright 2017 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
@ -152,13 +152,12 @@ func (t *tScreen) termioInit() error {
newtios.c_cflag &^= C.CSIZE | C.PARENB
newtios.c_cflag |= C.CS8
// We wake up at the earliest of 100 msec or when data is received.
// We need to wake up frequently to permit us to exit cleanly and
// close file descriptors on systems like Darwin, where close does
// cause a wakeup. (Probably we could reasonably increase this to
// something like 1 sec or 500 msec.)
newtios.c_cc[C.VMIN] = 0
newtios.c_cc[C.VTIME] = 1
// This is setup for blocking reads. In the past we attempted to
// use non-blocking reads, but now a separate input loop and timer
// copes with the problems we had on some systems (BSD/Darwin)
// where close hung forever.
newtios.Cc[syscall.VMIN] = 1
newtios.Cc[syscall.VTIME] = 0
if rv, e = C.tcsetattr(fd, C.TCSANOW|C.TCSAFLUSH, &newtios); rv != 0 {
goto failed