mirror of
https://github.com/gdamore/tcell.git
synced 2025-04-24 13:48:51 +08:00
fixes #10 Eliminate separate buffered implementation
fixes #9 Various API enhancements fixes #8 Mouse demo improvements fixes #7 Add support for handling $<delay> delays fixes #6 mkinfo doesn't honor terminfo LINES and COLUMNS fixes #5 Add better mouse tracking fixes #3 Windows performance improvements
This commit is contained in:
parent
385072b4a8
commit
434fc57169
90
README.md
90
README.md
@ -8,28 +8,62 @@
|
||||
[](https://godoc.org/github.com/gdamore/tcell)
|
||||
|
||||
> _Tcell is a work in progress (Beta).
|
||||
> Please use with caution; interfaces may change in before final release._
|
||||
> Please use with caution; interfaces may change in before final release.
|
||||
> That said, our confidence in Tcell's stability is increasing. If you
|
||||
> would like to use it in your own application, it is recommended that
|
||||
> you drop a message to garrett@damore.org before commitment._
|
||||
|
||||
Package tcell provides a cell based view for text terminals, like xterm.
|
||||
It was inspired by termbox, but differs from termbox in some important
|
||||
ways.
|
||||
ways. It also adds signficant functionality beyond termbox.
|
||||
|
||||
## Pure Go Terminfo Database
|
||||
|
||||
First, it includes a full parser and expander for terminfo capability strings,
|
||||
so that it can avoid hard coding escape strings for formatting. It also favors
|
||||
portability, and includes support for all POSIX systems, at the slight expense
|
||||
of needing cgo support for terminal initializations. (This will be corrected
|
||||
of needing cgo support for terminal initializations. (This may be corrected
|
||||
when Go provides standard support for terminal handling via termio ioctls on
|
||||
all POSIX platforms.)
|
||||
all POSIX platforms.) The database itself, while built using CGO, as well
|
||||
as the parser for it, is implemented in Pure Go.
|
||||
|
||||
Also, this code is able to operate without requiring
|
||||
The database is also flexible & extensibel, and can modified by either running a
|
||||
program to build the database, or hand editing of simple JSON files.
|
||||
|
||||
## More Portable
|
||||
|
||||
Tcell is portable to a wider variety of systems. It relies on standard
|
||||
POSIX supported function calls (on POSIX platforms) for setting terminal
|
||||
modes, which leads to improved support for a broader array of platforms.
|
||||
This does come at the cost of requiring your code to be able to use CGO, but
|
||||
we believe that the vastly improved portability justifies this
|
||||
requirement. Note that the functions called are part of the standard C
|
||||
library, so there shouldn't be any additional external requirements beyond
|
||||
that required for every POSIX program.
|
||||
|
||||
## No async IO
|
||||
|
||||
Termbox code is able to operate without requiring
|
||||
SIGIO signals, or asynchronous I/O, and can instead use standard Go file
|
||||
objects and Go routines.
|
||||
objects and Go routines. This means it should be safe, especially for
|
||||
use with programs that use exec, or otherwise need to manipulate the
|
||||
tty streams. This model is also much closer to idiomatic Go, leading
|
||||
to fewer surprises.
|
||||
|
||||
It also includes enhanced support for Unicode, include wide characters and
|
||||
combining characters. It also has richer support for a larger number of
|
||||
## Richer Unicode support
|
||||
|
||||
Tcell includes enhanced support for Unicode, include wide characters and
|
||||
combining characters, provided your terminal can support them. Note that
|
||||
Windows terminals generally don't support the full Unicode repertoire.
|
||||
|
||||
## More Function Keys
|
||||
|
||||
It also has richer support for a larger number of
|
||||
special keys that some terminals can send.
|
||||
|
||||
It will respect your terminal's color space as specified within your terminfo
|
||||
## Better color handling
|
||||
|
||||
Tcell will respect your terminal's color space as specified within your terminfo
|
||||
entries, so that for example attempts to emit color sequences on VT100 terminals
|
||||
won't result in unintended consequences.
|
||||
|
||||
@ -37,6 +71,13 @@ In Windows mode, we support 16 colors, underline, bold, dim, and reverse,
|
||||
instead of just termbox's 8 colors with reverse. (Note that there is some
|
||||
conflation with bold/dim and colors.)
|
||||
|
||||
## Better mouse support
|
||||
|
||||
It supports enhanced mouse tracking mode, so your application can receive
|
||||
regular mouse motion events, and wheel events, if your terminal supports it.
|
||||
|
||||
## Why not just patch termbox-go?
|
||||
|
||||
I started this project originally by submitting patches to the author of
|
||||
go-termbox, but due to some fundamental differences of opinion, I thought
|
||||
it might be simpler just to start from scratch.
|
||||
@ -124,12 +165,17 @@ is not possible as terminfo sequences are not defined.)
|
||||
|
||||
On Windows, the mouse works normally.
|
||||
|
||||
Mouse wheels are unlikely to work, and I have made no effort to support them,
|
||||
given the lack of portability across emulation packages.
|
||||
Mouse wheel buttons on various terminals are known to work, but the support
|
||||
in terminal emulators, as well as support for various buttons and
|
||||
live mouse tracking, varies widely. As a particular datum, MacOS X Terminal
|
||||
does not support Mouse events at all (as of MacOS 10.10, aka Yosemite.) The
|
||||
excellent iTerm application is fully supported, as is vanilla XTerm.
|
||||
|
||||
Only button press and release events are reported at this time. In the
|
||||
future we can easily add full tracking for Windows, and for "genuine" XTerm,
|
||||
but most alternatives don't have support for the full mouse motion events.
|
||||
Mouse tracking with live tracking also varies widely. Current XTerm
|
||||
implementations, as well as current Screen and iTerm2, and Windows
|
||||
consoles, all support this quite nicely. On other platforms you might
|
||||
find that only mouse click and release events are reported, with
|
||||
no intervening motion events. It really depends on your terminal.
|
||||
|
||||
## Platforms
|
||||
|
||||
@ -138,12 +184,16 @@ It also requires functional cgo to run. As of this writing, Cgo is available
|
||||
on all POSIX Go 1.5 platforms.
|
||||
|
||||
Windows console mode applications are supported. Unfortunately mintty
|
||||
and other cygwin style applications are not supported; modern console
|
||||
applications like ConEmu support all the good features (resize, mouse, etc.)
|
||||
The Windows version is a bit inefficient in its drawing, as it performs
|
||||
a single WriteConsoleOutput call for each on screen cell that is changed,
|
||||
but experimentally I wasn't able to notice the inefficiency.
|
||||
and other cygwin style applications are not supported.
|
||||
|
||||
Modern console applications like ConEmu support all the good features
|
||||
(resize, mouse tracking, etc.)
|
||||
|
||||
I haven't figured out how to cleanly resolve the dichotomy between cygwin
|
||||
style termios and the Windows Console API; it seems that perhaps nobody else
|
||||
has either. If anyone has suggestions, let me know!
|
||||
has either. If anyone has suggestions, let me know! Really, if you're
|
||||
using a Windows application, you should use the native Windows console or a
|
||||
fully compatible consule implementation. We expect that Windows 10
|
||||
ships with a less crippled implementation than prior releases -- we haven't
|
||||
tested that, lacking Windows 10 ourselves.
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"time"
|
||||
@ -24,44 +25,62 @@ import (
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
func makebox(s tcell.BufferedScreen) {
|
||||
func makebox(s tcell.Screen) {
|
||||
w, h := s.Size()
|
||||
|
||||
if w == 0 || h == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
glyphs := []rune { '@', '#', '&', '*', '=', '%', 'Z', 'A' }
|
||||
|
||||
lx := rand.Int() % w
|
||||
ly := rand.Int() % h
|
||||
lw := rand.Int() % (w-lx)
|
||||
lh := rand.Int() % (h-ly)
|
||||
st := tcell.StyleDefault.Background(tcell.Color(rand.Int() % s.Colors()))
|
||||
lw := rand.Int() % (w - lx)
|
||||
lh := rand.Int() % (h - ly)
|
||||
st := tcell.StyleDefault
|
||||
gl := ' '
|
||||
if s.Colors() > 1 {
|
||||
st = st.Background(tcell.Color(rand.Int() % s.Colors()))
|
||||
} else {
|
||||
st = st.Reverse(rand.Int() % 2 == 0)
|
||||
gl = glyphs[rand.Int() % len(glyphs)]
|
||||
}
|
||||
|
||||
for row := 0; row < lh; row++ {
|
||||
for col := 0; col < lw; col++ {
|
||||
s.SetCell(lx + col, ly + row, st, ' ')
|
||||
s.SetCell(lx+col, ly+row, st, gl)
|
||||
}
|
||||
}
|
||||
s.Show()
|
||||
}
|
||||
|
||||
func main() {
|
||||
s, e := tcell.NewBufferedScreen()
|
||||
s, e := tcell.NewScreen()
|
||||
if e != nil {
|
||||
panic(e.Error())
|
||||
fmt.Fprintf(os.Stderr, "%v\n", e)
|
||||
os.Exit(1)
|
||||
}
|
||||
if e = s.Init(); e != nil {
|
||||
panic(e.Error())
|
||||
fmt.Fprintf(os.Stderr, "%v\n", e)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
s.SetStyle(tcell.StyleDefault.
|
||||
Foreground(tcell.ColorBlack).
|
||||
Background(tcell.ColorWhite))
|
||||
s.Clear()
|
||||
|
||||
quit := make(chan struct{})
|
||||
go func() {
|
||||
for {
|
||||
ev := s.PollEvent()
|
||||
switch ev := ev.(type) {
|
||||
case *tcell.EventKey:
|
||||
switch ev.Key() {
|
||||
case tcell.KeyEscape:
|
||||
s.Fini()
|
||||
os.Exit(0)
|
||||
case tcell.KeyEscape, tcell.KeyEnter:
|
||||
close(quit)
|
||||
return
|
||||
case tcell.KeyCtrlL:
|
||||
s.Sync()
|
||||
}
|
||||
@ -71,8 +90,22 @@ func main() {
|
||||
}
|
||||
}()
|
||||
|
||||
cnt := 0
|
||||
dur := time.Duration(0)
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case <-quit:
|
||||
break loop
|
||||
case <-time.After(time.Millisecond * 50):
|
||||
}
|
||||
start := time.Now()
|
||||
makebox(s)
|
||||
time.Sleep(time.Millisecond*10)
|
||||
cnt++
|
||||
dur += time.Now().Sub(start)
|
||||
}
|
||||
|
||||
s.Fini()
|
||||
fmt.Printf("Finished %d boxes in %s\n", cnt, dur)
|
||||
fmt.Printf("Average is %0.3f ms / box\n", (float64(dur)/float64(cnt))/1000000.0)
|
||||
}
|
||||
|
228
_demos/mouse.go
228
_demos/mouse.go
@ -25,60 +25,186 @@ import (
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
// This program just shows simple mouse and keyboard events. Press ESC to
|
||||
// exit.
|
||||
func main() {
|
||||
s, e := tcell.NewBufferedScreen()
|
||||
if e != nil {
|
||||
fmt.Printf("oops: %v", e)
|
||||
}
|
||||
s.Init()
|
||||
s.EnableMouse()
|
||||
s.Clear()
|
||||
var defStyle tcell.Style
|
||||
|
||||
i := 1
|
||||
for _, c := range "Press ESC to exit." {
|
||||
s.SetCell(i, 1, tcell.StyleDefault, c)
|
||||
i++
|
||||
func emitStr(s tcell.Screen, x, y int, style tcell.Style, str string) {
|
||||
for _, c := range str {
|
||||
s.SetCell(x, y, style, c)
|
||||
x++
|
||||
}
|
||||
}
|
||||
|
||||
func drawBox(s tcell.Screen, x1, y1, x2, y2 int, style tcell.Style, r rune) {
|
||||
if y2 < y1 {
|
||||
y1, y2 = y2, y1
|
||||
}
|
||||
if x2 < x1 {
|
||||
x1, x2 = x2, x1
|
||||
}
|
||||
|
||||
for {
|
||||
s.Show()
|
||||
ev := s.PollEvent()
|
||||
st := tcell.StyleDefault.Background(tcell.ColorBrightRed)
|
||||
up := tcell.StyleDefault.Background(tcell.ColorBlue)
|
||||
switch ev := ev.(type) {
|
||||
case *tcell.EventResize:
|
||||
s.Sync()
|
||||
x, y := ev.Size()
|
||||
s.SetCell(x-1, y-1, st, 'R')
|
||||
case *tcell.EventKey:
|
||||
x, y := s.Size()
|
||||
s.SetCell(x-2, y-2, st, ev.Rune())
|
||||
s.SetCell(x-1, y-1, st, 'K')
|
||||
if ev.Key() == tcell.KeyEscape {
|
||||
s.Fini()
|
||||
os.Exit(0)
|
||||
}
|
||||
case *tcell.EventMouse:
|
||||
x, y := ev.Position()
|
||||
switch ev.Buttons() {
|
||||
case tcell.ButtonNone:
|
||||
s.SetCell(x, y, up, '-')
|
||||
case tcell.Button1:
|
||||
s.SetCell(x, y, st, '1')
|
||||
case tcell.Button2:
|
||||
s.SetCell(x, y, st, '2')
|
||||
case tcell.Button3:
|
||||
s.SetCell(x, y, st, '3')
|
||||
default:
|
||||
s.SetCell(x, y, st, '*')
|
||||
}
|
||||
x, y = s.Size()
|
||||
s.SetCell(x-1, y-1, st, 'M')
|
||||
default:
|
||||
x, y := s.Size()
|
||||
s.SetCell(x-1, y-1, st, 'X')
|
||||
for row := y1; row <= y2; row++ {
|
||||
for col := x1; col <= x2; col++ {
|
||||
s.SetCell(col, row, style, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func drawSelect(s tcell.Screen, x1, y1, x2, y2 int, sel bool) {
|
||||
|
||||
if y2 < y1 {
|
||||
y1, y2 = y2, y1
|
||||
}
|
||||
if x2 < x1 {
|
||||
x1, x2 = x2, x1
|
||||
}
|
||||
for row := y1; row <= y2; row++ {
|
||||
for col := x1; col <= x2; col++ {
|
||||
if cp := s.GetCell(col, row); cp != nil {
|
||||
st := cp.Style
|
||||
if st == tcell.StyleDefault {
|
||||
st = defStyle
|
||||
}
|
||||
st = st.Reverse(sel)
|
||||
cp.Style = st
|
||||
s.PutCell(col, row, cp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This program just shows simple mouse and keyboard events. Press ESC to
|
||||
// exit.
|
||||
func main() {
|
||||
s, e := tcell.NewScreen()
|
||||
if e != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", e)
|
||||
os.Exit(1)
|
||||
}
|
||||
if e := s.Init(); e != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", e)
|
||||
os.Exit(1)
|
||||
}
|
||||
defStyle = tcell.StyleDefault.
|
||||
Background(tcell.ColorBlack).
|
||||
Foreground(tcell.ColorWhite)
|
||||
s.SetStyle(defStyle)
|
||||
s.EnableMouse()
|
||||
s.Clear()
|
||||
|
||||
posfmt := "Mouse: %d, %d "
|
||||
btnfmt := "Buttons: %-20s"
|
||||
white := tcell.StyleDefault.
|
||||
Foreground(tcell.ColorBrightWhite).Background(tcell.ColorRed)
|
||||
|
||||
mx, my := -1, -1
|
||||
ox, oy := -1, -1
|
||||
bx, by := -1, -1
|
||||
w, h := s.Size()
|
||||
lchar := '*'
|
||||
bstr := ""
|
||||
|
||||
for {
|
||||
drawBox(s, 1, 1, 31, 3, white, ' ')
|
||||
emitStr(s, 1, 1, white, "Press ESC to exit, C to clear.")
|
||||
emitStr(s, 1, 2, white, fmt.Sprintf(posfmt, mx, my))
|
||||
emitStr(s, 1, 3, white, fmt.Sprintf(btnfmt, bstr))
|
||||
|
||||
s.Show()
|
||||
bstr = ""
|
||||
ev := s.PollEvent()
|
||||
st := tcell.StyleDefault.Background(tcell.ColorBrightRed)
|
||||
up := tcell.StyleDefault.
|
||||
Background(tcell.ColorBrightBlue).
|
||||
Foreground(tcell.ColorBrightGreen)
|
||||
w, h = s.Size()
|
||||
|
||||
// always clear any old selection box
|
||||
if ox >= 0 && oy >= 0 && bx >= 0 {
|
||||
drawSelect(s, ox, oy, bx, by, false)
|
||||
}
|
||||
|
||||
switch ev := ev.(type) {
|
||||
case *tcell.EventResize:
|
||||
s.Sync()
|
||||
s.SetCell(w-1, h-1, st, 'R')
|
||||
case *tcell.EventKey:
|
||||
s.SetCell(w-2, h-2, st, ev.Rune())
|
||||
s.SetCell(w-1, h-1, st, 'K')
|
||||
if ev.Key() == tcell.KeyEscape {
|
||||
s.Fini()
|
||||
os.Exit(0)
|
||||
} else if ev.Rune() == 'C' || ev.Rune() == 'c' {
|
||||
s.Clear()
|
||||
}
|
||||
case *tcell.EventMouse:
|
||||
x, y := ev.Position()
|
||||
button := ev.Buttons()
|
||||
for i := uint(0); i < 8; i++ {
|
||||
if int(button) & (1 << i) != 0 {
|
||||
bstr += fmt.Sprintf(" Button%d", i+1)
|
||||
}
|
||||
}
|
||||
if button & tcell.WheelUp != 0 {
|
||||
bstr += " WheelUp"
|
||||
}
|
||||
if button & tcell.WheelDown != 0 {
|
||||
bstr += " WheelDown"
|
||||
}
|
||||
if button & tcell.WheelLeft != 0 {
|
||||
bstr += " WheelLeft"
|
||||
}
|
||||
if button & tcell.WheelRight != 0 {
|
||||
bstr += " WheelRight"
|
||||
}
|
||||
// Only buttons, not wheel events
|
||||
button &= tcell.ButtonMask(0xff)
|
||||
ch := '*'
|
||||
|
||||
if button != tcell.ButtonNone && ox < 0 {
|
||||
ox, oy = x, y
|
||||
}
|
||||
switch ev.Buttons() {
|
||||
case tcell.ButtonNone:
|
||||
if ox >= 0 {
|
||||
bg := tcell.Color((lchar - '0')*2+1)
|
||||
drawBox(s, ox, oy, x, y,
|
||||
up.Background(bg),
|
||||
lchar)
|
||||
ox, oy = -1, -1
|
||||
bx, by = -1, -1
|
||||
}
|
||||
case tcell.Button1:
|
||||
ch = '1'
|
||||
case tcell.Button2:
|
||||
ch = '2'
|
||||
case tcell.Button3:
|
||||
ch = '3'
|
||||
case tcell.Button4:
|
||||
ch = '4'
|
||||
case tcell.Button5:
|
||||
ch = '5'
|
||||
case tcell.Button6:
|
||||
ch = '6'
|
||||
case tcell.Button7:
|
||||
ch = '7'
|
||||
case tcell.Button8:
|
||||
ch = '8'
|
||||
default:
|
||||
ch = '*'
|
||||
|
||||
}
|
||||
if button != tcell.ButtonNone {
|
||||
bx, by = x, y
|
||||
}
|
||||
lchar = ch
|
||||
s.SetCell(w-1, h-1, st, 'M')
|
||||
mx, my = x, y
|
||||
default:
|
||||
s.SetCell(w-1, h-1, st, 'X')
|
||||
}
|
||||
|
||||
if ox >= 0 && bx >= 0 {
|
||||
drawSelect(s, ox, oy, bx, by, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
316
buffered.go
316
buffered.go
@ -1,316 +0,0 @@
|
||||
// Copyright 2015 The TCell Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use file except in compliance with the License.
|
||||
// You may obtain a copy of the license at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tcell
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/mattn/go-runewidth"
|
||||
)
|
||||
|
||||
// BufferedScreen is like the base screen, but is buffered. Each screen
|
||||
// represents the root window that applications interface with.
|
||||
type BufferedScreen interface {
|
||||
// Init initializes the screen for use.
|
||||
Init() error
|
||||
|
||||
// Fini finazlizes the screen also releasing resources.
|
||||
Fini()
|
||||
|
||||
// Clear erases the screen.
|
||||
Clear()
|
||||
|
||||
// SetCell sets the cell at the given location.
|
||||
// The ch array contains at most one rune of width > 0, and the
|
||||
// runes with zero width (combining marks) must follow the first
|
||||
// non-zero width character. (If only combining marks are present,
|
||||
// a space character will be filled in.)
|
||||
//
|
||||
// Note that double wide runes occupy two cells, and attempts to
|
||||
// place a character at the immediately adjacent cell will have
|
||||
// undefined effects. Double wide runes that are printed in the
|
||||
// last column will be replaced with a single width space on output.
|
||||
SetCell(x int, y int, style Style, ch ...rune)
|
||||
|
||||
// ShowCursor is used to display the cursor at a given location.
|
||||
ShowCursor(x int, y int)
|
||||
|
||||
// HideCursor is used to hide the cursor.
|
||||
HideCursor()
|
||||
|
||||
// Size returns the screen size as width, height. This changes in
|
||||
// response to a call to Clear or Flush.
|
||||
Size() (int, int)
|
||||
|
||||
// PollEvent waits for events to arrive. Main application loops
|
||||
// can generally spin on this.
|
||||
PollEvent() Event
|
||||
|
||||
// PostEvent posts an event into the event stream.
|
||||
PostEvent(Event)
|
||||
|
||||
// Colors returns the number of colors. All colors are assumed to
|
||||
// use the ANSI color map.
|
||||
Colors() int
|
||||
|
||||
// EnableMouse enables mouse events, if your terminal has support
|
||||
// for them.
|
||||
EnableMouse()
|
||||
|
||||
// DisableMouse disables mouse events.
|
||||
DisableMouse()
|
||||
|
||||
// Sync synchronizes the buffered content with the screen, without
|
||||
// making any assumptions about the content that is displayed.
|
||||
// This is most often useful when some other program has altered the
|
||||
// screen state. Because this is a full redraw, it can be visually
|
||||
// jarring & expensive, and should only be done when truly needed.
|
||||
Sync()
|
||||
|
||||
// Show writes the contents of the buffer to the physical screen.
|
||||
// Only the contents that have changed will be written. This is what
|
||||
// applications call to redraw the screen.
|
||||
Show()
|
||||
}
|
||||
|
||||
type cell struct {
|
||||
ch []rune
|
||||
dirty bool
|
||||
width uint8
|
||||
style Style
|
||||
}
|
||||
|
||||
type bScreen struct {
|
||||
s Screen
|
||||
cells []cell
|
||||
w int
|
||||
h int
|
||||
cursorx int
|
||||
cursory int
|
||||
clear bool
|
||||
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func (b *bScreen) Init() error {
|
||||
if e := b.s.Init(); e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
// Allocate cells
|
||||
b.w, b.h = b.s.Size()
|
||||
b.cells = make([]cell, b.w*b.h)
|
||||
b.cursorx = -1
|
||||
b.cursory = -1
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *bScreen) Fini() {
|
||||
b.Lock()
|
||||
b.cells = nil
|
||||
b.w = 0
|
||||
b.h = 0
|
||||
b.clear = false
|
||||
b.cursorx = -1
|
||||
b.cursory = -1
|
||||
b.Unlock()
|
||||
b.s.Fini()
|
||||
}
|
||||
|
||||
func (b *bScreen) Colors() int {
|
||||
return b.s.Colors()
|
||||
}
|
||||
|
||||
func (b *bScreen) Size() (int, int) {
|
||||
b.Lock()
|
||||
w, h := b.w, b.h
|
||||
b.Unlock()
|
||||
return w, h
|
||||
}
|
||||
|
||||
func (b *bScreen) PollEvent() Event {
|
||||
ev := b.s.PollEvent()
|
||||
|
||||
// We need to capture resize events from the bottom screen, so that
|
||||
// we can readjust our buffer. This is important since the we need to
|
||||
// do any adjustment before the application starts drawing in response,
|
||||
// or coordinates may be erroneously believed out of range and results
|
||||
// discarded.
|
||||
if _, ok := ev.(*EventResize); ok {
|
||||
b.Lock()
|
||||
b.checkResize()
|
||||
b.Unlock()
|
||||
}
|
||||
return ev
|
||||
}
|
||||
|
||||
func (b *bScreen) PostEvent(ev Event) {
|
||||
// See comment in PollEvent for why we do this. Note that normally
|
||||
// events are posted directly into the screen below, so these are only
|
||||
// application supplied events.
|
||||
if _, ok := ev.(*EventResize); ok {
|
||||
b.Lock()
|
||||
b.checkResize()
|
||||
b.Unlock()
|
||||
}
|
||||
b.s.PostEvent(ev)
|
||||
}
|
||||
|
||||
func (b *bScreen) Clear() {
|
||||
b.Lock()
|
||||
for i := range b.cells {
|
||||
b.cells[i].dirty = true
|
||||
b.cells[i].style = StyleDefault
|
||||
b.cells[i].ch = nil
|
||||
}
|
||||
b.Unlock()
|
||||
}
|
||||
|
||||
func (b *bScreen) HideCursor() {
|
||||
b.Lock()
|
||||
b.cursorx = -1
|
||||
b.cursory = -1
|
||||
b.Unlock()
|
||||
}
|
||||
|
||||
func (b *bScreen) ShowCursor(x, y int) {
|
||||
b.Lock()
|
||||
b.cursorx = x
|
||||
b.cursory = y
|
||||
b.Unlock()
|
||||
}
|
||||
|
||||
func (b *bScreen) checkResize() {
|
||||
// Must be called with lock held!
|
||||
w, h := b.s.Size()
|
||||
if w == b.w && h == b.h {
|
||||
return
|
||||
}
|
||||
// We could reuse the cells, if we knew that both the row size did not
|
||||
// increase, and the total size did not increase. For now we just
|
||||
// take the lazy approach and grow a new cells structure. It would be
|
||||
// bad if the window size changes very frequently, but that shouldn't
|
||||
// happen.
|
||||
newc := make([]cell, w*h)
|
||||
for row := 0; row < h && row < b.h; row++ {
|
||||
for col := 0; col < w && col < b.w; col++ {
|
||||
newc[(row*w)+col] = b.cells[(row*b.w)+col]
|
||||
newc[(row*w)+col].dirty = true
|
||||
}
|
||||
}
|
||||
b.w = w
|
||||
b.h = h
|
||||
b.cells = newc
|
||||
// force a full screen redraw - just to be sure
|
||||
}
|
||||
|
||||
func (b *bScreen) SetCell(x int, y int, style Style, ch ...rune) {
|
||||
// compare ch, compare style
|
||||
b.Lock()
|
||||
if x < 0 || y < 0 || x >= b.w || y >= b.h {
|
||||
b.Unlock()
|
||||
return
|
||||
}
|
||||
cell := &b.cells[(y*b.w)+x]
|
||||
|
||||
// check to see if its the same value, if it is, don't mark it dirty
|
||||
match := false
|
||||
if len(ch) == len(cell.ch) && style == cell.style {
|
||||
match = true
|
||||
for i, r := range cell.ch {
|
||||
if ch[i] != r {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !match {
|
||||
cell.dirty = true
|
||||
cell.ch = ch
|
||||
cell.style = style
|
||||
cell.width = 1
|
||||
for i := range cell.ch {
|
||||
if runewidth.RuneWidth(ch[i]) == 2 {
|
||||
cell.width = 2
|
||||
}
|
||||
}
|
||||
}
|
||||
b.Unlock()
|
||||
}
|
||||
|
||||
func (b *bScreen) Show() {
|
||||
b.Lock()
|
||||
|
||||
b.checkResize()
|
||||
|
||||
b.s.HideCursor()
|
||||
|
||||
if b.clear {
|
||||
b.s.Clear()
|
||||
b.clear = false
|
||||
}
|
||||
|
||||
for row := 0; row < b.h; row++ {
|
||||
for col := 0; col < b.w; col++ {
|
||||
c := &b.cells[(row*b.w)+col]
|
||||
if !c.dirty {
|
||||
continue
|
||||
}
|
||||
b.s.SetCell(col, row, c.style, c.ch...)
|
||||
c.dirty = false
|
||||
if c.width == 2 {
|
||||
col++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b.s.ShowCursor(b.cursorx, b.cursory)
|
||||
b.Unlock()
|
||||
}
|
||||
|
||||
func (b *bScreen) Sync() {
|
||||
b.Lock()
|
||||
// do a clear screen and also mark everything dirty
|
||||
b.clear = true
|
||||
for i := range b.cells {
|
||||
b.cells[i].dirty = true
|
||||
}
|
||||
b.Unlock()
|
||||
b.Show()
|
||||
}
|
||||
|
||||
func (b *bScreen) EnableMouse() {
|
||||
b.Lock()
|
||||
b.s.EnableMouse()
|
||||
b.Unlock()
|
||||
}
|
||||
|
||||
func (b *bScreen) DisableMouse() {
|
||||
b.Lock()
|
||||
b.s.DisableMouse()
|
||||
b.Unlock()
|
||||
}
|
||||
|
||||
func NewBufferedScreen() (BufferedScreen, error) {
|
||||
s, e := NewScreen()
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
return MakeBufferedScreen(s), nil
|
||||
}
|
||||
|
||||
func MakeBufferedScreen(s Screen) BufferedScreen {
|
||||
return &bScreen{s: s}
|
||||
}
|
170
cell.go
Normal file
170
cell.go
Normal file
@ -0,0 +1,170 @@
|
||||
// Copyright 2015 The TCell Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use file except in compliance with the License.
|
||||
// You may obtain a copy of the license at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tcell
|
||||
|
||||
import (
|
||||
"github.com/mattn/go-runewidth"
|
||||
)
|
||||
|
||||
// Cell represents a single character cell. This is primarily intended for
|
||||
// use by Screen implementors.
|
||||
type Cell struct {
|
||||
Ch []rune
|
||||
Dirty bool
|
||||
Width uint8
|
||||
Style Style
|
||||
}
|
||||
|
||||
// ClearCells clears the entire set of cells, making them all whitespace with
|
||||
// the provided attribute.
|
||||
func ClearCells(c []Cell, style Style) {
|
||||
for i := range c {
|
||||
c[i].Ch = nil
|
||||
c[i].Style = style
|
||||
c[i].Width = 1
|
||||
c[i].Dirty = true
|
||||
}
|
||||
}
|
||||
|
||||
// InvalidateCells marks all cells in the array dirty.
|
||||
func InvalidateCells(c []Cell) {
|
||||
for i := range c {
|
||||
c[i].Dirty = true
|
||||
}
|
||||
}
|
||||
|
||||
// ResizeCells is used to create a new cells array, with different dimensions,
|
||||
// while preserving the original contents. The returned array may be the same
|
||||
// as the original, if we can reuse it. Hence, the old array should no longer
|
||||
// be used by the caller after this call. The cells will be marked dirty so
|
||||
// that they can be redrawn.
|
||||
func ResizeCells(oldc []Cell, oldw, oldh, neww, newh int) []Cell {
|
||||
|
||||
if oldh == newh && oldw == neww {
|
||||
return oldc
|
||||
}
|
||||
newc := oldc
|
||||
|
||||
// Probably are other conditions where we could reuse, but if there is
|
||||
// any doubt at all, its easier & safest to just realloc the window.
|
||||
if newh > oldh || neww > oldw {
|
||||
newc = make([]Cell, neww*newh)
|
||||
}
|
||||
for row := 0; row < newh && row < oldh; row++ {
|
||||
for col := 0; col < oldw && col < neww; col++ {
|
||||
newc[(row*neww)+col] = oldc[(row*oldw)+col]
|
||||
newc[(row*neww)+col].Dirty = true
|
||||
}
|
||||
}
|
||||
return newc
|
||||
}
|
||||
|
||||
// SetCell writes the contents into the cell. It ensures that at most one
|
||||
// nonzero width rune is present in the Ch array (and if any zero width runes
|
||||
// are present without a non-zero one, then a space is inserted), and updates
|
||||
// the Dirty bit if the contents are different than they were.
|
||||
func (c *Cell) SetCell(ch []rune, style Style) {
|
||||
|
||||
c.PutChars(ch)
|
||||
c.PutStyle(style)
|
||||
/*
|
||||
var mainc rune
|
||||
var width uint8
|
||||
var compc []rune
|
||||
|
||||
width = 1
|
||||
mainc = ' '
|
||||
for _, r := range ch {
|
||||
if r < ' ' {
|
||||
// skip over non-printable control characters
|
||||
continue
|
||||
}
|
||||
switch runewidth.RuneWidth(r) {
|
||||
case 1:
|
||||
mainc = r
|
||||
width = 1
|
||||
case 2:
|
||||
mainc = r
|
||||
width = 2
|
||||
case 0:
|
||||
compc = append(compc, r)
|
||||
}
|
||||
}
|
||||
|
||||
newch := append([]rune{mainc}, compc...)
|
||||
if len(newch) != len(c.Ch) || style != c.Style || c.Dirty {
|
||||
c.Dirty = true
|
||||
} else {
|
||||
for i := range newch {
|
||||
if newch[i] != c.Ch[i] {
|
||||
c.Dirty = true
|
||||
}
|
||||
}
|
||||
}
|
||||
c.Ch = newch
|
||||
c.Style = style
|
||||
c.Width = width
|
||||
*/
|
||||
}
|
||||
|
||||
func (c *Cell) PutChars(ch []rune) {
|
||||
|
||||
var mainc rune
|
||||
var width uint8
|
||||
var compc []rune
|
||||
|
||||
width = 1
|
||||
mainc = ' '
|
||||
for _, r := range ch {
|
||||
if r < ' ' {
|
||||
// skip over non-printable control characters
|
||||
continue
|
||||
}
|
||||
switch runewidth.RuneWidth(r) {
|
||||
case 1:
|
||||
mainc = r
|
||||
width = 1
|
||||
case 2:
|
||||
mainc = r
|
||||
width = 2
|
||||
case 0:
|
||||
compc = append(compc, r)
|
||||
}
|
||||
}
|
||||
|
||||
newch := append([]rune{mainc}, compc...)
|
||||
if len(newch) != len(c.Ch) {
|
||||
c.Dirty = true
|
||||
} else {
|
||||
for i := range newch {
|
||||
if newch[i] != c.Ch[i] {
|
||||
c.Dirty = true
|
||||
}
|
||||
}
|
||||
}
|
||||
c.Ch = newch
|
||||
c.Width = width
|
||||
}
|
||||
|
||||
func (c *Cell) PutChar(ch rune) {
|
||||
c.PutChars([]rune{ch})
|
||||
}
|
||||
|
||||
func (c *Cell) PutStyle(style Style) {
|
||||
if c.Style != style {
|
||||
c.Style = style
|
||||
c.Dirty = true
|
||||
}
|
||||
}
|
327
console_win.go
327
console_win.go
@ -17,11 +17,10 @@
|
||||
package tcell
|
||||
|
||||
import (
|
||||
// "fmt"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
|
||||
"github.com/mattn/go-runewidth"
|
||||
)
|
||||
|
||||
type cScreen struct {
|
||||
@ -30,13 +29,21 @@ type cScreen struct {
|
||||
mbtns uint32 // debounce mouse buttons
|
||||
evch chan Event
|
||||
quit chan struct{}
|
||||
curx int
|
||||
cury int
|
||||
style Style
|
||||
clear bool
|
||||
|
||||
w int
|
||||
h int
|
||||
|
||||
oscreen consoleInfo
|
||||
ocursor cursorInfo
|
||||
omode uint32
|
||||
oimode uint32
|
||||
oomode uint32
|
||||
cells []Cell
|
||||
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// all Windows systems are little endian
|
||||
@ -46,19 +53,18 @@ var k32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
// characters (Unicode) are in use. The documentation refers to them
|
||||
// without this suffix, as the resolution is made via preprocessor.
|
||||
var (
|
||||
procReadConsoleInput = k32.NewProc("ReadConsoleInputW")
|
||||
procGetConsoleCursorInfo = k32.NewProc("GetConsoleCursorInfo")
|
||||
procSetConsoleCursorInfo = k32.NewProc("SetConsoleCursorInfo")
|
||||
procSetConsoleCursorPosition = k32.NewProc("SetConsoleCursorPosition")
|
||||
procSetConsoleMode = k32.NewProc("SetConsoleMode")
|
||||
procGetConsoleMode = k32.NewProc("GetConsoleMode")
|
||||
procWriteConsoleOutput = k32.NewProc("WriteConsoleOutputW")
|
||||
procGetConsoleScreenBufferInfo = k32.NewProc("GetConsoleScreenBufferInfo")
|
||||
procFillConsoleOutputAttribute = k32.NewProc("FillConsoleOutputAttribute")
|
||||
procFillConsoleOutputCharacter = k32.NewProc("FillConsoleOutputCharacterW")
|
||||
procSetConsoleWindowInfo = k32.NewProc("SetConsoleWindowInfo")
|
||||
procSetConsoleScreenBufferSize = k32.NewProc("SetConsoleScreenBufferSize")
|
||||
procSetConsoleActiveScreenBuffer = k32.NewProc("SetConsoleActiveScreenBuffer")
|
||||
procReadConsoleInput = k32.NewProc("ReadConsoleInputW")
|
||||
procGetConsoleCursorInfo = k32.NewProc("GetConsoleCursorInfo")
|
||||
procSetConsoleCursorInfo = k32.NewProc("SetConsoleCursorInfo")
|
||||
procSetConsoleCursorPosition = k32.NewProc("SetConsoleCursorPosition")
|
||||
procSetConsoleMode = k32.NewProc("SetConsoleMode")
|
||||
procGetConsoleMode = k32.NewProc("GetConsoleMode")
|
||||
procGetConsoleScreenBufferInfo = k32.NewProc("GetConsoleScreenBufferInfo")
|
||||
procFillConsoleOutputAttribute = k32.NewProc("FillConsoleOutputAttribute")
|
||||
procFillConsoleOutputCharacter = k32.NewProc("FillConsoleOutputCharacterW")
|
||||
procSetConsoleWindowInfo = k32.NewProc("SetConsoleWindowInfo")
|
||||
procSetConsoleScreenBufferSize = k32.NewProc("SetConsoleScreenBufferSize")
|
||||
procSetConsoleTextAttribute = k32.NewProc("SetConsoleTextAttribute")
|
||||
)
|
||||
|
||||
// We have to bring in the kernel32.dll directly, so we can get access to some
|
||||
@ -85,38 +91,45 @@ func (s *cScreen) Init() error {
|
||||
s.out = out
|
||||
}
|
||||
|
||||
s.curx = -1
|
||||
s.cury = -1
|
||||
s.getCursorInfo(&s.ocursor)
|
||||
s.getConsoleInfo(&s.oscreen)
|
||||
s.getMode(&s.omode)
|
||||
|
||||
if err := s.setMode(modeResizeEn); err != nil {
|
||||
syscall.Close(s.in)
|
||||
syscall.Close(s.out)
|
||||
return err
|
||||
}
|
||||
s.getOutMode(&s.oomode)
|
||||
s.getInMode(&s.oimode)
|
||||
s.resize()
|
||||
s.Clear()
|
||||
s.HideCursor()
|
||||
//procSetConsoleActiveScreenBuffer.Call(uintptr(s.out))
|
||||
|
||||
s.setInMode(modeResizeEn)
|
||||
s.setOutMode(0)
|
||||
s.clearScreen(s.style)
|
||||
s.hideCursor()
|
||||
go s.scanInput()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *cScreen) EnableMouse() {
|
||||
s.setMode(modeResizeEn | modeMouseEn)
|
||||
s.setInMode(modeResizeEn | modeMouseEn)
|
||||
}
|
||||
|
||||
func (s *cScreen) DisableMouse() {
|
||||
s.setMode(modeResizeEn)
|
||||
s.setInMode(modeResizeEn)
|
||||
}
|
||||
|
||||
func (s *cScreen) Fini() {
|
||||
s.setCursorPos(0, 0)
|
||||
s.style = StyleDefault
|
||||
s.curx = -1
|
||||
s.cury = -1
|
||||
|
||||
s.setCursorInfo(&s.ocursor)
|
||||
s.setMode(s.omode)
|
||||
s.setInMode(s.oimode)
|
||||
s.setOutMode(s.oomode)
|
||||
s.setBufferSize(int(s.oscreen.size.x), int(s.oscreen.size.y))
|
||||
s.Clear()
|
||||
s.clearScreen(StyleDefault)
|
||||
s.setCursorPos(0, 0)
|
||||
procSetConsoleTextAttribute.Call(
|
||||
uintptr(s.out),
|
||||
uintptr(mapStyle(StyleDefault)))
|
||||
|
||||
close(s.quit)
|
||||
syscall.Close(s.in)
|
||||
@ -161,25 +174,35 @@ type rect struct {
|
||||
bottom int16
|
||||
}
|
||||
|
||||
func (s *cScreen) ShowCursor(x, y int) {
|
||||
var curinfo cursorInfo
|
||||
func (s *cScreen) showCursor() {
|
||||
s.setCursorInfo(&cursorInfo{size: 100, visible: 1})
|
||||
}
|
||||
|
||||
s.getCursorInfo(&curinfo)
|
||||
if x < 0 || y < 0 {
|
||||
if curinfo.visible == 0 {
|
||||
return
|
||||
}
|
||||
curinfo.visible = 0
|
||||
s.setCursorInfo(&curinfo)
|
||||
func (s *cScreen) hideCursor() {
|
||||
s.setCursorInfo(&cursorInfo{size: 1, visible: 0})
|
||||
}
|
||||
|
||||
func (s *cScreen) ShowCursor(x, y int) {
|
||||
s.Lock()
|
||||
s.curx = x
|
||||
s.cury = y
|
||||
s.Unlock()
|
||||
}
|
||||
|
||||
func (s *cScreen) doCursor() {
|
||||
x, y := s.curx, s.cury
|
||||
|
||||
if x < 0 || y < 0 || x >= s.w || y >= s.h {
|
||||
s.setCursorPos(0, 0)
|
||||
s.hideCursor()
|
||||
} else {
|
||||
curinfo.visible = 1
|
||||
s.setCursorPos(x, y)
|
||||
s.setCursorInfo(&curinfo)
|
||||
s.showCursor()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *cScreen) HideCursor() {
|
||||
c.ShowCursor(10, 5)
|
||||
c.ShowCursor(-1, -1)
|
||||
}
|
||||
|
||||
type charInfo struct {
|
||||
@ -208,6 +231,12 @@ type mouseRecord struct {
|
||||
mod uint32
|
||||
flags uint32
|
||||
}
|
||||
const (
|
||||
mouseDoubleClick uint32 = 0x2
|
||||
mouseHWheeled uint32 = 0x8
|
||||
mouseVWheeled uint32 = 0x4
|
||||
mouseMoved uint32 = 0x1
|
||||
)
|
||||
|
||||
type resizeRecord struct {
|
||||
x int16
|
||||
@ -422,7 +451,8 @@ func (s *cScreen) getConsoleInput() error {
|
||||
return nil
|
||||
}
|
||||
for krec.repeat > 0 {
|
||||
s.PostEvent(NewEventKey(key, rune(krec.ch), mod2mask(krec.mod)))
|
||||
s.PostEvent(NewEventKey(key, rune(krec.ch),
|
||||
mod2mask(krec.mod)))
|
||||
krec.repeat--
|
||||
}
|
||||
|
||||
@ -435,13 +465,6 @@ func (s *cScreen) getConsoleInput() error {
|
||||
mrec.flags = getu32(rec.data[12:]) // not using yet
|
||||
btns := ButtonNone
|
||||
|
||||
if mrec.btns == s.mbtns {
|
||||
// If the buttons have not changed,
|
||||
// then don't report the event. We aren't
|
||||
// reporting motion events at this time.
|
||||
return nil
|
||||
}
|
||||
|
||||
s.mbtns = mrec.btns
|
||||
if mrec.btns&0x1 != 0 {
|
||||
btns |= Button1
|
||||
@ -459,7 +482,23 @@ func (s *cScreen) getConsoleInput() error {
|
||||
btns |= Button5
|
||||
}
|
||||
|
||||
s.PostEvent(NewEventMouse(int(mrec.x), int(mrec.y), btns, mod2mask(mrec.mod)))
|
||||
if mrec.flags & mouseVWheeled != 0 {
|
||||
if mrec.btns & 0x80000000 == 0 {
|
||||
btns |= WheelUp
|
||||
} else {
|
||||
btns |= WheelDown
|
||||
}
|
||||
}
|
||||
if mrec.flags & mouseHWheeled != 0 {
|
||||
if mrec.btns & 0x80000000 == 0 {
|
||||
btns |= WheelRight
|
||||
} else {
|
||||
btns |= WheelLeft
|
||||
}
|
||||
}
|
||||
// we ignore double click, events are delivered normally
|
||||
s.PostEvent(NewEventMouse(int(mrec.x), int(mrec.y), btns,
|
||||
mod2mask(mrec.mod)))
|
||||
|
||||
case resizeEvent:
|
||||
var rrec resizeRecord
|
||||
@ -554,32 +593,120 @@ func mapStyle(style Style) uint16 {
|
||||
}
|
||||
|
||||
func (s *cScreen) SetCell(x, y int, style Style, ch ...rune) {
|
||||
r := ' '
|
||||
w := 0
|
||||
for i := range ch {
|
||||
if w = runewidth.RuneWidth(ch[i]); w != 0 {
|
||||
r = ch[i]
|
||||
break
|
||||
}
|
||||
|
||||
s.Lock()
|
||||
if x < 0 || y < 0 || x >= int(s.w) || y >= int(s.h) {
|
||||
s.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Windows console lacks support for combining chars
|
||||
if w == 0 {
|
||||
r = ' '
|
||||
w = 1
|
||||
cell := &s.cells[(y*int(s.w))+x]
|
||||
cell.SetCell(ch, style)
|
||||
s.Unlock()
|
||||
}
|
||||
|
||||
func (s *cScreen) PutCell(x, y int, cell *Cell) {
|
||||
s.Lock()
|
||||
if x < 0 || y < 0 || x >= int(s.w) || y >= int(s.h) {
|
||||
s.Unlock()
|
||||
return
|
||||
}
|
||||
cptr := &s.cells[(y*int(s.w))+x]
|
||||
cptr.PutChars(cell.Ch)
|
||||
cptr.PutStyle(cell.Style)
|
||||
s.Unlock()
|
||||
}
|
||||
|
||||
rec := rect{left: int16(x), right: int16(x), top: int16(y), bottom: int16(y)}
|
||||
pos := coord{x: int16(0), y: int16(0)}
|
||||
siz := coord{x: 1, y: 1}
|
||||
dat := charInfo{ch: uint16(r), attr: mapStyle(style)}
|
||||
func (s *cScreen) GetCell(x, y int) *Cell {
|
||||
s.Lock()
|
||||
if x < 0 || y < 0 || x >= int(s.w) || y >= int(s.h) {
|
||||
s.Unlock()
|
||||
return nil
|
||||
}
|
||||
cell := s.cells[(y*int(s.w))+x]
|
||||
s.Unlock()
|
||||
return &cell
|
||||
}
|
||||
|
||||
procWriteConsoleOutput.Call(
|
||||
func (s *cScreen) writeString(x, y int, style Style, ch []uint16) {
|
||||
// we assume the caller has hidden the cursor
|
||||
if len(ch) == 0 {
|
||||
return
|
||||
}
|
||||
nw := uint32(len(ch))
|
||||
procSetConsoleTextAttribute.Call(
|
||||
uintptr(s.out),
|
||||
uintptr(unsafe.Pointer(&dat)),
|
||||
siz.uintptr(),
|
||||
pos.uintptr(),
|
||||
uintptr(unsafe.Pointer(&rec)))
|
||||
uintptr(mapStyle(style)))
|
||||
s.setCursorPos(x, y)
|
||||
syscall.WriteConsole(s.out, &ch[0], nw, &nw, nil)
|
||||
}
|
||||
|
||||
func (s *cScreen) draw() {
|
||||
// allocate a scratch line bit enough for no combining chars.
|
||||
// if you have combining characters, you may pay for extra allocs.
|
||||
if s.clear {
|
||||
s.clearScreen(s.style)
|
||||
s.clear = false
|
||||
}
|
||||
buf := make([]uint16, 0, s.w)
|
||||
wcs := buf[:]
|
||||
style := Style(-1) // invalid attribute
|
||||
|
||||
x, y := -1, -1
|
||||
|
||||
for row := 0; row < int(s.h); row++ {
|
||||
width := 1
|
||||
for col := 0; col < int(s.w); col += width {
|
||||
|
||||
cell := &s.cells[(row*s.w)+col]
|
||||
width = int(cell.Width)
|
||||
if width < 1 {
|
||||
width = 1
|
||||
}
|
||||
|
||||
if !cell.Dirty || style != cell.Style {
|
||||
s.writeString(x, y, style, wcs)
|
||||
wcs = buf[0:0]
|
||||
style = Style(-1)
|
||||
if !cell.Dirty {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if len(wcs) == 0 {
|
||||
style = cell.Style
|
||||
x = col
|
||||
y = row
|
||||
}
|
||||
if len(cell.Ch) < 1 {
|
||||
wcs = append(wcs, uint16(' '))
|
||||
} else {
|
||||
wcs = append(wcs, utf16.Encode(cell.Ch)...)
|
||||
}
|
||||
cell.Dirty = false
|
||||
}
|
||||
s.writeString(x, y, style, wcs)
|
||||
wcs = buf[0:0]
|
||||
style = Style(-1)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *cScreen) Show() {
|
||||
s.Lock()
|
||||
s.hideCursor()
|
||||
s.resize()
|
||||
s.draw()
|
||||
s.doCursor()
|
||||
s.Unlock()
|
||||
}
|
||||
|
||||
func (s *cScreen) Sync() {
|
||||
s.Lock()
|
||||
InvalidateCells(s.cells)
|
||||
s.hideCursor()
|
||||
s.resize()
|
||||
s.draw()
|
||||
s.doCursor()
|
||||
s.Unlock()
|
||||
}
|
||||
|
||||
type consoleInfo struct {
|
||||
@ -622,10 +749,9 @@ func (s *cScreen) setBufferSize(x, y int) {
|
||||
|
||||
func (s *cScreen) Size() (int, int) {
|
||||
|
||||
info := consoleInfo{}
|
||||
s.getConsoleInfo(&info)
|
||||
w := int((info.win.right - info.win.left) + 1)
|
||||
h := int((info.win.bottom - info.win.top) + 1)
|
||||
s.Lock()
|
||||
w, h := s.w, s.h
|
||||
s.Unlock()
|
||||
|
||||
return w, h
|
||||
}
|
||||
@ -637,9 +763,15 @@ func (s *cScreen) resize() {
|
||||
|
||||
w := int((info.win.right - info.win.left) + 1)
|
||||
h := int((info.win.bottom - info.win.top) + 1)
|
||||
|
||||
if s.w == w && s.h == h {
|
||||
return
|
||||
}
|
||||
|
||||
s.cells = ResizeCells(s.cells, s.w, s.h, w, h)
|
||||
s.w = w
|
||||
s.h = h
|
||||
|
||||
r := rect{0, 0, int16(w - 1), int16(h - 1)}
|
||||
procSetConsoleWindowInfo.Call(
|
||||
uintptr(s.out),
|
||||
@ -647,12 +779,21 @@ func (s *cScreen) resize() {
|
||||
uintptr(unsafe.Pointer(&r)))
|
||||
|
||||
s.setBufferSize(w, h)
|
||||
|
||||
s.PostEvent(NewEventResize(w, h))
|
||||
}
|
||||
|
||||
func (s *cScreen) Clear() {
|
||||
s.Lock()
|
||||
ClearCells(s.cells, s.style)
|
||||
s.clear = true
|
||||
s.Unlock()
|
||||
}
|
||||
|
||||
func (s *cScreen) clearScreen(style Style) {
|
||||
pos := coord{0, 0}
|
||||
attr := uint16(0x7) // default white fg, black bg)
|
||||
x, y := s.Size()
|
||||
attr := mapStyle(style)
|
||||
x, y := s.w, s.h
|
||||
scratch := uint32(0)
|
||||
count := uint32(x * y)
|
||||
|
||||
@ -673,9 +814,11 @@ func (s *cScreen) Clear() {
|
||||
const (
|
||||
modeMouseEn uint32 = 0x0010
|
||||
modeResizeEn uint32 = 0x0008
|
||||
modeWrapEOL uint32 = 0x0002
|
||||
modeCooked uint32 = 0x0001
|
||||
)
|
||||
|
||||
func (s *cScreen) setMode(mode uint32) error {
|
||||
func (s *cScreen) setInMode(mode uint32) error {
|
||||
rv, _, err := procSetConsoleMode.Call(
|
||||
uintptr(s.in),
|
||||
uintptr(mode))
|
||||
@ -685,8 +828,30 @@ func (s *cScreen) setMode(mode uint32) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *cScreen) getMode(v *uint32) {
|
||||
func (s *cScreen) setOutMode(mode uint32) error {
|
||||
rv, _, err := procSetConsoleMode.Call(
|
||||
uintptr(s.out),
|
||||
uintptr(mode))
|
||||
if rv == 0 {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *cScreen) getInMode(v *uint32) {
|
||||
procGetConsoleMode.Call(
|
||||
uintptr(s.in),
|
||||
uintptr(unsafe.Pointer(v)))
|
||||
}
|
||||
|
||||
func (s *cScreen) getOutMode(v *uint32) {
|
||||
procGetConsoleMode.Call(
|
||||
uintptr(s.out),
|
||||
uintptr(unsafe.Pointer(v)))
|
||||
}
|
||||
|
||||
func (s *cScreen) SetStyle(style Style) {
|
||||
s.Lock()
|
||||
s.style = style
|
||||
s.Unlock()
|
||||
}
|
||||
|
359
database.go
359
database.go
@ -1,27 +1,28 @@
|
||||
// Generated by ./mkinfo (darwin/amd64) on Sat Sep 26 22:40:41 PDT 2015.
|
||||
// Generated by ./mkinfo (darwin/amd64) on Fri Oct 2 21:03:12 PDT 2015.
|
||||
// DO NOT HAND-EDIT
|
||||
|
||||
package tcell
|
||||
|
||||
func init() {
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "adm3a",
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
Clear: "\x1a$<1/>",
|
||||
SetCursor: "\x1b=%p1%' '%+%c%p2%' '%+%c",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\v",
|
||||
KeyUp: "\v",
|
||||
KeyDown: "\n",
|
||||
KeyRight: "\f",
|
||||
KeyLeft: "\b",
|
||||
Name: "adm3a",
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
Clear: "\x1a$<1/>",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b=%p1%' '%+%c%p2%' '%+%c",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\v",
|
||||
KeyUp: "\v",
|
||||
KeyDown: "\n",
|
||||
KeyRight: "\f",
|
||||
KeyLeft: "\b",
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "aixterm",
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Lines: 25,
|
||||
Colors: 8,
|
||||
Bell: "\a",
|
||||
Clear: "\x1b[H\x1b[J",
|
||||
@ -31,6 +32,7 @@ func init() {
|
||||
Reverse: "\x1b[7m",
|
||||
SetFg: "\x1b[3%p1%dm",
|
||||
SetBg: "\x1b[4%p1%dm",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -72,6 +74,7 @@ func init() {
|
||||
Reverse: "\x1b[7m",
|
||||
SetFg: "\x1b[3%p1%dm",
|
||||
SetBg: "\x1b[4%p1%dm",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\x1b[D",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -103,9 +106,10 @@ func init() {
|
||||
ExitKeypad: "\x1b>",
|
||||
SetFg: "\x1b[3%p1%dm",
|
||||
SetBg: "\x1b[4%p1%dm",
|
||||
PadChar: "\x00",
|
||||
Mouse: "\x1b[M",
|
||||
EnterMouse: "\x1b[?1000h",
|
||||
ExitMouse: "\x1b[?1000l",
|
||||
EnterMouse: "\x1b[?1000h\x1b[?1003h\x1b[?1006h",
|
||||
ExitMouse: "\x1b[?1006l\x1b[?1003l\x1b[?1000l",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -136,7 +140,7 @@ func init() {
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "beterm",
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Lines: 25,
|
||||
Colors: 8,
|
||||
Bell: "\a",
|
||||
Clear: "\x1b[H\x1b[J",
|
||||
@ -148,6 +152,7 @@ func init() {
|
||||
ExitKeypad: "\x1b[?4l",
|
||||
SetFg: "\x1b[3%p1%dm",
|
||||
SetBg: "\x1b[4%p1%dm",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -178,7 +183,7 @@ func init() {
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "bsdos-pc",
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Lines: 25,
|
||||
Colors: 8,
|
||||
Bell: "\a",
|
||||
Clear: "\x1bc",
|
||||
@ -189,6 +194,7 @@ func init() {
|
||||
Reverse: "\x1b[7m",
|
||||
SetFg: "\x1b[3%p1%dm",
|
||||
SetBg: "\x1b[4%p1%dm",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -204,8 +210,8 @@ func init() {
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "cygwin",
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Columns: -1,
|
||||
Lines: -1,
|
||||
Colors: 8,
|
||||
Bell: "\a",
|
||||
Clear: "\x1b[H\x1b[J",
|
||||
@ -217,6 +223,7 @@ func init() {
|
||||
Reverse: "\x1b[7m",
|
||||
SetFg: "\x1b[3%p1%dm",
|
||||
SetBg: "\x1b[4%p1%dm",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -245,72 +252,74 @@ func init() {
|
||||
KeyF12: "\x1b[24~",
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "d200",
|
||||
Aliases: []string{"d200-dg"},
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
Clear: "\f",
|
||||
AttrOff: "\x0f\x15\x1d\x1eE",
|
||||
Underline: "\x14",
|
||||
Bold: "\x1eD\x14",
|
||||
Dim: "\x1c",
|
||||
Blink: "\x0e",
|
||||
Reverse: "\x1eD",
|
||||
SetCursor: "\x10%p2%c%p1%c",
|
||||
CursorBack1: "\x19",
|
||||
CursorUp1: "\x17",
|
||||
KeyUp: "\x17",
|
||||
KeyDown: "\x1a",
|
||||
KeyRight: "\x18",
|
||||
KeyLeft: "\x19",
|
||||
KeyHome: "\b",
|
||||
KeyF1: "\x1eq",
|
||||
KeyF2: "\x1er",
|
||||
KeyF3: "\x1es",
|
||||
KeyF4: "\x1et",
|
||||
KeyF5: "\x1eu",
|
||||
KeyF6: "\x1ev",
|
||||
KeyF7: "\x1ew",
|
||||
KeyF8: "\x1ex",
|
||||
KeyF9: "\x1ey",
|
||||
KeyF10: "\x1ez",
|
||||
KeyF11: "\x1e{",
|
||||
KeyF12: "\x1e|",
|
||||
Name: "d200",
|
||||
Aliases: []string{ "d200-dg" },
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
Clear: "\f",
|
||||
AttrOff: "\x0f\x15\x1d\x1eE",
|
||||
Underline: "\x14",
|
||||
Bold: "\x1eD\x14",
|
||||
Dim: "\x1c",
|
||||
Blink: "\x0e",
|
||||
Reverse: "\x1eD",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x10%p2%c%p1%c",
|
||||
CursorBack1: "\x19",
|
||||
CursorUp1: "\x17",
|
||||
KeyUp: "\x17",
|
||||
KeyDown: "\x1a",
|
||||
KeyRight: "\x18",
|
||||
KeyLeft: "\x19",
|
||||
KeyHome: "\b",
|
||||
KeyF1: "\x1eq",
|
||||
KeyF2: "\x1er",
|
||||
KeyF3: "\x1es",
|
||||
KeyF4: "\x1et",
|
||||
KeyF5: "\x1eu",
|
||||
KeyF6: "\x1ev",
|
||||
KeyF7: "\x1ew",
|
||||
KeyF8: "\x1ex",
|
||||
KeyF9: "\x1ey",
|
||||
KeyF10: "\x1ez",
|
||||
KeyF11: "\x1e{",
|
||||
KeyF12: "\x1e|",
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "d210",
|
||||
Aliases: []string{"d214"},
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
Clear: "\x1b[2J",
|
||||
AttrOff: "\x1b[m",
|
||||
Underline: "\x1b[4m",
|
||||
Bold: "\x1b[4;7m",
|
||||
Dim: "\x1b[2m",
|
||||
Blink: "\x1b[5m",
|
||||
Reverse: "\x1b[7m",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
KeyUp: "\x1b[A",
|
||||
KeyDown: "\x1b[B",
|
||||
KeyRight: "\x1b[C",
|
||||
KeyLeft: "\x1b[D",
|
||||
KeyHome: "\x1b[H",
|
||||
KeyF1: "\x1b[001z",
|
||||
KeyF2: "\x1b[002z",
|
||||
KeyF3: "\x1b[003z",
|
||||
KeyF4: "\x1b[004z",
|
||||
KeyF5: "\x1b[005z",
|
||||
KeyF6: "\x1b[006z",
|
||||
KeyF7: "\x1b[007z",
|
||||
KeyF8: "\x1b[008z",
|
||||
KeyF9: "\x1b[009z",
|
||||
KeyF10: "\x1b[010z",
|
||||
KeyF11: "\x1b[011z",
|
||||
KeyF12: "\x1b[012z",
|
||||
Name: "d210",
|
||||
Aliases: []string{ "d214" },
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
Clear: "\x1b[2J",
|
||||
AttrOff: "\x1b[m",
|
||||
Underline: "\x1b[4m",
|
||||
Bold: "\x1b[4;7m",
|
||||
Dim: "\x1b[2m",
|
||||
Blink: "\x1b[5m",
|
||||
Reverse: "\x1b[7m",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
KeyUp: "\x1b[A",
|
||||
KeyDown: "\x1b[B",
|
||||
KeyRight: "\x1b[C",
|
||||
KeyLeft: "\x1b[D",
|
||||
KeyHome: "\x1b[H",
|
||||
KeyF1: "\x1b[001z",
|
||||
KeyF2: "\x1b[002z",
|
||||
KeyF3: "\x1b[003z",
|
||||
KeyF4: "\x1b[004z",
|
||||
KeyF5: "\x1b[005z",
|
||||
KeyF6: "\x1b[006z",
|
||||
KeyF7: "\x1b[007z",
|
||||
KeyF8: "\x1b[008z",
|
||||
KeyF9: "\x1b[009z",
|
||||
KeyF10: "\x1b[010z",
|
||||
KeyF11: "\x1b[011z",
|
||||
KeyF12: "\x1b[012z",
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "dtterm",
|
||||
@ -329,6 +338,7 @@ func init() {
|
||||
Reverse: "\x1b[7m",
|
||||
SetFg: "\x1b[3%p1%dm",
|
||||
SetBg: "\x1b[4%p1%dm",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -356,7 +366,7 @@ func init() {
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "Eterm",
|
||||
Aliases: []string{"Eterm-color"},
|
||||
Aliases: []string{ "Eterm-color" },
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Colors: 8,
|
||||
@ -373,9 +383,10 @@ func init() {
|
||||
Reverse: "\x1b[7m",
|
||||
SetFg: "\x1b[3%p1%dm",
|
||||
SetBg: "\x1b[4%p1%dm",
|
||||
PadChar: "\x00",
|
||||
Mouse: "\x1b[M",
|
||||
EnterMouse: "\x1b[?1000h",
|
||||
ExitMouse: "\x1b[?1000l",
|
||||
EnterMouse: "\x1b[?1000h\x1b[?1003h\x1b[?1006h",
|
||||
ExitMouse: "\x1b[?1006l\x1b[?1003l\x1b[?1000l",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -421,9 +432,10 @@ func init() {
|
||||
Reverse: "\x1b[7m",
|
||||
SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
|
||||
SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
|
||||
PadChar: "\x00",
|
||||
Mouse: "\x1b[M",
|
||||
EnterMouse: "\x1b[?1000h",
|
||||
ExitMouse: "\x1b[?1000l",
|
||||
EnterMouse: "\x1b[?1000h\x1b[?1003h\x1b[?1006h",
|
||||
ExitMouse: "\x1b[?1006l\x1b[?1003l\x1b[?1000l",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -452,20 +464,21 @@ func init() {
|
||||
KeyF12: "\x1b[24~",
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "eterm",
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
Clear: "\x1b[H\x1b[J",
|
||||
EnterCA: "\x1b7\x1b[?47h",
|
||||
ExitCA: "\x1b[2J\x1b[?47l\x1b8",
|
||||
AttrOff: "\x1b[m",
|
||||
Underline: "\x1b[4m",
|
||||
Bold: "\x1b[1m",
|
||||
Reverse: "\x1b[7m",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
Name: "eterm",
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
Clear: "\x1b[H\x1b[J",
|
||||
EnterCA: "\x1b7\x1b[?47h",
|
||||
ExitCA: "\x1b[2J\x1b[?47l\x1b8",
|
||||
AttrOff: "\x1b[m",
|
||||
Underline: "\x1b[4m",
|
||||
Bold: "\x1b[1m",
|
||||
Reverse: "\x1b[7m",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "gnome",
|
||||
@ -486,9 +499,10 @@ func init() {
|
||||
ExitKeypad: "\x1b[?1l\x1b>",
|
||||
SetFg: "\x1b[3%p1%dm",
|
||||
SetBg: "\x1b[4%p1%dm",
|
||||
PadChar: "\x00",
|
||||
Mouse: "\x1b[M",
|
||||
EnterMouse: "\x1b[?1000h",
|
||||
ExitMouse: "\x1b[?1000l",
|
||||
EnterMouse: "\x1b[?1000h\x1b[?1003h\x1b[?1006h",
|
||||
ExitMouse: "\x1b[?1006l\x1b[?1003l\x1b[?1000l",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -535,9 +549,10 @@ func init() {
|
||||
ExitKeypad: "\x1b[?1l\x1b>",
|
||||
SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
|
||||
SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
|
||||
PadChar: "\x00",
|
||||
Mouse: "\x1b[M",
|
||||
EnterMouse: "\x1b[?1000h",
|
||||
ExitMouse: "\x1b[?1000l",
|
||||
EnterMouse: "\x1b[?1000h\x1b[?1003h\x1b[?1006h",
|
||||
ExitMouse: "\x1b[?1006l\x1b[?1003l\x1b[?1000l",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -567,7 +582,7 @@ func init() {
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "hpterm",
|
||||
Aliases: []string{"X-hpterm"},
|
||||
Aliases: []string{ "X-hpterm" },
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
@ -579,6 +594,7 @@ func init() {
|
||||
Reverse: "\x1b&dB",
|
||||
EnterKeypad: "\x1b&s1A",
|
||||
ExitKeypad: "\x1b&s0A",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b&a%p1%dy%p2%dC",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1bA",
|
||||
@ -602,19 +618,20 @@ func init() {
|
||||
KeyF8: "\x1bw",
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "hz1500",
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
Clear: "~\x1c",
|
||||
SetCursor: "~\x11%p2%p2%?%{30}%>%t%' '%+%;%'`'%+%c%p1%'`'%+%c",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "~\f",
|
||||
KeyUp: "~\f",
|
||||
KeyDown: "\n",
|
||||
KeyRight: "\x10",
|
||||
KeyLeft: "\b",
|
||||
KeyHome: "~\x12",
|
||||
Name: "hz1500",
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
Clear: "~\x1c",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "~\x11%p2%p2%?%{30}%>%t%' '%+%;%'`'%+%c%p1%'`'%+%c",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "~\f",
|
||||
KeyUp: "~\f",
|
||||
KeyDown: "\n",
|
||||
KeyRight: "\x10",
|
||||
KeyLeft: "\b",
|
||||
KeyHome: "~\x12",
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "konsole",
|
||||
@ -636,8 +653,8 @@ func init() {
|
||||
SetFg: "\x1b[3%p1%dm",
|
||||
SetBg: "\x1b[4%p1%dm",
|
||||
Mouse: "\x1b[M",
|
||||
EnterMouse: "\x1b[?1000h",
|
||||
ExitMouse: "\x1b[?1000l",
|
||||
EnterMouse: "\x1b[?1000h\x1b[?1003h\x1b[?1006h",
|
||||
ExitMouse: "\x1b[?1006l\x1b[?1003l\x1b[?1000l",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -682,9 +699,10 @@ func init() {
|
||||
ExitKeypad: "\x1b[?1l\x1b>",
|
||||
SetFg: "\x1b[3%p1%dm",
|
||||
SetBg: "\x1b[4%p1%dm",
|
||||
PadChar: "\x00",
|
||||
Mouse: "\x1b[M",
|
||||
EnterMouse: "\x1b[?1000h",
|
||||
ExitMouse: "\x1b[?1000l",
|
||||
EnterMouse: "\x1b[?1000h\x1b[?1003h\x1b[?1006h",
|
||||
ExitMouse: "\x1b[?1006l\x1b[?1003l\x1b[?1000l",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -712,8 +730,8 @@ func init() {
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "linux",
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Columns: -1,
|
||||
Lines: -1,
|
||||
Colors: 8,
|
||||
Bell: "\a",
|
||||
Clear: "\x1b[H\x1b[J",
|
||||
@ -727,9 +745,10 @@ func init() {
|
||||
Reverse: "\x1b[7m",
|
||||
SetFg: "\x1b[3%p1%dm",
|
||||
SetBg: "\x1b[4%p1%dm",
|
||||
PadChar: "\x00",
|
||||
Mouse: "\x1b[M",
|
||||
EnterMouse: "\x1b[?1000h",
|
||||
ExitMouse: "\x1b[?1000l",
|
||||
EnterMouse: "\x1b[?1000h\x1b[?1003h\x1b[?1006h",
|
||||
ExitMouse: "\x1b[?1006l\x1b[?1003l\x1b[?1000l",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -771,6 +790,7 @@ func init() {
|
||||
Reverse: "\x1b[7m",
|
||||
SetFg: "\x1b[3%p1%dm",
|
||||
SetBg: "\x1b[4%p1%dm",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\x1b[D",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -801,9 +821,10 @@ func init() {
|
||||
ExitKeypad: "\x1b>",
|
||||
SetFg: "\x1b[3%p1%dm",
|
||||
SetBg: "\x1b[4%p1%dm",
|
||||
PadChar: "\x00",
|
||||
Mouse: "\x1b[M",
|
||||
EnterMouse: "\x1b[?1000h",
|
||||
ExitMouse: "\x1b[?1000l",
|
||||
EnterMouse: "\x1b[?1000h\x1b[?1003h\x1b[?1006h",
|
||||
ExitMouse: "\x1b[?1006l\x1b[?1003l\x1b[?1000l",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -851,9 +872,10 @@ func init() {
|
||||
ExitKeypad: "\x1b>",
|
||||
SetFg: "\x1b[%?%p1%{8}%<%t%p1%{30}%+%e%p1%'R'%+%;%dm",
|
||||
SetBg: "\x1b[%?%p1%{8}%<%t%p1%'('%+%e%p1%{92}%+%;%dm",
|
||||
PadChar: "\x00",
|
||||
Mouse: "\x1b[M",
|
||||
EnterMouse: "\x1b[?1000h",
|
||||
ExitMouse: "\x1b[?1000l",
|
||||
EnterMouse: "\x1b[?1000h\x1b[?1003h\x1b[?1006h",
|
||||
ExitMouse: "\x1b[?1006l\x1b[?1003l\x1b[?1000l",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -901,9 +923,10 @@ func init() {
|
||||
ExitKeypad: "\x1b>",
|
||||
SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
|
||||
SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
|
||||
PadChar: "\x00",
|
||||
Mouse: "\x1b[M",
|
||||
EnterMouse: "\x1b[?1000h",
|
||||
ExitMouse: "\x1b[?1000l",
|
||||
EnterMouse: "\x1b[?1000h\x1b[?1003h\x1b[?1006h",
|
||||
ExitMouse: "\x1b[?1006l\x1b[?1003l\x1b[?1000l",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -951,9 +974,10 @@ func init() {
|
||||
ExitKeypad: "\x1b[?1l\x1b>",
|
||||
SetFg: "\x1b[3%p1%dm",
|
||||
SetBg: "\x1b[4%p1%dm",
|
||||
PadChar: "\x00",
|
||||
Mouse: "\x1b[M",
|
||||
EnterMouse: "\x1b[?1000h",
|
||||
ExitMouse: "\x1b[?1000l",
|
||||
EnterMouse: "\x1b[?1000h\x1b[?1003h\x1b[?1006h",
|
||||
ExitMouse: "\x1b[?1006l\x1b[?1003l\x1b[?1000l",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1bM",
|
||||
@ -983,13 +1007,14 @@ func init() {
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "sun",
|
||||
Aliases: []string{"sun1", "sun2"},
|
||||
Aliases: []string{ "sun1", "sun2" },
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Lines: 34,
|
||||
Bell: "\a",
|
||||
Clear: "\f",
|
||||
AttrOff: "\x1b[m",
|
||||
Reverse: "\x1b[7m",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -1019,7 +1044,7 @@ func init() {
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "sun-color",
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Lines: 34,
|
||||
Colors: 8,
|
||||
Bell: "\a",
|
||||
Clear: "\f",
|
||||
@ -1027,6 +1052,7 @@ func init() {
|
||||
Reverse: "\x1b[7m",
|
||||
SetFg: "\x1b[3%p1%dm",
|
||||
SetBg: "\x1b[4%p1%dm",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -1062,6 +1088,7 @@ func init() {
|
||||
AttrOff: "\x1bG0",
|
||||
Underline: "\x1bG8",
|
||||
Reverse: "\x1bG4",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b=%p1%' '%+%c%p2%' '%+%c",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\v",
|
||||
@ -1083,12 +1110,13 @@ func init() {
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "tvi912",
|
||||
Aliases: []string{"tvi920", "tvi914"},
|
||||
Aliases: []string{ "tvi920", "tvi914" },
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
Clear: "\x1a",
|
||||
Underline: "\x1bl",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b=%p1%' '%+%c%p2%' '%+%c",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\v",
|
||||
@ -1117,6 +1145,7 @@ func init() {
|
||||
AttrOff: "\x1bG0",
|
||||
Underline: "\x1bG8",
|
||||
Reverse: "\x1bG4",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b=%p1%' '%+%c%p2%' '%+%c$<3/>",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\v",
|
||||
@ -1138,6 +1167,7 @@ func init() {
|
||||
AttrOff: "\x1bG0",
|
||||
Underline: "\x1bG8",
|
||||
Reverse: "\x1bG4",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b=%p1%' '%+%c%p2%' '%+%c",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\v",
|
||||
@ -1168,6 +1198,7 @@ func init() {
|
||||
AttrOff: "\x1bG0",
|
||||
Underline: "\x1bG8",
|
||||
Reverse: "\x1bG4",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b=%p1%' '%+%c%p2%' '%+%c",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\v",
|
||||
@ -1197,6 +1228,7 @@ func init() {
|
||||
EnterCA: "\x1b[?20l\x1b[?7h\x1b[1Q",
|
||||
AttrOff: "\x1b[m",
|
||||
Underline: "\x1b[4m",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%df",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1bM",
|
||||
@ -1222,6 +1254,7 @@ func init() {
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
Clear: "\x1bH\x1bJ",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1bY%p1%' '%+%c%p2%' '%+%c",
|
||||
CursorBack1: "\x1bD",
|
||||
CursorUp1: "\x1bA",
|
||||
@ -1233,7 +1266,7 @@ func init() {
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "vt100",
|
||||
Aliases: []string{"vt100-am"},
|
||||
Aliases: []string{ "vt100-am" },
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
@ -1245,6 +1278,7 @@ func init() {
|
||||
Reverse: "\x1b[7m$<2>",
|
||||
EnterKeypad: "\x1b[?1h\x1b=",
|
||||
ExitKeypad: "\x1b[?1l\x1b>",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH$<5>",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A$<2>",
|
||||
@ -1277,6 +1311,7 @@ func init() {
|
||||
Reverse: "\x1b[7m$<2>",
|
||||
EnterKeypad: "\x1b[?1h\x1b=",
|
||||
ExitKeypad: "\x1b[?1l\x1b>",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH$<5>",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A$<2>",
|
||||
@ -1298,7 +1333,7 @@ func init() {
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "vt220",
|
||||
Aliases: []string{"vt200"},
|
||||
Aliases: []string{ "vt200" },
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
@ -1308,6 +1343,7 @@ func init() {
|
||||
Bold: "\x1b[1m",
|
||||
Blink: "\x1b[5m",
|
||||
Reverse: "\x1b[7m",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -1333,7 +1369,7 @@ func init() {
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "vt320",
|
||||
Aliases: []string{"vt300"},
|
||||
Aliases: []string{ "vt300" },
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
@ -1347,6 +1383,7 @@ func init() {
|
||||
Reverse: "\x1b[7m",
|
||||
EnterKeypad: "\x1b[?1h\x1b=",
|
||||
ExitKeypad: "\x1b[?1l\x1b>",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -1374,7 +1411,7 @@ func init() {
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "vt400",
|
||||
Aliases: []string{"vt400-24", "dec-vt400"},
|
||||
Aliases: []string{ "vt400-24", "dec-vt400" },
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Clear: "\x1b[H\x1b[J$<10/>",
|
||||
@ -1387,6 +1424,7 @@ func init() {
|
||||
Reverse: "\x1b[7m",
|
||||
EnterKeypad: "\x1b[?1h\x1b=",
|
||||
ExitKeypad: "\x1b[?1l\x1b>",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -1417,6 +1455,7 @@ func init() {
|
||||
Reverse: "\x1b[7m$<2>",
|
||||
EnterKeypad: "\x1b=",
|
||||
ExitKeypad: "\x1b>",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH$<10>",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -1442,7 +1481,7 @@ func init() {
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "wy50",
|
||||
Aliases: []string{"wyse50"},
|
||||
Aliases: []string{ "wyse50" },
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
@ -1452,6 +1491,7 @@ func init() {
|
||||
AttrOff: "\x1b(\x1bH\x03",
|
||||
Dim: "\x1b`7\x1b)",
|
||||
Reverse: "\x1b`6\x1b)",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b=%p1%' '%+%c%p2%' '%+%c",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\v",
|
||||
@ -1480,7 +1520,7 @@ func init() {
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "wy60",
|
||||
Aliases: []string{"wyse60"},
|
||||
Aliases: []string{ "wyse60" },
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
@ -1494,6 +1534,7 @@ func init() {
|
||||
Dim: "\x1bGp",
|
||||
Blink: "\x1bG2",
|
||||
Reverse: "\x1bG4",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b=%p1%' '%+%c%p2%' '%+%c",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\v",
|
||||
@ -1523,7 +1564,7 @@ func init() {
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "wy99-ansi",
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Lines: 25,
|
||||
Bell: "\a",
|
||||
Clear: "\x1b[H\x1b[J$<200>",
|
||||
ShowCursor: "\x1b[34h\x1b[?25h",
|
||||
@ -1536,6 +1577,7 @@ func init() {
|
||||
Reverse: "\x1b[7m",
|
||||
EnterKeypad: "\x1b[?1h",
|
||||
ExitKeypad: "\x1b[?1l",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b$<1>",
|
||||
CursorUp1: "\x1bM",
|
||||
@ -1560,7 +1602,7 @@ func init() {
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "wy99a-ansi",
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Lines: 25,
|
||||
Bell: "\a",
|
||||
Clear: "\x1b[H\x1b[J$<200>",
|
||||
ShowCursor: "\x1b[34h\x1b[?25h",
|
||||
@ -1573,6 +1615,7 @@ func init() {
|
||||
Reverse: "\x1b[7m",
|
||||
EnterKeypad: "\x1b[?1h",
|
||||
ExitKeypad: "\x1b[?1l",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b$<1>",
|
||||
CursorUp1: "\x1bM",
|
||||
@ -1613,9 +1656,10 @@ func init() {
|
||||
ExitKeypad: "\x1b[?1l\x1b>",
|
||||
SetFg: "\x1b[3%p1%dm",
|
||||
SetBg: "\x1b[4%p1%dm",
|
||||
PadChar: "\x00",
|
||||
Mouse: "\x1b[M",
|
||||
EnterMouse: "\x1b[?1000h",
|
||||
ExitMouse: "\x1b[?1000l",
|
||||
EnterMouse: "\x1b[?1000h\x1b[?1003h\x1b[?1006h",
|
||||
ExitMouse: "\x1b[?1006l\x1b[?1003l\x1b[?1000l",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -1645,8 +1689,8 @@ func init() {
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "xnuppc",
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Columns: -1,
|
||||
Lines: -1,
|
||||
Colors: 8,
|
||||
Clear: "\x1b[H\x1b[J",
|
||||
AttrOff: "\x1b[m\x0f",
|
||||
@ -1657,6 +1701,7 @@ func init() {
|
||||
ExitKeypad: "\x1b[?1l\x1b>",
|
||||
SetFg: "\x1b[3%p1%dm",
|
||||
SetBg: "\x1b[4%p1%dm",
|
||||
PadChar: "\x00",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\x1b[D",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -1687,8 +1732,8 @@ func init() {
|
||||
SetFg: "\x1b[3%p1%dm",
|
||||
SetBg: "\x1b[4%p1%dm",
|
||||
Mouse: "\x1b[M",
|
||||
EnterMouse: "\x1b[?1000h",
|
||||
ExitMouse: "\x1b[?1000l",
|
||||
EnterMouse: "\x1b[?1000h\x1b[?1003h\x1b[?1006h",
|
||||
ExitMouse: "\x1b[?1006l\x1b[?1003l\x1b[?1000l",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
@ -1737,8 +1782,8 @@ func init() {
|
||||
SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
|
||||
SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
|
||||
Mouse: "\x1b[M",
|
||||
EnterMouse: "\x1b[?1000h",
|
||||
ExitMouse: "\x1b[?1000l",
|
||||
EnterMouse: "\x1b[?1000h\x1b[?1003h\x1b[?1006h",
|
||||
ExitMouse: "\x1b[?1006l\x1b[?1003l\x1b[?1000l",
|
||||
SetCursor: "\x1b[%i%p1%d;%p2%dH",
|
||||
CursorBack1: "\b",
|
||||
CursorUp1: "\x1b[A",
|
||||
|
2694
database.json
2694
database.json
File diff suppressed because it is too large
Load Diff
35
mkinfo.go
35
mkinfo.go
@ -46,6 +46,10 @@ import (
|
||||
// #include <curses.h>
|
||||
// #include <term.h>
|
||||
// #cgo LDFLAGS: -lcurses
|
||||
//
|
||||
// void noenv() {
|
||||
// use_env(FALSE);
|
||||
// }
|
||||
import "C"
|
||||
|
||||
func tigetnum(s string) int {
|
||||
@ -53,6 +57,11 @@ func tigetnum(s string) int {
|
||||
return int(n)
|
||||
}
|
||||
|
||||
func tigetflag(s string) bool {
|
||||
n := C.tigetflag(C.CString(s))
|
||||
return n != 0
|
||||
}
|
||||
|
||||
func tigetstr(s string) string {
|
||||
// NB: If the string is invalid, we'll get back -1, which causes
|
||||
// no end of grief. So make sure your capability strings are correct!
|
||||
@ -70,7 +79,8 @@ func tigetstr(s string) string {
|
||||
// terminal types.
|
||||
func getinfo(name string) (*tcell.Terminfo, error) {
|
||||
rsn := C.int(0)
|
||||
rv, e := C.setupterm(C.CString(name), 1, &rsn)
|
||||
C.noenv()
|
||||
rv, _ := C.setupterm(C.CString(name), 1, &rsn)
|
||||
if rv == C.ERR {
|
||||
switch rsn {
|
||||
case 1:
|
||||
@ -83,9 +93,6 @@ func getinfo(name string) (*tcell.Terminfo, error) {
|
||||
return nil, errors.New("setupterm failed (other)")
|
||||
}
|
||||
}
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
t := &tcell.Terminfo{}
|
||||
t.Name = name
|
||||
t.Colors = tigetnum("colors")
|
||||
@ -141,8 +148,13 @@ func getinfo(name string) (*tcell.Terminfo, error) {
|
||||
// manual, and all terminals that have kmous are expected to
|
||||
// use these same codes.
|
||||
if t.Mouse != "" {
|
||||
t.EnterMouse = "\x1b[?1000h"
|
||||
t.ExitMouse = "\x1b[?1000l"
|
||||
// we anticipate that all xterm mouse tracking compatible
|
||||
// terminals understand mouse tracking (1000), but we hope
|
||||
// that those that don't understand any-event tracking (1003)
|
||||
// will at least ignore it. Likewise we hope that terminals
|
||||
// that don't understand SGR reporting (1006) just ignore it.
|
||||
t.EnterMouse = "\x1b[?1000h\x1b[?1003h\x1b[?1006h"
|
||||
t.ExitMouse = "\x1b[?1006l\x1b[?1003l\x1b[?1000l"
|
||||
}
|
||||
// We only support colors in ANSI 8 or 256 color mode.
|
||||
if t.Colors < 8 || t.SetFg == "" {
|
||||
@ -151,6 +163,16 @@ func getinfo(name string) (*tcell.Terminfo, error) {
|
||||
if t.SetCursor == "" {
|
||||
return nil, errors.New("terminal not cursor addressable")
|
||||
}
|
||||
|
||||
// For padding, we lookup the pad char. If that isn't present,
|
||||
// and npc is *not* set, then we assume a null byte.
|
||||
t.PadChar = tigetstr("pad")
|
||||
if t.PadChar == "" {
|
||||
if !tigetflag("npc") {
|
||||
t.PadChar = "\u0000"
|
||||
}
|
||||
}
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
@ -223,6 +245,7 @@ func dotGoInfo(w io.Writer, t *tcell.Terminfo) {
|
||||
dotGoAddStr(w, "ExitKeypad", t.ExitKeypad)
|
||||
dotGoAddStr(w, "SetFg", t.SetFg)
|
||||
dotGoAddStr(w, "SetBg", t.SetBg)
|
||||
dotGoAddStr(w, "PadChar", t.PadChar)
|
||||
dotGoAddStr(w, "Mouse", t.Mouse)
|
||||
dotGoAddStr(w, "EnterMouse", t.EnterMouse)
|
||||
dotGoAddStr(w, "ExitMouse", t.ExitMouse)
|
||||
|
19
mouse.go
19
mouse.go
@ -19,10 +19,19 @@ import (
|
||||
)
|
||||
|
||||
// EventMouse is a mouse event. It is sent on either mouse up or mouse down
|
||||
// events. (Eventually we can also arrange for mouse motion events, but only
|
||||
// with genuine xterm -- other emulators lack support for tracking this.)
|
||||
// events. It is also sent on mouse motion events - if the terminal supports
|
||||
// it. We make every effort to ensure that mouse release events are delivered.
|
||||
// Hence, click drag can be identified by a motion event with the mouse down,
|
||||
// without any intervening button release.
|
||||
//
|
||||
// Mouse wheel events, when reported, may appear on their own as individual
|
||||
// impulses.
|
||||
//
|
||||
// Most terminals cannot report the state of more than one button at a time --
|
||||
// that is buttons are seen together.
|
||||
// and few can report motion events.
|
||||
//
|
||||
// Applications can inspect the time between events to figure out double clicks
|
||||
// and such.
|
||||
type EventMouse struct {
|
||||
t time.Time
|
||||
btn ButtonMask
|
||||
@ -63,5 +72,9 @@ const (
|
||||
Button6
|
||||
Button7
|
||||
Button8
|
||||
WheelUp
|
||||
WheelDown
|
||||
WheelLeft
|
||||
WheelRight
|
||||
)
|
||||
const ButtonNone ButtonMask = 0
|
||||
|
40
screen.go
40
screen.go
@ -29,7 +29,7 @@ type Screen interface {
|
||||
Clear()
|
||||
|
||||
// SetCell sets the cell at the given location.
|
||||
// The ch array contains at most one rune of width > 0, and the
|
||||
// The ch list contains at most one rune of width > 0, and the
|
||||
// runes with zero width (combining marks) must follow the first
|
||||
// non-zero width character. (If only combining marks are present,
|
||||
// a space character will be filled in.)
|
||||
@ -39,15 +39,32 @@ type Screen interface {
|
||||
// undefined effects. Double wide runes that are printed in the
|
||||
// last column will be replaced with a single width space on output.
|
||||
//
|
||||
// Note that unlike the higher level interfaces, this operates
|
||||
// immediately, without any buffering.
|
||||
//
|
||||
// SetCell may change the cursor location. Callers should explictly
|
||||
// save and restore cursor state if neccesary. The cursor visibility
|
||||
// is not affected, so callers probably should hide the cursor when
|
||||
// calling this.
|
||||
//
|
||||
// Note that the results will not be visible until either Show() or
|
||||
// Sync() are called.
|
||||
SetCell(x int, y int, style Style, ch ...rune)
|
||||
|
||||
// PutCell stores the contents of the given cell at the given location.
|
||||
// The Dirty flag on the stored cell is set to true if the contents
|
||||
// do not match.
|
||||
PutCell(x, y int, cell *Cell)
|
||||
|
||||
// GetCell returns the contents of the given cell. If the coordinates
|
||||
// are out of range, then nil will be returned for the rune array.
|
||||
// This will also be the case if no content has been written to that
|
||||
// location. Note that the returned Cell object is a copy, and
|
||||
// modifications made will not change the display.
|
||||
GetCell(x, y int) *Cell
|
||||
|
||||
// SetStyle sets the default style to use when clearing the screen
|
||||
// or when StyleDefault is specified. If it is also STyleDefault,
|
||||
// then whatever system/terminal default is relevant will be used.
|
||||
SetStyle(style Style)
|
||||
|
||||
// ShowCursor is used to display the cursor at a given location.
|
||||
// If the coordinates -1, -1 are given or are otherwise outside the
|
||||
// dimensions of the screen, the cursor will be hidden.
|
||||
@ -78,6 +95,21 @@ type Screen interface {
|
||||
// Colors returns the number of colors. All colors are assumed to
|
||||
// use the ANSI color map.
|
||||
Colors() int
|
||||
|
||||
// Show takes any output that was deferred due to buffering, and
|
||||
// flushes it to the physical display. It does so in the least
|
||||
// expensive and disruptive manner possible, only making writes that
|
||||
// are believed to actually be necessary.
|
||||
Show()
|
||||
|
||||
// Sync works like Show(), but it updates every visible cell on the
|
||||
// physical display, assuming that it is not synchronized with any
|
||||
// internal model. This may be both expensive and visually jarring,
|
||||
// so it should only be used when believed to actually be necessary.
|
||||
// Typically this is called as a result of a user-requested redraw
|
||||
// (e.g. to clear up on screen corruption caused by some other program),
|
||||
// or during a resize event.
|
||||
Sync()
|
||||
}
|
||||
|
||||
func NewScreen() (Screen, error) {
|
||||
|
@ -22,12 +22,12 @@ import (
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
var screen tcell.BufferedScreen
|
||||
var screen tcell.Screen
|
||||
var outMode OutputMode
|
||||
|
||||
func Init() error {
|
||||
outMode = OutputNormal
|
||||
if s, e := tcell.NewBufferedScreen(); e != nil {
|
||||
if s, e := tcell.NewScreen(); e != nil {
|
||||
return e
|
||||
} else if e = s.Init(); e != nil {
|
||||
return e
|
||||
@ -59,6 +59,7 @@ func Size() (int, int) {
|
||||
}
|
||||
|
||||
type Attribute uint16
|
||||
|
||||
const (
|
||||
ColorDefault Attribute = iota
|
||||
ColorBlack
|
||||
@ -71,7 +72,7 @@ const (
|
||||
ColorWhite
|
||||
)
|
||||
const (
|
||||
AttrBold Attribute = 1 << (9+iota)
|
||||
AttrBold Attribute = 1 << (9 + iota)
|
||||
AttrUnderline
|
||||
AttrReverse
|
||||
)
|
||||
@ -113,13 +114,13 @@ func mkStyle(fg, bg Attribute) tcell.Style {
|
||||
}
|
||||
st = st.Foreground(tcell.Color(f))
|
||||
st = st.Background(tcell.Color(b))
|
||||
if (fg & AttrBold != 0 ) || (bg & AttrBold != 0) {
|
||||
if (fg&AttrBold != 0) || (bg&AttrBold != 0) {
|
||||
st = st.Bold(true)
|
||||
}
|
||||
if (fg & AttrUnderline != 0 ) || (bg & AttrUnderline != 0) {
|
||||
if (fg&AttrUnderline != 0) || (bg&AttrUnderline != 0) {
|
||||
st = st.Underline(true)
|
||||
}
|
||||
if (fg & AttrReverse != 0 ) || (bg & AttrReverse != 0) {
|
||||
if (fg&AttrReverse != 0) || (bg&AttrReverse != 0) {
|
||||
st = st.Reverse(true)
|
||||
}
|
||||
return st
|
||||
@ -137,6 +138,7 @@ func Clear(fg, bg Attribute) {
|
||||
}
|
||||
|
||||
type InputMode int
|
||||
|
||||
const (
|
||||
InputCurrent InputMode = iota
|
||||
InputEsc
|
||||
@ -150,6 +152,7 @@ func SetInputMode(mode InputMode) InputMode {
|
||||
}
|
||||
|
||||
type OutputMode int
|
||||
|
||||
const (
|
||||
OutputCurrent OutputMode = iota
|
||||
OutputNormal
|
||||
@ -159,6 +162,9 @@ const (
|
||||
)
|
||||
|
||||
func SetOutputMode(mode OutputMode) OutputMode {
|
||||
if screen.Colors() < 256 {
|
||||
mode = OutputNormal
|
||||
}
|
||||
switch mode {
|
||||
case OutputCurrent:
|
||||
return outMode
|
||||
@ -185,16 +191,16 @@ type Modifier tcell.ModMask
|
||||
type Key tcell.Key
|
||||
|
||||
type Event struct {
|
||||
Type EventType
|
||||
Mod Modifier
|
||||
Key Key
|
||||
Ch rune
|
||||
Width int
|
||||
Height int
|
||||
Err error
|
||||
MouseX int
|
||||
MouseY int
|
||||
N int
|
||||
Type EventType
|
||||
Mod Modifier
|
||||
Key Key
|
||||
Ch rune
|
||||
Width int
|
||||
Height int
|
||||
Err error
|
||||
MouseX int
|
||||
MouseY int
|
||||
N int
|
||||
}
|
||||
|
||||
const (
|
||||
@ -208,68 +214,68 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
KeyF1 = Key(tcell.KeyF1)
|
||||
KeyF2 = Key(tcell.KeyF2)
|
||||
KeyF3 = Key(tcell.KeyF3)
|
||||
KeyF4 = Key(tcell.KeyF4)
|
||||
KeyF5 = Key(tcell.KeyF5)
|
||||
KeyF6 = Key(tcell.KeyF6)
|
||||
KeyF7 = Key(tcell.KeyF7)
|
||||
KeyF8 = Key(tcell.KeyF8)
|
||||
KeyF9 = Key(tcell.KeyF9)
|
||||
KeyF10 = Key(tcell.KeyF10)
|
||||
KeyF11 = Key(tcell.KeyF11)
|
||||
KeyF12 = Key(tcell.KeyF12)
|
||||
KeyInsert = Key(tcell.KeyInsert)
|
||||
KeyDelete = Key(tcell.KeyDelete)
|
||||
KeyHome = Key(tcell.KeyHome)
|
||||
KeyEnd = Key(tcell.KeyEnd)
|
||||
KeyArrowUp = Key(tcell.KeyUp)
|
||||
KeyArrowDown = Key(tcell.KeyDown)
|
||||
KeyF1 = Key(tcell.KeyF1)
|
||||
KeyF2 = Key(tcell.KeyF2)
|
||||
KeyF3 = Key(tcell.KeyF3)
|
||||
KeyF4 = Key(tcell.KeyF4)
|
||||
KeyF5 = Key(tcell.KeyF5)
|
||||
KeyF6 = Key(tcell.KeyF6)
|
||||
KeyF7 = Key(tcell.KeyF7)
|
||||
KeyF8 = Key(tcell.KeyF8)
|
||||
KeyF9 = Key(tcell.KeyF9)
|
||||
KeyF10 = Key(tcell.KeyF10)
|
||||
KeyF11 = Key(tcell.KeyF11)
|
||||
KeyF12 = Key(tcell.KeyF12)
|
||||
KeyInsert = Key(tcell.KeyInsert)
|
||||
KeyDelete = Key(tcell.KeyDelete)
|
||||
KeyHome = Key(tcell.KeyHome)
|
||||
KeyEnd = Key(tcell.KeyEnd)
|
||||
KeyArrowUp = Key(tcell.KeyUp)
|
||||
KeyArrowDown = Key(tcell.KeyDown)
|
||||
KeyArrowRight = Key(tcell.KeyRight)
|
||||
KeyArrowLeft = Key(tcell.KeyLeft)
|
||||
KeyCtrlA = Key(tcell.KeyCtrlA)
|
||||
KeyCtrlB = Key(tcell.KeyCtrlB)
|
||||
KeyCtrlC = Key(tcell.KeyCtrlC)
|
||||
KeyCtrlD = Key(tcell.KeyCtrlD)
|
||||
KeyCtrlE = Key(tcell.KeyCtrlE)
|
||||
KeyCtrlF = Key(tcell.KeyCtrlF)
|
||||
KeyCtrlG = Key(tcell.KeyCtrlG)
|
||||
KeyCtrlH = Key(tcell.KeyCtrlH)
|
||||
KeyCtrlI = Key(tcell.KeyCtrlI)
|
||||
KeyCtrlJ = Key(tcell.KeyCtrlJ)
|
||||
KeyCtrlK = Key(tcell.KeyCtrlK)
|
||||
KeyCtrlL = Key(tcell.KeyCtrlL)
|
||||
KeyCtrlM = Key(tcell.KeyCtrlM)
|
||||
KeyCtrlN = Key(tcell.KeyCtrlN)
|
||||
KeyCtrlO = Key(tcell.KeyCtrlO)
|
||||
KeyCtrlP = Key(tcell.KeyCtrlP)
|
||||
KeyCtrlQ = Key(tcell.KeyCtrlQ)
|
||||
KeyCtrlR = Key(tcell.KeyCtrlR)
|
||||
KeyCtrlS = Key(tcell.KeyCtrlS)
|
||||
KeyCtrlT = Key(tcell.KeyCtrlT)
|
||||
KeyCtrlU = Key(tcell.KeyCtrlU)
|
||||
KeyCtrlV = Key(tcell.KeyCtrlV)
|
||||
KeyCtrlW = Key(tcell.KeyCtrlW)
|
||||
KeyCtrlX = Key(tcell.KeyCtrlX)
|
||||
KeyCtrlY = Key(tcell.KeyCtrlY)
|
||||
KeyCtrlZ = Key(tcell.KeyCtrlZ)
|
||||
KeyBackspace = Key(tcell.KeyBackspace)
|
||||
KeyArrowLeft = Key(tcell.KeyLeft)
|
||||
KeyCtrlA = Key(tcell.KeyCtrlA)
|
||||
KeyCtrlB = Key(tcell.KeyCtrlB)
|
||||
KeyCtrlC = Key(tcell.KeyCtrlC)
|
||||
KeyCtrlD = Key(tcell.KeyCtrlD)
|
||||
KeyCtrlE = Key(tcell.KeyCtrlE)
|
||||
KeyCtrlF = Key(tcell.KeyCtrlF)
|
||||
KeyCtrlG = Key(tcell.KeyCtrlG)
|
||||
KeyCtrlH = Key(tcell.KeyCtrlH)
|
||||
KeyCtrlI = Key(tcell.KeyCtrlI)
|
||||
KeyCtrlJ = Key(tcell.KeyCtrlJ)
|
||||
KeyCtrlK = Key(tcell.KeyCtrlK)
|
||||
KeyCtrlL = Key(tcell.KeyCtrlL)
|
||||
KeyCtrlM = Key(tcell.KeyCtrlM)
|
||||
KeyCtrlN = Key(tcell.KeyCtrlN)
|
||||
KeyCtrlO = Key(tcell.KeyCtrlO)
|
||||
KeyCtrlP = Key(tcell.KeyCtrlP)
|
||||
KeyCtrlQ = Key(tcell.KeyCtrlQ)
|
||||
KeyCtrlR = Key(tcell.KeyCtrlR)
|
||||
KeyCtrlS = Key(tcell.KeyCtrlS)
|
||||
KeyCtrlT = Key(tcell.KeyCtrlT)
|
||||
KeyCtrlU = Key(tcell.KeyCtrlU)
|
||||
KeyCtrlV = Key(tcell.KeyCtrlV)
|
||||
KeyCtrlW = Key(tcell.KeyCtrlW)
|
||||
KeyCtrlX = Key(tcell.KeyCtrlX)
|
||||
KeyCtrlY = Key(tcell.KeyCtrlY)
|
||||
KeyCtrlZ = Key(tcell.KeyCtrlZ)
|
||||
KeyBackspace = Key(tcell.KeyBackspace)
|
||||
KeyBackspace2 = Key(tcell.KeyBackspace2)
|
||||
KeyTab = Key(tcell.KeyTab)
|
||||
KeyEnter = Key(tcell.KeyEnter)
|
||||
KeySpace = Key(tcell.KeySpace)
|
||||
KeyEsc = Key(tcell.KeyEscape)
|
||||
MouseLeft = Key(tcell.KeyF63) // arbitrary assignments
|
||||
MouseRight = Key(tcell.KeyF62)
|
||||
MouseMiddle = Key(tcell.KeyF61)
|
||||
KeyTab = Key(tcell.KeyTab)
|
||||
KeyEnter = Key(tcell.KeyEnter)
|
||||
KeySpace = Key(tcell.KeySpace)
|
||||
KeyEsc = Key(tcell.KeyEscape)
|
||||
MouseLeft = Key(tcell.KeyF63) // arbitrary assignments
|
||||
MouseRight = Key(tcell.KeyF62)
|
||||
MouseMiddle = Key(tcell.KeyF61)
|
||||
)
|
||||
|
||||
const (
|
||||
ModAlt = Modifier(tcell.ModAlt)
|
||||
)
|
||||
|
||||
func makeEvent(tev tcell.Event) Event {
|
||||
func makeEvent(tev tcell.Event) Event {
|
||||
switch tev := tev.(type) {
|
||||
case *tcell.EventInterrupt:
|
||||
return Event{Type: EventInterrupt}
|
||||
@ -282,9 +288,9 @@ func makeEvent(tev tcell.Event) Event {
|
||||
mod := tev.Mod()
|
||||
return Event{
|
||||
Type: EventKey,
|
||||
Key: Key(k),
|
||||
Ch: ch,
|
||||
Mod: Modifier(mod),
|
||||
Key: Key(k),
|
||||
Ch: ch,
|
||||
Mod: Modifier(mod),
|
||||
}
|
||||
default:
|
||||
return Event{Type: EventNone}
|
||||
|
50
terminfo.go
50
terminfo.go
@ -21,6 +21,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
@ -53,6 +54,7 @@ type Terminfo struct {
|
||||
SetCursor string `json:"cup,omitempty"` // cup
|
||||
CursorBack1 string `json:"cub1,omitempty"` // cub1
|
||||
CursorUp1 string `json:"cuu1,omitempty"` // cuu1
|
||||
PadChar string `json:"pad,omitempty"` // pad
|
||||
KeyBackspace string `json:"kbs,omitempty"` // kbs
|
||||
KeyF1 string `json:"kf1,omitempty"` // kf1
|
||||
KeyF2 string `json:"kf2,omitempty"` // kf2
|
||||
@ -408,6 +410,54 @@ func (t *Terminfo) TParm(s string, p ...int) string {
|
||||
return out.String()
|
||||
}
|
||||
|
||||
func (t *Terminfo) TPuts(w io.Writer, s string, baud int) {
|
||||
for {
|
||||
beg := strings.Index(s, "$<")
|
||||
if beg < 0 {
|
||||
// Most strings don't need padding, which is good news!
|
||||
io.WriteString(w, s)
|
||||
return
|
||||
}
|
||||
io.WriteString(w, s[:beg])
|
||||
s = s[beg+2:]
|
||||
end := strings.Index(s, ">")
|
||||
if end < 0 {
|
||||
// unterminated.. just emit bytes unadulterated
|
||||
io.WriteString(w, "$<" + s)
|
||||
return
|
||||
}
|
||||
val := s[:end]
|
||||
s = s[end+1:]
|
||||
padus := 0
|
||||
unit := 1000
|
||||
dot := false
|
||||
loop:
|
||||
for i := range val {
|
||||
switch val[i] {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
padus *= 10
|
||||
padus += int(val[i] - '0')
|
||||
if dot {
|
||||
unit *= 10
|
||||
}
|
||||
case '.':
|
||||
if !dot {
|
||||
dot = true
|
||||
} else {
|
||||
break loop
|
||||
}
|
||||
default:
|
||||
break loop
|
||||
}
|
||||
}
|
||||
cnt := int(((baud/8)*padus)/unit)
|
||||
for cnt > 0 {
|
||||
io.WriteString(w, t.PadChar)
|
||||
cnt--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TGoto returns a string suitable for addressing the cursor at the given
|
||||
// row and column. The origin 0, 0 is in the upper left corner of the screen.
|
||||
func (t *Terminfo) TGoto(col, row int) string {
|
||||
|
669
tscreen.go
669
tscreen.go
@ -21,8 +21,6 @@ import (
|
||||
"strconv"
|
||||
"sync"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/mattn/go-runewidth"
|
||||
)
|
||||
|
||||
func NewTerminfoScreen() (Screen, error) {
|
||||
@ -58,37 +56,49 @@ type tScreen struct {
|
||||
h int
|
||||
in *os.File
|
||||
out *os.File
|
||||
cinvis bool
|
||||
curstyle Style
|
||||
style Style
|
||||
evch chan Event
|
||||
sigwinch chan os.Signal
|
||||
quit chan struct{}
|
||||
indoneq chan struct{}
|
||||
keys map[Key][]byte
|
||||
cx int
|
||||
cy int
|
||||
mouse []byte
|
||||
cells []Cell
|
||||
clear bool
|
||||
cursorx int
|
||||
cursory int
|
||||
tiosp *termiosPrivate
|
||||
baud int
|
||||
wasbtn bool
|
||||
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func (t *tScreen) Init() error {
|
||||
t.evch = make(chan Event, 2)
|
||||
t.indoneq = make(chan struct{})
|
||||
if e := t.termioInit(); e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
out := t.out
|
||||
ti := t.ti
|
||||
|
||||
io.WriteString(out, ti.EnterCA)
|
||||
io.WriteString(out, ti.EnterKeypad)
|
||||
io.WriteString(out, ti.HideCursor)
|
||||
io.WriteString(out, ti.Clear)
|
||||
t.TPuts(ti.EnterCA)
|
||||
t.TPuts(ti.EnterKeypad)
|
||||
t.TPuts(ti.HideCursor)
|
||||
t.TPuts(ti.Clear)
|
||||
|
||||
t.quit = make(chan struct{})
|
||||
t.cx = -1
|
||||
t.cy = -1
|
||||
t.style = StyleDefault
|
||||
|
||||
t.cells = ResizeCells(nil, 0, 0, t.w, t.h)
|
||||
t.cursorx = -1
|
||||
t.cursory = -1
|
||||
go t.inputLoop()
|
||||
|
||||
return nil
|
||||
@ -138,129 +148,139 @@ func (t *tScreen) prepareKeys() {
|
||||
|
||||
func (t *tScreen) Fini() {
|
||||
ti := t.ti
|
||||
out := t.out
|
||||
io.WriteString(out, ti.ShowCursor)
|
||||
io.WriteString(out, ti.AttrOff)
|
||||
io.WriteString(out, ti.Clear)
|
||||
io.WriteString(out, ti.ExitCA)
|
||||
io.WriteString(out, ti.ExitKeypad)
|
||||
io.WriteString(out, ti.ExitMouse)
|
||||
if t.quit != nil {
|
||||
close(t.quit)
|
||||
}
|
||||
t.TPuts(ti.ShowCursor)
|
||||
t.TPuts(ti.AttrOff)
|
||||
t.TPuts(ti.Clear)
|
||||
t.TPuts(ti.ExitCA)
|
||||
t.TPuts(ti.ExitKeypad)
|
||||
t.TPuts(ti.ExitMouse)
|
||||
|
||||
t.w = 0
|
||||
t.h = 0
|
||||
t.cells = nil
|
||||
t.curstyle = Style(-1)
|
||||
t.cinvis = false
|
||||
if t.quit != nil {
|
||||
close(t.quit)
|
||||
} else {
|
||||
t.termioFini()
|
||||
}
|
||||
t.clear = false
|
||||
t.termioFini()
|
||||
}
|
||||
|
||||
func (t *tScreen) SetStyle(style Style) {
|
||||
t.Lock()
|
||||
t.style = style
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *tScreen) Clear() {
|
||||
return
|
||||
|
||||
t.Lock()
|
||||
t.curstyle = Style(-1)
|
||||
t.cx = -1
|
||||
t.cy = -1
|
||||
io.WriteString(t.out, t.ti.Clear)
|
||||
ClearCells(t.cells, t.style)
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *tScreen) SetCell(x, y int, style Style, ch ...rune) {
|
||||
// XXX: this would be a place to check for hazeltine not being able
|
||||
// to display ~, or possibly non-UTF-8 locales, etc.
|
||||
|
||||
t.Lock()
|
||||
if x < 0 || y < 0 || x >= t.w || y >= t.h {
|
||||
t.Unlock()
|
||||
return
|
||||
}
|
||||
cell := &t.cells[(y*t.w)+x]
|
||||
cell.SetCell(ch, style)
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *tScreen) PutCell(x, y int, cell *Cell) {
|
||||
t.Lock()
|
||||
if x < 0 || y < 0 || x >= t.w || y >= t.h {
|
||||
t.Unlock()
|
||||
return
|
||||
}
|
||||
cp := &t.cells[(y*t.w)+x]
|
||||
cp.PutStyle(cell.Style)
|
||||
cp.PutChars(cell.Ch)
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *tScreen) GetCell(x, y int) *Cell {
|
||||
t.Lock()
|
||||
if x < 0 || y < 0 || x >= t.w || y >= t.h {
|
||||
t.Unlock()
|
||||
return nil
|
||||
}
|
||||
cell := t.cells[(y*t.w)+x]
|
||||
t.Unlock()
|
||||
return &cell
|
||||
}
|
||||
|
||||
func (t *tScreen) drawCell(x, y int, cell *Cell) {
|
||||
// XXX: this would be a place to check for hazeltine not being able
|
||||
// to display ~, or possibly non-UTF-8 locales, etc.
|
||||
|
||||
ti := t.ti
|
||||
|
||||
if t.cy != y || t.cx != x {
|
||||
io.WriteString(t.out, ti.TGoto(x, y))
|
||||
t.TPuts(ti.TGoto(x, y))
|
||||
}
|
||||
style := cell.Style
|
||||
if style == StyleDefault {
|
||||
style = t.style
|
||||
}
|
||||
if style != t.curstyle {
|
||||
fg, bg, attrs := style.Decompose()
|
||||
|
||||
io.WriteString(t.out, ti.AttrOff)
|
||||
t.TPuts(ti.AttrOff)
|
||||
if attrs&AttrBold != 0 {
|
||||
io.WriteString(t.out, ti.Bold)
|
||||
t.TPuts(ti.Bold)
|
||||
}
|
||||
if attrs&AttrUnderline != 0 {
|
||||
io.WriteString(t.out, ti.Underline)
|
||||
t.TPuts(ti.Underline)
|
||||
}
|
||||
if attrs&AttrReverse != 0 {
|
||||
io.WriteString(t.out, ti.Reverse)
|
||||
t.TPuts(ti.Reverse)
|
||||
}
|
||||
if attrs&AttrBlink != 0 {
|
||||
io.WriteString(t.out, ti.Blink)
|
||||
t.TPuts(ti.Blink)
|
||||
}
|
||||
if attrs&AttrDim != 0 {
|
||||
io.WriteString(t.out, ti.Dim)
|
||||
t.TPuts(ti.Dim)
|
||||
}
|
||||
if fg != ColorDefault {
|
||||
c := int(fg) - 1
|
||||
io.WriteString(t.out, ti.TParm(ti.SetFg, c))
|
||||
t.TPuts(ti.TParm(ti.SetFg, c))
|
||||
}
|
||||
if bg != ColorDefault {
|
||||
c := int(bg) - 1
|
||||
io.WriteString(t.out, ti.TParm(ti.SetBg, c))
|
||||
t.TPuts(ti.TParm(ti.SetBg, c))
|
||||
}
|
||||
t.curstyle = style
|
||||
}
|
||||
// now emit a character - taking care to not overrun width with a
|
||||
// now emit runes - taking care to not overrun width with a
|
||||
// wide character, and to ensure that we emit exactly one regular
|
||||
// character followed up by any residual combing characters
|
||||
mainc := ' '
|
||||
combc := ""
|
||||
width := 1
|
||||
|
||||
for _, c := range ch {
|
||||
if c < ' ' {
|
||||
// no control charcters allowed
|
||||
continue
|
||||
}
|
||||
switch runewidth.RuneWidth(c) {
|
||||
case 0:
|
||||
combc = combc + string(c)
|
||||
case 1:
|
||||
mainc = c
|
||||
width = 1
|
||||
case 2:
|
||||
mainc = c
|
||||
width = 2
|
||||
if x >= t.w-1 {
|
||||
// too wide to fit; emit space instead
|
||||
mainc = ' '
|
||||
width = 1
|
||||
}
|
||||
}
|
||||
width := int(cell.Width)
|
||||
var str string
|
||||
if len(cell.Ch) == 0 {
|
||||
str = " "
|
||||
} else {
|
||||
str = string(cell.Ch)
|
||||
}
|
||||
io.WriteString(t.out, string(mainc))
|
||||
io.WriteString(t.out, combc)
|
||||
if width == 2 && x >= t.w-1 {
|
||||
// too wide to fit; emit space instead
|
||||
width = 1
|
||||
str = " "
|
||||
}
|
||||
io.WriteString(t.out, str)
|
||||
t.cy = y
|
||||
t.cx = x + width
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *tScreen) ShowCursor(x, y int) {
|
||||
t.Lock()
|
||||
if x < 0 || y < 0 || x >= t.w || y >= t.h {
|
||||
t.cinvis = true
|
||||
io.WriteString(t.out, t.ti.HideCursor)
|
||||
t.Unlock()
|
||||
return
|
||||
}
|
||||
if t.cx != x || t.cy != y {
|
||||
io.WriteString(t.out, t.ti.TGoto(x, y))
|
||||
}
|
||||
io.WriteString(t.out, t.ti.ShowCursor)
|
||||
|
||||
t.cinvis = false
|
||||
t.cx = x
|
||||
t.cy = y
|
||||
t.cursorx = x
|
||||
t.cursory = y
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
@ -268,20 +288,82 @@ func (t *tScreen) HideCursor() {
|
||||
t.ShowCursor(-1, -1)
|
||||
}
|
||||
|
||||
func (t *tScreen) showCursor() {
|
||||
|
||||
x, y := t.cursorx, t.cursory
|
||||
if x < 0 || y < 0 || x >= t.w || y >= t.h {
|
||||
t.TPuts(t.ti.HideCursor)
|
||||
return
|
||||
}
|
||||
if t.cx != x || t.cy != y {
|
||||
t.TPuts(t.ti.TGoto(x, y))
|
||||
}
|
||||
t.TPuts(t.ti.ShowCursor)
|
||||
t.cx = x
|
||||
t.cy = y
|
||||
}
|
||||
|
||||
func (t *tScreen) TPuts(s string) {
|
||||
t.ti.TPuts(t.out, s, t.baud)
|
||||
}
|
||||
|
||||
func (t *tScreen) Show() {
|
||||
t.Lock()
|
||||
t.resize()
|
||||
t.draw()
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *tScreen) clearScreen() {
|
||||
t.TPuts(t.ti.Clear)
|
||||
t.clear = false
|
||||
}
|
||||
|
||||
func (t *tScreen) hideCursor() {
|
||||
// does not update cursor position
|
||||
t.TPuts(t.ti.HideCursor)
|
||||
}
|
||||
|
||||
func (t *tScreen) draw() {
|
||||
// clobber cursor position, because we're gonna change it all
|
||||
t.cx = -1
|
||||
t.cy = -1
|
||||
|
||||
// hide the cursor while we move stuff around
|
||||
t.hideCursor()
|
||||
|
||||
if t.clear {
|
||||
t.clearScreen()
|
||||
}
|
||||
|
||||
for row := 0; row < t.h; row++ {
|
||||
for col := 0; col < t.w; col++ {
|
||||
cell := &t.cells[(row*t.w)+col]
|
||||
if !cell.Dirty {
|
||||
continue
|
||||
}
|
||||
t.drawCell(col, row, cell)
|
||||
cell.Dirty = false
|
||||
}
|
||||
}
|
||||
|
||||
// restore the cursor
|
||||
t.showCursor()
|
||||
}
|
||||
|
||||
func (t *tScreen) EnableMouse() {
|
||||
if len(t.mouse) != 0 {
|
||||
io.WriteString(t.out, t.ti.EnterMouse)
|
||||
t.TPuts(t.ti.EnterMouse)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tScreen) DisableMouse() {
|
||||
if len(t.mouse) != 0 {
|
||||
io.WriteString(t.out, t.ti.ExitMouse)
|
||||
t.TPuts(t.ti.ExitMouse)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tScreen) Size() (int, int) {
|
||||
// XXX: get underlying size
|
||||
t.Lock()
|
||||
w, h := t.w, t.h
|
||||
t.Unlock()
|
||||
@ -290,17 +372,17 @@ func (t *tScreen) Size() (int, int) {
|
||||
|
||||
func (t *tScreen) resize() {
|
||||
var ev Event
|
||||
t.Lock()
|
||||
if w, h, e := t.getWinSize(); e == nil {
|
||||
if w != t.w || h != t.h {
|
||||
ev = NewEventResize(w, h)
|
||||
t.w = w
|
||||
t.h = h
|
||||
t.cx = -1
|
||||
t.cy = -1
|
||||
|
||||
t.cells = ResizeCells(t.cells, t.w, t.h, w, h)
|
||||
t.w = w
|
||||
t.h = h
|
||||
}
|
||||
}
|
||||
t.Unlock()
|
||||
if ev != nil {
|
||||
t.PostEvent(ev)
|
||||
}
|
||||
@ -324,6 +406,282 @@ func (t *tScreen) PostEvent(ev Event) {
|
||||
t.evch <- ev
|
||||
}
|
||||
|
||||
|
||||
func (t *tScreen) postMouseEvent(x, y, btn int) {
|
||||
|
||||
// XTerm mouse events only report at most one button at a time,
|
||||
// which may include a wheel button. Wheel motion events are
|
||||
// reported as single impulses, while other button events are reported
|
||||
// as separate press & release events.
|
||||
|
||||
button := ButtonNone
|
||||
mod := ModNone
|
||||
|
||||
// Mouse wheel has bit 6 set, no release events. It should be noted
|
||||
// that wheel events are sometimes misdelivered as mouse button events
|
||||
// during a click-drag, so we debounce these, considering them to be
|
||||
// button press events unless we see an intervening release event.
|
||||
switch btn & 0x43 {
|
||||
case 0:
|
||||
button = Button1
|
||||
t.wasbtn = true
|
||||
case 1:
|
||||
button = Button2
|
||||
t.wasbtn = true
|
||||
case 2:
|
||||
button = Button3
|
||||
t.wasbtn = true
|
||||
case 3:
|
||||
button = ButtonNone
|
||||
t.wasbtn = false
|
||||
case 0x40:
|
||||
if !t.wasbtn {
|
||||
button = WheelUp
|
||||
} else {
|
||||
button = Button1
|
||||
}
|
||||
case 0x41:
|
||||
if !t.wasbtn {
|
||||
button = WheelDown
|
||||
} else {
|
||||
button = Button2
|
||||
}
|
||||
}
|
||||
|
||||
if btn & 0x4 != 0 {
|
||||
mod |= ModShift
|
||||
}
|
||||
if btn & 0x8 != 0 {
|
||||
mod |= ModMeta
|
||||
}
|
||||
if btn & 0x10 != 0 {
|
||||
mod |= ModCtrl
|
||||
}
|
||||
|
||||
// Some terminals will report mouse coordinates outside the
|
||||
// screen, especially with click-drag events. Clip the coordinates
|
||||
// to the screen in that case.
|
||||
if x < 0 {
|
||||
x = 0
|
||||
}
|
||||
if x > t.w-1 {
|
||||
x = t.w-1
|
||||
}
|
||||
if y < 0 {
|
||||
y = 0
|
||||
}
|
||||
if y > t.h-1 {
|
||||
y = t.h-1
|
||||
}
|
||||
ev := NewEventMouse(x, y, button, mod)
|
||||
t.PostEvent(ev)
|
||||
}
|
||||
|
||||
// parseSgrMouse attempts to locate an SGR mouse record at the start of the
|
||||
// buffer. It returns true, true if it found one, and the associated bytes
|
||||
// be removed from the buffer. It returns true, false if the buffer might
|
||||
// contain such an event, but more bytes are necessary (partial match), and
|
||||
// false, false if the content is definitely *not* an SGR mouse record.
|
||||
func (t *tScreen) parseSgrMouse(buf *bytes.Buffer) (bool, bool) {
|
||||
|
||||
b := buf.Bytes()
|
||||
|
||||
var x, y, btn, state int
|
||||
dig := false
|
||||
neg := false
|
||||
i := 0
|
||||
val := 0
|
||||
|
||||
for i = range b {
|
||||
switch b[i] {
|
||||
case '\x1b':
|
||||
if state != 0 {
|
||||
return false, false
|
||||
}
|
||||
state = 1
|
||||
|
||||
case '\x9b':
|
||||
if state != 0 {
|
||||
return false, false
|
||||
}
|
||||
state = 2
|
||||
|
||||
case '[':
|
||||
if state != 1 {
|
||||
return false, false
|
||||
}
|
||||
state = 2
|
||||
|
||||
case '<':
|
||||
if state != 2 {
|
||||
return false, false
|
||||
}
|
||||
val = 0
|
||||
dig = false
|
||||
neg = false
|
||||
state = 3
|
||||
|
||||
case '-':
|
||||
if state != 3 || state != 4 || state != 5 {
|
||||
return false, false
|
||||
}
|
||||
if dig || neg {
|
||||
return false, false
|
||||
}
|
||||
neg = true // stay in state
|
||||
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
if state != 3 && state != 4 && state != 5 {
|
||||
return false, false
|
||||
}
|
||||
val *= 10
|
||||
val += int(b[i]-'0')
|
||||
dig = true // stay in state
|
||||
|
||||
case ';':
|
||||
if neg {
|
||||
val = -val
|
||||
}
|
||||
switch state {
|
||||
case 3:
|
||||
btn, val = val, 0
|
||||
neg, dig, state = false, false, 4
|
||||
case 4:
|
||||
x, val = val, 0
|
||||
neg, dig, state = false, false, 5
|
||||
default:
|
||||
return false, false
|
||||
}
|
||||
|
||||
case 'm', 'M':
|
||||
if state != 5 {
|
||||
return false, false
|
||||
}
|
||||
if neg {
|
||||
val = -val
|
||||
}
|
||||
y = val
|
||||
|
||||
// We don't care about the motion bit
|
||||
btn &^= 32
|
||||
if b[i] == 'm' {
|
||||
// mouse release, clear all buttons
|
||||
btn |= 3
|
||||
btn &^= 0x40
|
||||
}
|
||||
// consume the event bytes
|
||||
for i >= 0 {
|
||||
buf.ReadByte()
|
||||
i--
|
||||
}
|
||||
t.postMouseEvent(x, y, btn)
|
||||
return true, true
|
||||
}
|
||||
}
|
||||
|
||||
// incomplete & inconclusve at this point
|
||||
return true, false
|
||||
}
|
||||
|
||||
// parseXtermMouse is like parseSgrMouse, but it parses a legacy
|
||||
// X11 mouse record.
|
||||
func (t *tScreen) parseXtermMouse(buf *bytes.Buffer) (bool, bool) {
|
||||
|
||||
b := buf.Bytes()
|
||||
|
||||
state := 0
|
||||
btn := 0
|
||||
x := 0
|
||||
y := 0
|
||||
|
||||
for i := range b {
|
||||
switch state {
|
||||
case 0:
|
||||
switch b[i] {
|
||||
case '\x1b':
|
||||
state = 1
|
||||
case '\x9b':
|
||||
state = 2
|
||||
default:
|
||||
return false, false
|
||||
}
|
||||
case 1:
|
||||
if b[i] != '[' {
|
||||
return false, false
|
||||
}
|
||||
state = 2
|
||||
case 2:
|
||||
if b[i] != 'M' {
|
||||
return false, false
|
||||
}
|
||||
state++
|
||||
case 3:
|
||||
btn = int(b[i])
|
||||
state++
|
||||
case 4:
|
||||
x = int(b[i]) - 32 - 1
|
||||
state++
|
||||
case 5:
|
||||
y = int(b[i]) - 32 - 1
|
||||
for i >= 0 {
|
||||
buf.ReadByte()
|
||||
i--
|
||||
}
|
||||
t.postMouseEvent(x, y, btn)
|
||||
return true, true
|
||||
}
|
||||
}
|
||||
return true, false
|
||||
}
|
||||
|
||||
func (t *tScreen) parseFunctionKey(buf *bytes.Buffer) (bool, bool) {
|
||||
b := buf.Bytes()
|
||||
partial := false
|
||||
for k, esc := range t.keys {
|
||||
if bytes.HasPrefix(b, esc) {
|
||||
// matched
|
||||
var r rune
|
||||
if len(esc) == 1 {
|
||||
r = rune(b[0])
|
||||
}
|
||||
ev := NewEventKey(k, r, ModNone)
|
||||
t.PostEvent(ev)
|
||||
for i := 0; i < len(esc); i++ {
|
||||
buf.ReadByte()
|
||||
}
|
||||
return true, true
|
||||
}
|
||||
if bytes.HasPrefix(esc, b) {
|
||||
partial = true
|
||||
}
|
||||
}
|
||||
return partial, false
|
||||
}
|
||||
|
||||
func (t *tScreen) parseRune(buf *bytes.Buffer) (bool, bool) {
|
||||
b := buf.Bytes()
|
||||
if b[0] >= ' ' && b[0] <= 0x7F {
|
||||
// printable ASCII easy to deal with -- no encodings
|
||||
buf.ReadByte()
|
||||
ev := NewEventKey(KeyRune, rune(b[0]), ModNone)
|
||||
t.PostEvent(ev)
|
||||
return true, true
|
||||
}
|
||||
if b[0] >= 0x80 {
|
||||
if utf8.FullRune(b) {
|
||||
r, _, e := buf.ReadRune()
|
||||
if e == nil {
|
||||
ev := NewEventKey(KeyRune, r, ModNone)
|
||||
t.PostEvent(ev)
|
||||
return true, true
|
||||
}
|
||||
}
|
||||
// Looks like potential escape
|
||||
return true, false
|
||||
}
|
||||
return false, false
|
||||
}
|
||||
|
||||
func (t *tScreen) scanInput(buf *bytes.Buffer, expire bool) {
|
||||
|
||||
for {
|
||||
@ -332,108 +690,46 @@ func (t *tScreen) scanInput(buf *bytes.Buffer, expire bool) {
|
||||
buf.Reset()
|
||||
return
|
||||
}
|
||||
if b[0] >= ' ' && b[0] <= 0x7F {
|
||||
// printable ASCII easy to deal with -- no encodings
|
||||
buf.ReadByte()
|
||||
ev := NewEventKey(KeyRune, rune(b[0]), ModNone)
|
||||
t.PostEvent(ev)
|
||||
continue
|
||||
}
|
||||
// We assume that the first character of any terminal escape
|
||||
// sequence will be in ASCII -- most often (by far) it is ESC.
|
||||
if b[0] >= 0x80 && utf8.FullRune(b) {
|
||||
r, _, e := buf.ReadRune()
|
||||
if e == nil {
|
||||
ev := NewEventKey(KeyRune, r, ModNone)
|
||||
t.PostEvent(ev)
|
||||
continue
|
||||
}
|
||||
}
|
||||
// Now check the codes we know about
|
||||
|
||||
partials := 0
|
||||
matched := false
|
||||
for k, esc := range t.keys {
|
||||
if bytes.HasPrefix(b, esc) {
|
||||
// matched
|
||||
var r rune
|
||||
if len(esc) == 1 {
|
||||
r = rune(b[0])
|
||||
}
|
||||
ev := NewEventKey(k, r, ModNone)
|
||||
t.PostEvent(ev)
|
||||
matched = true
|
||||
for i := 0; i < len(esc); i++ {
|
||||
buf.ReadByte()
|
||||
}
|
||||
break
|
||||
}
|
||||
if bytes.HasPrefix(esc, b) {
|
||||
partials++
|
||||
}
|
||||
}
|
||||
|
||||
// Mouse events are special, as they carry parameters
|
||||
if !matched && len(t.mouse) != 0 &&
|
||||
bytes.HasPrefix(b, t.mouse) {
|
||||
|
||||
if len(b) >= len(t.mouse)+3 {
|
||||
// mouse record
|
||||
b = b[len(t.mouse):]
|
||||
btns := ButtonNone
|
||||
mod := ModNone
|
||||
switch b[0] & 3 {
|
||||
case 0:
|
||||
btns = Button1
|
||||
case 1:
|
||||
btns = Button2
|
||||
case 2:
|
||||
btns = Button3
|
||||
case 3:
|
||||
btns = 0
|
||||
}
|
||||
if b[0]&4 != 0 {
|
||||
mod |= ModShift
|
||||
}
|
||||
if b[0]&8 != 0 {
|
||||
mod |= ModMeta
|
||||
}
|
||||
if b[0]&16 != 0 {
|
||||
mod |= ModCtrl
|
||||
}
|
||||
x := int(b[1]) - 33
|
||||
y := int(b[2]) - 33
|
||||
for i := 0; i < len(t.mouse)+3; i++ {
|
||||
buf.ReadByte()
|
||||
}
|
||||
matched = true
|
||||
ev := NewEventMouse(x, y, btns, mod)
|
||||
t.PostEvent(ev)
|
||||
continue
|
||||
} else {
|
||||
partials++
|
||||
}
|
||||
|
||||
} else {
|
||||
if part, comp := t.parseRune(buf); comp {
|
||||
continue
|
||||
} else if part {
|
||||
partials++
|
||||
}
|
||||
|
||||
// if we expired, we implicitly fail matches
|
||||
if expire {
|
||||
partials = 0
|
||||
}
|
||||
// If we had no partial matches, just send first character as
|
||||
// a rune. Others might still work.
|
||||
if partials == 0 && !matched {
|
||||
ev := NewEventKey(KeyRune, rune(b[0]), ModNone)
|
||||
t.PostEvent(ev)
|
||||
buf.ReadByte()
|
||||
if part, comp := t.parseFunctionKey(buf); comp {
|
||||
continue
|
||||
} else if part {
|
||||
partials++
|
||||
}
|
||||
|
||||
if partials > 0 {
|
||||
// We had one or more partial matches, wait for more
|
||||
// data.
|
||||
return
|
||||
if part, comp := t.parseXtermMouse(buf); comp {
|
||||
continue
|
||||
} else if part {
|
||||
partials++
|
||||
}
|
||||
|
||||
if part, comp := t.parseSgrMouse(buf); comp {
|
||||
continue
|
||||
} else if part {
|
||||
partials++
|
||||
}
|
||||
|
||||
if partials == 0 || expire {
|
||||
// Nothing was going to match, or we timed out
|
||||
// waiting for more data -- just deliver the characters
|
||||
// to the app & let them sort it out.
|
||||
by, _ := buf.ReadByte()
|
||||
ev := NewEventKey(KeyRune, rune(by), ModNone)
|
||||
t.PostEvent(ev)
|
||||
continue
|
||||
}
|
||||
|
||||
// well we have some partial data, wait until we get
|
||||
// some more
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@ -443,10 +739,12 @@ func (t *tScreen) inputLoop() {
|
||||
for {
|
||||
select {
|
||||
case <-t.quit:
|
||||
t.termioFini()
|
||||
close(t.indoneq)
|
||||
return
|
||||
case <-t.sigwinch:
|
||||
t.Lock()
|
||||
t.resize()
|
||||
t.Unlock()
|
||||
continue
|
||||
default:
|
||||
}
|
||||
@ -462,7 +760,7 @@ func (t *tScreen) inputLoop() {
|
||||
continue
|
||||
case nil:
|
||||
default:
|
||||
// XXX: post error event?
|
||||
close(t.indoneq)
|
||||
return
|
||||
}
|
||||
buf.Write(chunk[:n])
|
||||
@ -470,3 +768,12 @@ func (t *tScreen) inputLoop() {
|
||||
t.scanInput(buf, false)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tScreen) Sync() {
|
||||
t.Lock()
|
||||
t.resize()
|
||||
t.clear = true
|
||||
InvalidateCells(t.cells)
|
||||
t.draw()
|
||||
t.Unlock()
|
||||
}
|
||||
|
102
tscreen_posix.go
102
tscreen_posix.go
@ -38,18 +38,93 @@ import (
|
||||
// return (-1);
|
||||
// #endif
|
||||
// }
|
||||
//
|
||||
// int getbaud(struct termios *tios) {
|
||||
// switch (cfgetospeed(tios)) {
|
||||
// #ifdef B0
|
||||
// case B0: return (0);
|
||||
// #endif
|
||||
// #ifdef B50
|
||||
// case B50: return (50);
|
||||
// #endif
|
||||
// #ifdef B75
|
||||
// case B75: return (75);
|
||||
// #endif
|
||||
// #ifdef B110
|
||||
// case B110: return (110);
|
||||
// #endif
|
||||
// #ifdef B134
|
||||
// case B134: return (134);
|
||||
// #endif
|
||||
// #ifdef B150
|
||||
// case B150: return (150);
|
||||
// #endif
|
||||
// #ifdef B200
|
||||
// case B200: return (200);
|
||||
// #endif
|
||||
// #ifdef B300
|
||||
// case B300: return (300);
|
||||
// #endif
|
||||
// #ifdef B600
|
||||
// case B600: return (600);
|
||||
// #endif
|
||||
// #ifdef B1200
|
||||
// case B1200: return (1200);
|
||||
// #endif
|
||||
// #ifdef B1800
|
||||
// case B1800: return (1800);
|
||||
// #endif
|
||||
// #ifdef B2400
|
||||
// case B2400: return (2400);
|
||||
// #endif
|
||||
// #ifdef B4800
|
||||
// case B4800: return (4800);
|
||||
// #endif
|
||||
// #ifdef B9600
|
||||
// case B9600: return (9600);
|
||||
// #endif
|
||||
// #ifdef B19200
|
||||
// case B19200: return (19200);
|
||||
// #endif
|
||||
// #ifdef B38400
|
||||
// case B38400: return (38400);
|
||||
// #endif
|
||||
// #ifdef B57600
|
||||
// case B57600: return (57600);
|
||||
// #endif
|
||||
// #ifdef B76800
|
||||
// case B76800: return (76800);
|
||||
// #endif
|
||||
// #ifdef B115200
|
||||
// case B115200: return (115200);
|
||||
// #endif
|
||||
// #ifdef B153600
|
||||
// case B153600: return (153600);
|
||||
// #endif
|
||||
// #ifdef B230400
|
||||
// case B230400: return (230400);
|
||||
// #endif
|
||||
// #ifdef B307200
|
||||
// case B307200: return (307200);
|
||||
// #endif
|
||||
// #ifdef B460800
|
||||
// case B460800: return (460800);
|
||||
// #endif
|
||||
// #ifdef B921600
|
||||
// case B921600: return (921600);
|
||||
// #endif
|
||||
// }
|
||||
// return (0);
|
||||
// }
|
||||
import "C"
|
||||
|
||||
var savedtios map[*tScreen]*C.struct_termios
|
||||
|
||||
func init() {
|
||||
savedtios = make(map[*tScreen]*C.struct_termios)
|
||||
type termiosPrivate struct {
|
||||
tios C.struct_termios
|
||||
}
|
||||
|
||||
func (t *tScreen) termioInit() error {
|
||||
var e error
|
||||
var rv C.int
|
||||
var oldtios C.struct_termios
|
||||
var newtios C.struct_termios
|
||||
var fd C.int
|
||||
|
||||
@ -60,11 +135,14 @@ func (t *tScreen) termioInit() error {
|
||||
goto failed
|
||||
}
|
||||
|
||||
t.tiosp = &termiosPrivate{}
|
||||
|
||||
fd = C.int(t.out.Fd())
|
||||
if rv, e = C.tcgetattr(fd, &oldtios); rv != 0 {
|
||||
if rv, e = C.tcgetattr(fd, &t.tiosp.tios); rv != 0 {
|
||||
goto failed
|
||||
}
|
||||
newtios = oldtios
|
||||
t.baud = int(C.getbaud(&t.tiosp.tios))
|
||||
newtios = t.tiosp.tios
|
||||
newtios.c_iflag &^= C.IGNBRK | C.BRKINT | C.PARMRK |
|
||||
C.ISTRIP | C.INLCR | C.IGNCR |
|
||||
C.ICRNL | C.IXON
|
||||
@ -86,7 +164,6 @@ func (t *tScreen) termioInit() error {
|
||||
goto failed
|
||||
}
|
||||
|
||||
savedtios[t] = &oldtios
|
||||
signal.Notify(t.sigwinch, syscall.SIGWINCH)
|
||||
|
||||
if w, h, e := t.getWinSize(); e == nil && w != 0 && h != 0 {
|
||||
@ -110,12 +187,11 @@ func (t *tScreen) termioFini() {
|
||||
|
||||
signal.Stop(t.sigwinch)
|
||||
|
||||
<-t.indoneq
|
||||
|
||||
if t.out != nil {
|
||||
if oldtios, ok := savedtios[t]; ok {
|
||||
fd := C.int(t.out.Fd())
|
||||
C.tcsetattr(fd, C.TCSANOW, oldtios)
|
||||
delete(savedtios, t)
|
||||
}
|
||||
fd := C.int(t.out.Fd())
|
||||
C.tcsetattr(fd, C.TCSANOW|C.TCSAFLUSH, &t.tiosp.tios)
|
||||
t.out.Close()
|
||||
}
|
||||
if t.in != nil {
|
||||
|
@ -37,3 +37,5 @@ func (t *tScreen) termioFini() {
|
||||
func (t *tScreen) getWinSize() (int, int, error) {
|
||||
return 0, 0, errors.New("no temrios on Windows")
|
||||
}
|
||||
|
||||
type termiosPrivate struct{}
|
||||
|
Loading…
x
Reference in New Issue
Block a user