mirror of
https://github.com/gdamore/tcell.git
synced 2025-04-24 13:48:51 +08:00
fixes #37 Improve docs & fix golint
fixes #38 Broke wide characters in last update fixes #39 Clean up logic for encodings, enhance fallback options fixes #36 Support more mouse buttons on Windows.
This commit is contained in:
parent
02eef725e2
commit
7322e40c26
@ -24,14 +24,23 @@ import (
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/gdamore/tcell/encoding"
|
||||
|
||||
"github.com/mattn/go-runewidth"
|
||||
)
|
||||
|
||||
var defStyle tcell.Style
|
||||
|
||||
func emitStr(s tcell.Screen, x, y int, style tcell.Style, str string) {
|
||||
for _, c := range str {
|
||||
s.SetContent(x, y, c, nil, style)
|
||||
x++
|
||||
var comb []rune
|
||||
w := runewidth.RuneWidth(c)
|
||||
if w == 0 {
|
||||
comb = []rune{c}
|
||||
c = ' '
|
||||
w = 1
|
||||
}
|
||||
s.SetContent(x, y, c, comb, style)
|
||||
x += w
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,7 +90,7 @@ func drawSelect(s tcell.Screen, x1, y1, x2, y2 int, sel bool) {
|
||||
}
|
||||
style = style.Reverse(sel)
|
||||
s.SetContent(col, row, mainc, combc, style)
|
||||
col += width-1
|
||||
col += width - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
91
ascii.go
Normal file
91
ascii.go
Normal file
@ -0,0 +1,91 @@
|
||||
// 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 (
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
type ascii struct{ transform.NopResetter }
|
||||
type asciiDecoder struct{ transform.NopResetter }
|
||||
type asciiEncoder struct{ transform.NopResetter }
|
||||
|
||||
// ASCII represents an basic 7-bit ASCII scheme. It decodes directly to UTF-8
|
||||
// without change, as all ASCII values are legal UTF-8. It encodes any UTF-8
|
||||
// runes outside of ASCII to 0x1A, the ASCII substitution character.
|
||||
var ASCII encoding.Encoding = ascii{}
|
||||
|
||||
func (ascii) NewDecoder() transform.Transformer {
|
||||
return asciiDecoder{}
|
||||
}
|
||||
|
||||
func (ascii) NewEncoder() transform.Transformer {
|
||||
return asciiEncoder{}
|
||||
}
|
||||
|
||||
func (asciiDecoder) Transform(dst, src []byte, atEOF bool) (int, int, error) {
|
||||
var e error
|
||||
var ndst, nsrc int
|
||||
for _, c := range src {
|
||||
if ndst >= len(dst) {
|
||||
e = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
dst[ndst] = c
|
||||
ndst++
|
||||
nsrc++
|
||||
}
|
||||
return ndst, nsrc, e
|
||||
}
|
||||
|
||||
func (asciiEncoder) Transform(dst, src []byte, atEOF bool) (int, int, error) {
|
||||
var e error
|
||||
var ndst, nsrc int
|
||||
var sz int
|
||||
for nsrc < len(src) {
|
||||
if ndst >= len(dst) {
|
||||
e = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
r := rune(src[nsrc])
|
||||
if r < utf8.RuneSelf {
|
||||
dst[ndst] = uint8(r)
|
||||
nsrc++
|
||||
ndst++
|
||||
continue
|
||||
}
|
||||
|
||||
// No valid runes beyond ASCII. However, we need to consume
|
||||
// the full rune, and report incomplete runes properly.
|
||||
|
||||
// Attempt to decode a multibyte rune
|
||||
r, sz = utf8.DecodeRune(src[nsrc:])
|
||||
if sz == 1 {
|
||||
// If its inconclusive due to insufficient data in
|
||||
// in the source, report it
|
||||
if !atEOF && !utf8.FullRune(src[nsrc:]) {
|
||||
e = transform.ErrShortSrc
|
||||
break
|
||||
}
|
||||
}
|
||||
nsrc += sz
|
||||
dst[ndst] = encoding.ASCIISub
|
||||
ndst++
|
||||
}
|
||||
return ndst, nsrc, e
|
||||
}
|
37
cell.go
37
cell.go
@ -18,18 +18,6 @@ import (
|
||||
"github.com/mattn/go-runewidth"
|
||||
)
|
||||
|
||||
// Cell represents a single character cell. This is primarily intended for
|
||||
// use by Screen implementors.
|
||||
type Cell struct {
|
||||
currMain rune
|
||||
currComb []rune
|
||||
currStyle Style
|
||||
lastMain rune
|
||||
lastStyle Style
|
||||
lastComb []rune
|
||||
width int
|
||||
}
|
||||
|
||||
type cell struct {
|
||||
currMain rune
|
||||
currComb []rune
|
||||
@ -40,12 +28,20 @@ type cell struct {
|
||||
width int
|
||||
}
|
||||
|
||||
// CellBuffer represents a two dimensional array of character cells.
|
||||
// This is primarily intended for use by Screen implementors; it
|
||||
// contains much of the common code they need. To create one, just
|
||||
// declare a variable of its type; no explicit initialization is necessary.
|
||||
//
|
||||
// CellBuffer is not thread safe.
|
||||
type CellBuffer struct {
|
||||
w int
|
||||
h int
|
||||
cells []cell
|
||||
}
|
||||
|
||||
// SetContent sets the contents (primary rune, combining runes,
|
||||
// and style) for a cell at a given location.
|
||||
func (cb *CellBuffer) SetContent(x int, y int,
|
||||
mainc rune, combc []rune, style Style) {
|
||||
|
||||
@ -65,7 +61,6 @@ func (cb *CellBuffer) SetContent(x int, y int,
|
||||
|
||||
if c.currMain != mainc {
|
||||
c.width = runewidth.RuneWidth(mainc)
|
||||
c.width = 1
|
||||
}
|
||||
c.currMain = mainc
|
||||
c.currComb = combc
|
||||
@ -73,6 +68,10 @@ func (cb *CellBuffer) SetContent(x int, y int,
|
||||
}
|
||||
}
|
||||
|
||||
// GetContent returns the contents of a character cell, including the
|
||||
// primary rune, any combining character runes (which will usually be
|
||||
// nil), the style, and the display width in cells. (The width can be
|
||||
// either 1, normally, or 2 for East Asian full-width characters.)
|
||||
func (cb *CellBuffer) GetContent(x, y int) (rune, []rune, Style, int) {
|
||||
var mainc rune
|
||||
var combc []rune
|
||||
@ -89,16 +88,22 @@ func (cb *CellBuffer) GetContent(x, y int) (rune, []rune, Style, int) {
|
||||
return mainc, combc, style, width
|
||||
}
|
||||
|
||||
// Size returns the (width, height) in cells of the buffer.
|
||||
func (cb *CellBuffer) Size() (int, int) {
|
||||
return cb.w, cb.h
|
||||
}
|
||||
|
||||
// Invalidate marks all characters within the buffer as dirty.
|
||||
func (cb *CellBuffer) Invalidate() {
|
||||
for i := range cb.cells {
|
||||
cb.cells[i].lastMain = rune(0)
|
||||
}
|
||||
}
|
||||
|
||||
// Dirty checks if a character at the given location needs an
|
||||
// to be refreshed on the physical display. This returns true
|
||||
// if the cell content is different since the last time it was
|
||||
// marked clean.
|
||||
func (cb *CellBuffer) Dirty(x, y int) bool {
|
||||
if x >= 0 && y >= 0 && x < cb.w && y < cb.h {
|
||||
c := &cb.cells[(y*cb.w)+x]
|
||||
@ -123,6 +128,9 @@ func (cb *CellBuffer) Dirty(x, y int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// SetDirty is normally used to indicate that a cell has
|
||||
// been displayed (in which case dirty is false), or to manually
|
||||
// force a cell to be marked dirty.
|
||||
func (cb *CellBuffer) SetDirty(x, y int, dirty bool) {
|
||||
if x >= 0 && y >= 0 && x < cb.w && y < cb.h {
|
||||
c := &cb.cells[(y*cb.w)+x]
|
||||
@ -167,8 +175,7 @@ func (cb *CellBuffer) Resize(w, h int) {
|
||||
|
||||
// Fill fills the entire cell buffer array with the specified character
|
||||
// and style. Normally choose ' ' to clear the screen. This API doesn't
|
||||
// support combining characters. (Why would you want to fill with combining
|
||||
// characters?!?)
|
||||
// support combining characters.
|
||||
func (cb *CellBuffer) Fill(r rune, style Style) {
|
||||
for i := range cb.cells {
|
||||
c := &cb.cells[i]
|
||||
|
@ -16,8 +16,8 @@
|
||||
|
||||
package tcell
|
||||
|
||||
import "errors"
|
||||
|
||||
// NewConsoleScreen returns a console based screen. This platform
|
||||
// doesn't have support for any, so it returns nil and a suitable error.
|
||||
func NewConsoleScreen() (Screen, error) {
|
||||
return nil, errors.New("no platform specific console support")
|
||||
return nil, ErrNoScreen
|
||||
}
|
||||
|
@ -47,12 +47,17 @@ type cScreen struct {
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// all Windows systems are little endian
|
||||
var k32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
|
||||
// We have to bring in the kernel32.dll directly, so we can get access to some
|
||||
// system calls that the core Go API lacks.
|
||||
//
|
||||
// Note that Windows appends some functions with W to indicate that wide
|
||||
// characters (Unicode) are in use. The documentation refers to them
|
||||
// without this suffix, as the resolution is made via preprocessor.
|
||||
// We have to bring in the kernel32.dll directly, so we can get access to some
|
||||
// system calls that the core Go API lacks.
|
||||
|
||||
var (
|
||||
procReadConsoleInput = k32.NewProc("ReadConsoleInputW")
|
||||
procGetConsoleCursorInfo = k32.NewProc("GetConsoleCursorInfo")
|
||||
@ -68,9 +73,9 @@ var (
|
||||
procSetConsoleTextAttribute = k32.NewProc("SetConsoleTextAttribute")
|
||||
)
|
||||
|
||||
// We have to bring in the kernel32.dll directly, so we can get access to some
|
||||
// system calls that the core Go API lacks.
|
||||
|
||||
// NewConsoleScreen returns a Screen for the Windows console associated
|
||||
// with the current process. The Screen makes use of the Windows Console
|
||||
// API to display content and read events.
|
||||
func NewConsoleScreen() (Screen, error) {
|
||||
return &cScreen{}, nil
|
||||
}
|
||||
@ -218,8 +223,8 @@ func (s *cScreen) doCursor() {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *cScreen) HideCursor() {
|
||||
c.ShowCursor(-1, -1)
|
||||
func (s *cScreen) HideCursor() {
|
||||
s.ShowCursor(-1, -1)
|
||||
}
|
||||
|
||||
type charInfo struct {
|
||||
@ -511,6 +516,15 @@ func (s *cScreen) getConsoleInput() error {
|
||||
if mrec.btns&0x10 != 0 {
|
||||
btns |= Button5
|
||||
}
|
||||
if mrec.btns&0x20 != 0 {
|
||||
btns |= Button6
|
||||
}
|
||||
if mrec.btns&0x40 != 0 {
|
||||
btns |= Button7
|
||||
}
|
||||
if mrec.btns&0x80 != 0 {
|
||||
btns |= Button8
|
||||
}
|
||||
|
||||
if mrec.flags&mouseVWheeled != 0 {
|
||||
if mrec.btns&0x80000000 == 0 {
|
||||
@ -701,7 +715,7 @@ func (s *cScreen) draw() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if x > s.w - width {
|
||||
if x > s.w-width {
|
||||
mainc = ' '
|
||||
combc = nil
|
||||
width = 1
|
||||
|
26
database.go
26
database.go
@ -1,4 +1,4 @@
|
||||
// Generated by ./mkinfo (darwin/amd64) on Tue Oct 6 00:00:22 PDT 2015.
|
||||
// Generated by ./mkinfo (darwin/amd64) on Thu Oct 8 09:34:28 PDT 2015.
|
||||
// DO NOT HAND-EDIT
|
||||
|
||||
package tcell
|
||||
@ -333,7 +333,7 @@ func init() {
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "d200",
|
||||
Aliases: []string{ "d200-dg" },
|
||||
Aliases: []string{"d200-dg"},
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
@ -417,7 +417,7 @@ func init() {
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "d210",
|
||||
Aliases: []string{ "d214" },
|
||||
Aliases: []string{"d214"},
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
@ -557,7 +557,7 @@ func init() {
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "Eterm",
|
||||
Aliases: []string{ "Eterm-color" },
|
||||
Aliases: []string{"Eterm-color"},
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Colors: 8,
|
||||
@ -951,7 +951,7 @@ func init() {
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "hpterm",
|
||||
Aliases: []string{ "X-hpterm" },
|
||||
Aliases: []string{"X-hpterm"},
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
@ -1565,7 +1565,7 @@ func init() {
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "sun",
|
||||
Aliases: []string{ "sun1", "sun2" },
|
||||
Aliases: []string{"sun1", "sun2"},
|
||||
Columns: 80,
|
||||
Lines: 34,
|
||||
Bell: "\a",
|
||||
@ -1668,7 +1668,7 @@ func init() {
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "tvi912",
|
||||
Aliases: []string{ "tvi914", "tvi920" },
|
||||
Aliases: []string{"tvi914", "tvi920"},
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
@ -1838,7 +1838,7 @@ func init() {
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "vt100",
|
||||
Aliases: []string{ "vt100-am" },
|
||||
Aliases: []string{"vt100-am"},
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
@ -1911,7 +1911,7 @@ func init() {
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "vt220",
|
||||
Aliases: []string{ "vt200" },
|
||||
Aliases: []string{"vt200"},
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
@ -1957,7 +1957,7 @@ func init() {
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "vt320",
|
||||
Aliases: []string{ "vt300" },
|
||||
Aliases: []string{"vt300"},
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
@ -2010,7 +2010,7 @@ func init() {
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "vt400",
|
||||
Aliases: []string{ "dec-vt400", "vt400-24" },
|
||||
Aliases: []string{"dec-vt400", "vt400-24"},
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Clear: "\x1b[H\x1b[J$<10/>",
|
||||
@ -2086,7 +2086,7 @@ func init() {
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "wy50",
|
||||
Aliases: []string{ "wyse50" },
|
||||
Aliases: []string{"wyse50"},
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
@ -2134,7 +2134,7 @@ func init() {
|
||||
})
|
||||
AddTerminfo(&Terminfo{
|
||||
Name: "wy60",
|
||||
Aliases: []string{ "wyse60" },
|
||||
Aliases: []string{"wyse60"},
|
||||
Columns: 80,
|
||||
Lines: 24,
|
||||
Bell: "\a",
|
||||
|
71
encoding.go
71
encoding.go
@ -15,6 +15,7 @@
|
||||
package tcell
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
@ -22,10 +23,15 @@ import (
|
||||
|
||||
var encodings map[string]encoding.Encoding
|
||||
var encodingLk sync.Mutex
|
||||
var encodingFallback EncodingFallback = EncodingFallbackFail
|
||||
|
||||
// RegisterEncoding may be called by the application to register an encoding.
|
||||
// The presence of additional encodings will facilitate application usage with
|
||||
// terminal environments where the I/O subsystem does not support Unicode.
|
||||
//
|
||||
// Windows systems use Unicode natively, and do not need any of the encoding
|
||||
// subsystem when using Windows Console screens.
|
||||
//
|
||||
// Please see the Go documentation for golang.org/x/text/encoding -- most of
|
||||
// the common ones exist already as stock variables. For example, ISO8859-15
|
||||
// can be registered using the following code:
|
||||
@ -43,16 +49,12 @@ var encodingLk sync.Mutex
|
||||
// These are expected to have the following pattern:
|
||||
//
|
||||
// $language[.$codeset[@$variant]
|
||||
|
||||
//
|
||||
// We extract only the $codeset part, which will usually be something like
|
||||
// UTF-8 or ISO8859-15 or KOI8-R. Note that if the locale is either "POSIX"
|
||||
// or "C", then we assume US-ASCII (the POSIX 'portable character set'
|
||||
// and assume all other characters are somehow invalid.)
|
||||
//
|
||||
// On Windows systems, the Console is assumed to be UTF-16LE. As we
|
||||
// communicate with the console subsystem using UTF-16LE, no conversions are
|
||||
// necessary. So none of this is required for Windows systems.
|
||||
//
|
||||
// Modern POSIX systems and terminal emulators may use UTF-8, and for those
|
||||
// systems, this API is also unnecessary. For example, Darwin (MacOS X) and
|
||||
// modern Linux running modern xterm generally will out of the box without
|
||||
@ -64,12 +66,43 @@ var encodingLk sync.Mutex
|
||||
// increase quite a bit as each encoding is added. The East Asian encodings
|
||||
// have been seen to add 100-200K per encoding to the application size.
|
||||
//
|
||||
func RegisterEncoding(name string, enc encoding.Encoding) {
|
||||
func RegisterEncoding(charset string, enc encoding.Encoding) {
|
||||
encodingLk.Lock()
|
||||
if encodings == nil {
|
||||
encodings = make(map[string]encoding.Encoding)
|
||||
}
|
||||
encodings[name] = enc
|
||||
charset = strings.ToLower(charset)
|
||||
encodings[charset] = enc
|
||||
encodingLk.Unlock()
|
||||
}
|
||||
|
||||
// EncodingFallback describes how the system behavees when the locale
|
||||
// requires a character set that we do not support. The system always
|
||||
// supports UTF-8 and US-ASCII. On Windows consoles, UTF-16LE is also
|
||||
// supported automatically. Other character sets must be added using the
|
||||
// RegisterEncoding API. (A large group of nearly all of them can be
|
||||
// added using the RegisterAll function in the encoding sub package.)
|
||||
type EncodingFallback int
|
||||
|
||||
const (
|
||||
// EncodingFallbackFail behavior causes GetEncoding to fail
|
||||
// when it cannot find an encoding.
|
||||
EncodingFallbackFail = iota
|
||||
|
||||
// EncodingFallbackASCII behaviore causes GetEncoding to fall back
|
||||
// to a 7-bit ASCII encoding, if no other encoding can be found.
|
||||
EncodingFallbackASCII
|
||||
|
||||
// EncodingFallbackUTF8 behavior causes GetEncoding to assume
|
||||
// UTF8 can pass unmodified upon failure. Note that this behavior
|
||||
// is not recommended, unless you are sure your terminal can cope
|
||||
// with real UTF8 sequences.
|
||||
EncodingFallbackUTF8
|
||||
)
|
||||
|
||||
// SetEncodingFallback changes the behavior of GetEncoding when a suitable
|
||||
// encoding is not found. The default is EncodingFallbackFail, which
|
||||
// causes GetEncoding to simply return nil.
|
||||
func SetEncodingFallback(fb EncodingFallback) {
|
||||
encodingLk.Lock()
|
||||
encodingFallback = fb
|
||||
encodingLk.Unlock()
|
||||
}
|
||||
|
||||
@ -77,11 +110,25 @@ func RegisterEncoding(name string, enc encoding.Encoding) {
|
||||
// for the given character set name. Note that this will return nil for
|
||||
// either the Unicode (UTF-8) or ASCII encodings, since we don't use
|
||||
// encodings for them but instead have our own native methods.
|
||||
func GetEncoding(name string) encoding.Encoding {
|
||||
func GetEncoding(charset string) encoding.Encoding {
|
||||
charset = strings.ToLower(charset)
|
||||
encodingLk.Lock()
|
||||
defer encodingLk.Unlock()
|
||||
if enc, ok := encodings[name]; ok {
|
||||
if enc, ok := encodings[charset]; ok {
|
||||
return enc
|
||||
}
|
||||
switch encodingFallback {
|
||||
case EncodingFallbackASCII:
|
||||
return ASCII
|
||||
case EncodingFallbackUTF8:
|
||||
return encoding.Nop
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
// We always support UTF-8 and ASCII.
|
||||
encodings = make(map[string]encoding.Encoding)
|
||||
encodings["utf-8"] = UTF8
|
||||
encodings["us-ascii"] = ASCII
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
// +build !windows,!nacl,!plan9
|
||||
|
||||
// Copyright 2015 The TCell Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -26,8 +24,16 @@ import (
|
||||
"golang.org/x/text/encoding/traditionalchinese"
|
||||
)
|
||||
|
||||
// Register registers all known encodings. This is a short-cut to
|
||||
// add full character set support to your program. Note that this can
|
||||
// add several megabytes to your program's size, because some of the encoodings
|
||||
// are rather large (particularly those from East Asia.)
|
||||
func Register() {
|
||||
tcell.RegisterEncoding("ISO8859-1", charmap.ISO8859_15) // alias for now
|
||||
// We supply latin1 and latin5, because Go doesn't
|
||||
tcell.RegisterEncoding("ISO8859-1", ISO8859_1)
|
||||
tcell.RegisterEncoding("ISO8859-9", ISO8859_9)
|
||||
|
||||
tcell.RegisterEncoding("ISO8859-10", charmap.ISO8859_10)
|
||||
tcell.RegisterEncoding("ISO8859-13", charmap.ISO8859_13)
|
||||
tcell.RegisterEncoding("ISO8859-14", charmap.ISO8859_14)
|
||||
tcell.RegisterEncoding("ISO8859-15", charmap.ISO8859_15)
|
||||
@ -39,14 +45,12 @@ func Register() {
|
||||
tcell.RegisterEncoding("ISO8859-6", charmap.ISO8859_6)
|
||||
tcell.RegisterEncoding("ISO8859-7", charmap.ISO8859_7)
|
||||
tcell.RegisterEncoding("ISO8859-8", charmap.ISO8859_8)
|
||||
// ISO8859-9 is missing -- not present in GO, which is a shame since its basically
|
||||
// almost 8859-1/-15.
|
||||
tcell.RegisterEncoding("KOI8-R", charmap.KOI8R)
|
||||
tcell.RegisterEncoding("KOI8-U", charmap.KOI8U)
|
||||
|
||||
// Asian stuff
|
||||
tcell.RegisterEncoding("EUC-JP", japanese.EUCJP)
|
||||
tcell.RegisterEncoding("Shift_JIS", japanese.ShiftJIS)
|
||||
tcell.RegisterEncoding("SHIFT_JIS", japanese.ShiftJIS)
|
||||
tcell.RegisterEncoding("ISO2022JP", japanese.ISO2022JP)
|
||||
|
||||
tcell.RegisterEncoding("EUC-KR", korean.EUCKR)
|
||||
@ -83,16 +87,28 @@ func Register() {
|
||||
"ISO-8859-7": "ISO8859-7",
|
||||
"8859-8": "ISO8859-8",
|
||||
"ISO-8859-8": "ISO8859-8",
|
||||
"8859-9": "ISO8859-9",
|
||||
"ISO-8859-9": "ISO8859-9",
|
||||
|
||||
"SJIS": "Shift_JIS",
|
||||
"eucJP": "EUC-JP",
|
||||
"EUCJP": "EUC-JP",
|
||||
"2022-JP": "ISO2022JP",
|
||||
"ISO-2022-JP": "ISO2022JP",
|
||||
|
||||
"eucKR": "EUC-KR",
|
||||
"EUCKR": "EUC-KR",
|
||||
|
||||
// ISO646 isn't quite exactly ASCII, but the 1991 IRV
|
||||
// (international reference version) is so. This helps
|
||||
// some older systems that may use "646" for POSIX locales.
|
||||
"646": "US-ASCII",
|
||||
"ISO646": "US-ASCII",
|
||||
|
||||
// Other names for UTF-8
|
||||
"UTF8": "UTF-8",
|
||||
}
|
||||
for n, v := range aliases {
|
||||
tcell.RegisterEncoding(n, tcell.GetEncoding(v))
|
||||
if enc := tcell.GetEncoding(v); enc != nil {
|
||||
tcell.RegisterEncoding(n, enc)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
144
encoding/charmap.go
Normal file
144
encoding/charmap.go
Normal file
@ -0,0 +1,144 @@
|
||||
// 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 encoding
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// suitable for 8-bit encodings
|
||||
type cmap struct {
|
||||
transform.NopResetter
|
||||
bytes map[rune]byte
|
||||
runes [256]rune // offset by 128, as all values are identical
|
||||
ascii bool
|
||||
}
|
||||
|
||||
type cmapDecoder struct {
|
||||
transform.NopResetter
|
||||
cmap *cmap
|
||||
}
|
||||
type cmapEncoder struct {
|
||||
transform.NopResetter
|
||||
cmap *cmap
|
||||
}
|
||||
|
||||
func (c *cmap) Init() {
|
||||
c.bytes = make(map[rune]byte)
|
||||
for i := 0; i < 256; i++ {
|
||||
c.bytes[rune(i)] = byte(i)
|
||||
c.runes[i] = rune(i)
|
||||
c.ascii = true
|
||||
}
|
||||
}
|
||||
|
||||
func (c *cmap) Map(b byte, r rune) {
|
||||
if b < 128 {
|
||||
c.ascii = false
|
||||
}
|
||||
|
||||
// delete the old self-mapping
|
||||
delete(c.bytes, rune(b))
|
||||
|
||||
// and add the new one
|
||||
c.bytes[r] = b
|
||||
c.runes[int(b)] = r
|
||||
}
|
||||
|
||||
func (c *cmap) NewDecoder() transform.Transformer {
|
||||
return cmapDecoder{cmap: c}
|
||||
}
|
||||
|
||||
func (c *cmap) NewEncoder() transform.Transformer {
|
||||
return cmapEncoder{cmap: c}
|
||||
}
|
||||
|
||||
func (d cmapDecoder) Transform(dst, src []byte, atEOF bool) (int, int, error) {
|
||||
var e error
|
||||
var ndst, nsrc int
|
||||
|
||||
for _, c := range src {
|
||||
if d.cmap.ascii && c < utf8.RuneSelf {
|
||||
if ndst >= len(dst) {
|
||||
e = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
dst[ndst] = c
|
||||
ndst++
|
||||
nsrc++
|
||||
continue
|
||||
}
|
||||
|
||||
r := d.cmap.runes[c]
|
||||
l := utf8.RuneLen(r)
|
||||
|
||||
// l will be a positive number, because we never inject invalid
|
||||
// runes into the rune map.
|
||||
|
||||
if ndst+l > len(dst) {
|
||||
e = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
utf8.EncodeRune(dst[ndst:], r)
|
||||
ndst += l
|
||||
nsrc++
|
||||
}
|
||||
return ndst, nsrc, e
|
||||
}
|
||||
|
||||
func (d cmapEncoder) Transform(dst, src []byte, atEOF bool) (int, int, error) {
|
||||
var e error
|
||||
var ndst, nsrc int
|
||||
for nsrc < len(src) {
|
||||
if ndst >= len(dst) {
|
||||
e = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
ch := src[nsrc]
|
||||
if d.cmap.ascii && ch < utf8.RuneSelf {
|
||||
dst[ndst] = ch
|
||||
nsrc++
|
||||
ndst++
|
||||
continue
|
||||
}
|
||||
|
||||
// No valid runes beyond 0xFF. However, we need to consume
|
||||
// the full rune, and report incomplete runes properly.
|
||||
|
||||
// Attempt to decode a multibyte rune
|
||||
r, sz := utf8.DecodeRune(src[nsrc:])
|
||||
if r == utf8.RuneError && sz == 1 {
|
||||
// If its inconclusive due to insufficient data in
|
||||
// in the source, report it
|
||||
if !atEOF && !utf8.FullRune(src[nsrc:]) {
|
||||
e = transform.ErrShortSrc
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if c, ok := d.cmap.bytes[r]; ok {
|
||||
dst[ndst] = c
|
||||
} else {
|
||||
dst[ndst] = encoding.ASCIISub
|
||||
}
|
||||
nsrc += sz
|
||||
ndst++
|
||||
}
|
||||
|
||||
return ndst, nsrc, e
|
||||
}
|
34
encoding/latin1.go
Normal file
34
encoding/latin1.go
Normal file
@ -0,0 +1,34 @@
|
||||
// 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 encoding
|
||||
|
||||
import (
|
||||
"golang.org/x/text/encoding"
|
||||
)
|
||||
|
||||
// ISO8859_1 represents the 8-bit ISO8859-1 scheme. It decodes directly to
|
||||
// UTF-8 without change, as all ISO8859-1 values are legal UTF-8.
|
||||
// Unicode values less than 256 (i.e. 8 bits) map 1:1 with 8859-1.
|
||||
// It encodes runes outside of that to 0x1A, the ASCII substitution character.
|
||||
var ISO8859_1 encoding.Encoding
|
||||
|
||||
func init() {
|
||||
cm := &cmap{}
|
||||
cm.Init()
|
||||
|
||||
// No further mapping needed for ISO8859-1, as there is exactly a 1:1
|
||||
// mapping between the Unicode and 8859-1 namespaces.
|
||||
ISO8859_1 = cm
|
||||
}
|
@ -1,5 +1,3 @@
|
||||
// +build windows nacl plan9
|
||||
|
||||
// Copyright 2015 The TCell Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -16,10 +14,22 @@
|
||||
|
||||
package encoding
|
||||
|
||||
func Register() {
|
||||
// So Windows is only UTF-16LE (yay!)
|
||||
import (
|
||||
"golang.org/x/text/encoding"
|
||||
)
|
||||
|
||||
// Other platforms that don't use termios/terminfo are pretty much unsupported.
|
||||
// Therefore, we shouldn't bring in all this stuff because it creates a lot of
|
||||
// bloat for those platforms. So, just punt.
|
||||
// ISO8859_9 represents the 8-bit ISO8859-1 scheme. It decodes to UTF-8
|
||||
// unchanged for all 256 positions except for six positions.
|
||||
var ISO8859_9 encoding.Encoding
|
||||
|
||||
func init() {
|
||||
cm := &cmap{}
|
||||
cm.Init()
|
||||
cm.Map(0xD0, 'Ğ')
|
||||
cm.Map(0xDD, 'İ')
|
||||
cm.Map(0xDE, 'Ş')
|
||||
cm.Map(0xF0, 'ğ')
|
||||
cm.Map(0xFD, 'ı')
|
||||
cm.Map(0xFE, 'ş')
|
||||
ISO8859_9 = cm
|
||||
}
|
27
errors.go
27
errors.go
@ -20,23 +20,48 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoDatabase = errors.New("terminal database not found")
|
||||
// ErrTermNotFound indicates that a suitable terminal entry could
|
||||
// not be found. This can result from either not having TERM set,
|
||||
// or from the TERM failing to support certain minimal functionality,
|
||||
// in particular absolute cursor addressability (the cup capability)
|
||||
// is required. For example, legacy "adm3" lacks this capability,
|
||||
// whereas the slightly newer "adm3a" supports it. This failure
|
||||
// occurs most often with "dumb".
|
||||
ErrTermNotFound = errors.New("terminal entry not found")
|
||||
|
||||
// ErrNoScreen indicates that no suitable screen could be found.
|
||||
// This may result from attempting to run on a platform where there
|
||||
// is no support for either termios or console I/O (such as nacl),
|
||||
// or from running in an environment where there is no access to
|
||||
// a suitable console/terminal device. (For example, running on
|
||||
// without a controlling TTY or with no /dev/tty on POSIX platforms.)
|
||||
ErrNoScreen = errors.New("no suitable screen available")
|
||||
|
||||
// ErrNoCharset indicates that the locale environment the
|
||||
// program is not supported by the program, because no suitable
|
||||
// encoding was found for it. This problem never occurs if
|
||||
// the environment is UTF-8 or UTF-16.
|
||||
ErrNoCharset = errors.New("character set not supported")
|
||||
)
|
||||
|
||||
// An EventError is an event representing some sort of error, and carries
|
||||
// an error payload.
|
||||
type EventError struct {
|
||||
t time.Time
|
||||
err error
|
||||
}
|
||||
|
||||
// When returns the time when the event was created.
|
||||
func (ev *EventError) When() time.Time {
|
||||
return ev.t
|
||||
}
|
||||
|
||||
// Error implements the error.
|
||||
func (ev *EventError) Error() string {
|
||||
return ev.err.Error()
|
||||
}
|
||||
|
||||
// NewEventError creates an ErrorEvent with the given error payload.
|
||||
func NewEventError(err error) *EventError {
|
||||
return &EventError{t: time.Now(), err: err}
|
||||
}
|
||||
|
@ -25,13 +25,17 @@ type EventInterrupt struct {
|
||||
v interface{}
|
||||
}
|
||||
|
||||
// When returns the time when this event was created.
|
||||
func (ev *EventInterrupt) When() time.Time {
|
||||
return ev.t
|
||||
}
|
||||
|
||||
// Data is used to obtain the opaque event payload.
|
||||
func (ev *EventInterrupt) Data() interface{} {
|
||||
return ev.v
|
||||
}
|
||||
|
||||
// NewEventInterrupt creates an EventInterrupt with the given payload.
|
||||
func NewEventInterrupt(data interface{}) *EventInterrupt {
|
||||
return &EventInterrupt{t: time.Now(), v: data}
|
||||
}
|
||||
|
6
key.go
6
key.go
@ -70,11 +70,11 @@ func (ev *EventKey) Key() Key {
|
||||
return ev.key
|
||||
}
|
||||
|
||||
// ModMask returns the modifiers that were present with the key press. Note
|
||||
// Modifiers returns the modifiers that were present with the key press. Note
|
||||
// that not all platforms and terminals support this equally well, and some
|
||||
// cases we will not not know for sure. Hence, applications should avoid
|
||||
// using this in most circumstances.
|
||||
func (ev *EventKey) Mod() ModMask {
|
||||
func (ev *EventKey) Modifiers() ModMask {
|
||||
return ev.mod
|
||||
}
|
||||
|
||||
@ -397,5 +397,5 @@ const (
|
||||
KeyEscape = KeyESC
|
||||
KeyEnter = KeyCR
|
||||
KeySpace = KeySP
|
||||
KeyBackspace2 = KeyDEL // This is delete back, not forward
|
||||
KeyBackspace2 = KeyDEL
|
||||
)
|
||||
|
@ -43,6 +43,7 @@ go build mkinfo.go
|
||||
# first make the database.go file
|
||||
echo "Building Go database"
|
||||
./mkinfo -go database.go `cat models.txt aliases.txt`
|
||||
go fmt database.go
|
||||
|
||||
echo "Building JSON database"
|
||||
|
||||
|
@ -269,7 +269,7 @@ func dotGoAddArr(w io.Writer, n string, a []string) {
|
||||
if len(a) == 0 {
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(w, " %-13s []string{ ", n+":")
|
||||
fmt.Fprintf(w, " %-13s []string{", n+":")
|
||||
did := false
|
||||
for _, b := range a {
|
||||
if did {
|
||||
@ -278,7 +278,7 @@ func dotGoAddArr(w io.Writer, n string, a []string) {
|
||||
did = true
|
||||
fmt.Fprintf(w, "%q", b)
|
||||
}
|
||||
fmt.Fprintln(w, " },")
|
||||
fmt.Fprintln(w, "},")
|
||||
}
|
||||
|
||||
func dotGoHeader(w io.Writer) {
|
||||
|
46
mouse.go
46
mouse.go
@ -22,18 +22,18 @@ import (
|
||||
// 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.
|
||||
// without any intervening button release. On some terminals only the initiating
|
||||
// press and terminating release event will be delivered.
|
||||
//
|
||||
// Mouse wheel events, when reported, may appear on their own as individual
|
||||
// impulses; that is, there will normally not be a release event delivered
|
||||
// for mouse wheel movements.
|
||||
//
|
||||
// Most terminals cannot report the state of more than one button at a time --
|
||||
// and many cannot report motion events. (Windows consoles, modern XTerm, and
|
||||
// modern emulators like iTerm2, are known to support this well, though.)
|
||||
// and some cannot report motion events unless a button is pressed.
|
||||
//
|
||||
// Applications can inspect the time between events to figure out double clicks
|
||||
// and such.
|
||||
// Applications can inspect the time between events to resolve double or
|
||||
// triple clicks.
|
||||
type EventMouse struct {
|
||||
t time.Time
|
||||
btn ButtonMask
|
||||
@ -42,11 +42,12 @@ type EventMouse struct {
|
||||
y int
|
||||
}
|
||||
|
||||
// When returns the time when this EventMouse was created.
|
||||
func (ev *EventMouse) When() time.Time {
|
||||
return ev.t
|
||||
}
|
||||
|
||||
// ButtonMask returns the list of buttons that were pressed.
|
||||
// Buttons returns the list of buttons that were pressed or wheel motions.
|
||||
func (ev *EventMouse) Buttons() ButtonMask {
|
||||
return ev.btn
|
||||
}
|
||||
@ -69,26 +70,27 @@ func NewEventMouse(x, y int, btn ButtonMask, mod ModMask) *EventMouse {
|
||||
return &EventMouse{t: time.Now(), x: x, y: y, btn: btn, mod: mod}
|
||||
}
|
||||
|
||||
// BtnMask is a mask of mouse buttons.
|
||||
// ButtonMask is a mask of mouse buttons and wheel events. Mouse button presses
|
||||
// are normally delivered as both press and release events. Mouse wheel events
|
||||
// are normally just single impulse events. Windows supports up to eight
|
||||
// separate buttons plus all four wheel directions, but XTerm can only support
|
||||
// mouse buttons 1-3 and wheel up/down. Its not unheard of for terminals
|
||||
// to support only one or two buttons (think Macs). Old terminals, and true
|
||||
// emulations (such as vt100) won't support mice at all, of course.
|
||||
type ButtonMask int16
|
||||
|
||||
const (
|
||||
// Button1 is usually the left mouse button.
|
||||
Button1 ButtonMask = 1 << iota
|
||||
// Button2 is usually the middle mouse button, for three button mice.
|
||||
Button2
|
||||
// Button3 is usually the right mouse button on 2 or 3 button mice.
|
||||
Button3
|
||||
Button4
|
||||
Button5
|
||||
Button1 ButtonMask = 1 << iota // Usually left mouse button.
|
||||
Button2 // Usually the middle mouse button.
|
||||
Button3 // Usually the right mouse button.
|
||||
Button4 // Often a side button (thumb/next).
|
||||
Button5 // Often a side button (thumb/prev).
|
||||
Button6
|
||||
Button7
|
||||
Button8
|
||||
// WheelUp indicates the wheel being moved up, away from the user.
|
||||
WheelUp
|
||||
// WheelDown indicates the wheel being moved down, towards the user.
|
||||
WheelDown
|
||||
WheelLeft
|
||||
WheelRight
|
||||
WheelUp // Wheel motion up/away from user.
|
||||
WheelDown // Wheel motion down/towards user.
|
||||
WheelLeft // Wheel motion to left.
|
||||
WheelRight // Wheel motion to right.
|
||||
ButtonNone ButtonMask = 0 // No button or wheel events.
|
||||
)
|
||||
const ButtonNone ButtonMask = 0
|
||||
|
@ -25,14 +25,18 @@ type EventResize struct {
|
||||
h int
|
||||
}
|
||||
|
||||
// NewEventResize creates an EventResize with the new updated window size,
|
||||
// which is given in character cells.
|
||||
func NewEventResize(width, height int) *EventResize {
|
||||
return &EventResize{t: time.Now(), w: width, h: height}
|
||||
}
|
||||
|
||||
// When returns the time when the Event was created.
|
||||
func (ev *EventResize) When() time.Time {
|
||||
return ev.t
|
||||
}
|
||||
|
||||
// Size returns the new window size as width, height in character cells.
|
||||
func (ev *EventResize) Size() (int, int) {
|
||||
return ev.w, ev.h
|
||||
}
|
||||
|
42
screen.go
42
screen.go
@ -29,24 +29,8 @@ type Screen interface {
|
||||
// filling the screen with spaces, using the global default style.
|
||||
Clear()
|
||||
|
||||
// SetCell sets the cell at the given location.
|
||||
// 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.)
|
||||
//
|
||||
// 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 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 is an older API, and will be removed. Please use
|
||||
// SetContent instead; SetCell is implemented in terms of SetContent.
|
||||
SetCell(x int, y int, style Style, ch ...rune)
|
||||
|
||||
// GetContent returns the contents at the given location. If the
|
||||
@ -54,14 +38,23 @@ type Screen interface {
|
||||
// StyleDefault. Note that the contents returned are logical contents
|
||||
// and may not actually be what is displayed, but rather are what will
|
||||
// be displayed if Show() or Sync() is called. The width is the width
|
||||
// in screen cells - this should either be 1 or 2.
|
||||
// in screen cells; most often this will be 1, but some East Asian
|
||||
// characters require two cells.
|
||||
GetContent(x, y int) (mainc rune, combc []rune, style Style, width int)
|
||||
|
||||
// SetContent sets the contents of the given cell location. If
|
||||
// the coordinates are out of range, then the operation is ignored.
|
||||
//
|
||||
// The first rune is the primary non-zero width rune. The array
|
||||
// that follows is a possible list of combining characters to append.
|
||||
// that follows is a possible list of combining characters to append,
|
||||
// and will usually be nil (no combining characters.)
|
||||
//
|
||||
// The results are not displayd until Show() or Sync() is called.
|
||||
//
|
||||
// Note that wide (East Asian full width) runes occupy two cells,
|
||||
// and attempts to place character at next cell to the right will have
|
||||
// undefined effects. Wide runes that are printed in the
|
||||
// last column will be replaced with a single width space on output.
|
||||
SetContent(x int, y int, mainc rune, combc []rune, style Style)
|
||||
|
||||
// SetStyle sets the default style to use when clearing the screen
|
||||
@ -101,15 +94,18 @@ type Screen interface {
|
||||
// return 0.
|
||||
Colors() int
|
||||
|
||||
// Show takes any output that was deferred due to buffering, and
|
||||
// flushes it to the physical display. It does so in the most
|
||||
// efficient and least visually disruptive manner possible.
|
||||
// Show makes all the content changes made using SetContent() visible
|
||||
// on the display.
|
||||
//
|
||||
// It does so in the most efficient and least visually disruptive
|
||||
// manner possible.
|
||||
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.
|
||||
|
13
style.go
13
style.go
@ -23,12 +23,12 @@ package tcell
|
||||
// Note that not all terminals can display all colors or attributes, and
|
||||
// many might have specific incompatibilities between specific attributes
|
||||
// and color combinations.
|
||||
//
|
||||
// To use Style, just declare a variable of its type.
|
||||
type Style int64
|
||||
|
||||
func NewStyle() Style {
|
||||
return Style(0)
|
||||
}
|
||||
|
||||
// StyleDefault represents a default style, based upon the context.
|
||||
// It is the zero value.
|
||||
const StyleDefault Style = 0
|
||||
|
||||
// Foreground returns a new style based on s, with the foreground color set
|
||||
@ -54,9 +54,8 @@ func (s Style) Decompose() (fg Color, bg Color, attr AttrMask) {
|
||||
func (s Style) setAttrs(attrs Style, on bool) Style {
|
||||
if on {
|
||||
return s | (attrs << 32)
|
||||
} else {
|
||||
return s &^ (attrs << 32)
|
||||
}
|
||||
return s &^ (attrs << 32)
|
||||
}
|
||||
|
||||
// Normal returns the style with all attributes disabled.
|
||||
@ -89,7 +88,7 @@ func (s Style) Reverse(on bool) Style {
|
||||
return s.setAttrs(Style(AttrReverse), on)
|
||||
}
|
||||
|
||||
// Reverse returns a new style based on s, with the underline attribute set
|
||||
// Underline returns a new style based on s, with the underline attribute set
|
||||
// as requested.
|
||||
func (s Style) Underline(on bool) Style {
|
||||
return s.setAttrs(Style(AttrUnderline), on)
|
||||
|
27
terminfo.go
27
terminfo.go
@ -188,9 +188,8 @@ func (st stack) PushBool(i bool) stack {
|
||||
func nextch(s string, index int) (byte, int) {
|
||||
if index < len(s) {
|
||||
return s[index], index + 1
|
||||
} else {
|
||||
return 0, index
|
||||
}
|
||||
return 0, index
|
||||
}
|
||||
|
||||
// static vars
|
||||
@ -461,6 +460,12 @@ func (t *Terminfo) TParm(s string, p ...int) string {
|
||||
return out.String()
|
||||
}
|
||||
|
||||
// TPuts emits the string to the writer, but expands inline padding
|
||||
// indications (of the form $<[delay]> where [delay] is msec) to
|
||||
// a suitable number of padding characters (usually null bytes) based
|
||||
// upon the supplied baud. At high baud rates, more padding characters
|
||||
// will be inserted. All Terminfo based strings should be emitted using
|
||||
// this function.
|
||||
func (t *Terminfo) TPuts(w io.Writer, s string, baud int) {
|
||||
for {
|
||||
beg := strings.Index(s, "$<")
|
||||
@ -515,7 +520,7 @@ func (t *Terminfo) TGoto(col, row int) string {
|
||||
return t.TParm(t.SetCursor, row, col)
|
||||
}
|
||||
|
||||
// Color returns a string corresponding to the given foreground and background
|
||||
// TColor returns a string corresponding to the given foreground and background
|
||||
// colors. Either fg or bg can be set to -1 to elide.
|
||||
func (t *Terminfo) TColor(fg, bg Color) string {
|
||||
fi := int(fg - 1)
|
||||
@ -567,9 +572,10 @@ func AddTerminfo(t *Terminfo) {
|
||||
}
|
||||
|
||||
func loadFromFile(fname string, term string) (*Terminfo, error) {
|
||||
if f, e := os.Open(fname); e != nil {
|
||||
return nil, ErrNoDatabase
|
||||
} else {
|
||||
f, e := os.Open(fname)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
d := json.NewDecoder(f)
|
||||
for {
|
||||
t := &Terminfo{}
|
||||
@ -583,7 +589,6 @@ func loadFromFile(fname string, term string) (*Terminfo, error) {
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LookupTerminfo attemps to find a definition for the named $TERM.
|
||||
@ -599,22 +604,18 @@ func LookupTerminfo(name string) (*Terminfo, error) {
|
||||
dblock.Unlock()
|
||||
|
||||
if t == nil {
|
||||
var e error
|
||||
// Load the database located here. Its expected that TCELLSDB
|
||||
// points either to a single JSON file, or to a directory of
|
||||
// of files all of which should be loaded.
|
||||
if pth := os.Getenv("TCELLDB"); pth != "" {
|
||||
t, e = loadFromFile(pth, name)
|
||||
t, _ = loadFromFile(pth, name)
|
||||
} else {
|
||||
pth = path.Join(os.Getenv("GOPATH"), "src",
|
||||
"github.com", "gdamore", "tcell",
|
||||
"database.json")
|
||||
t, e = loadFromFile(pth, name)
|
||||
t, _ = loadFromFile(pth, name)
|
||||
|
||||
}
|
||||
if t == nil {
|
||||
return nil, e
|
||||
}
|
||||
if t != nil {
|
||||
dblock.Lock()
|
||||
terminfos[name] = t
|
||||
|
76
tscreen.go
76
tscreen.go
@ -16,7 +16,6 @@ package tcell
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
@ -91,17 +90,11 @@ func (t *tScreen) Init() error {
|
||||
t.charset = "UTF-8"
|
||||
|
||||
t.charset = t.getCharset()
|
||||
switch t.charset {
|
||||
case "UTF-8", "US-ASCII":
|
||||
t.encoder = nil
|
||||
t.decoder = nil
|
||||
default:
|
||||
if enc := GetEncoding(t.charset); enc != nil {
|
||||
t.encoder = enc.NewEncoder()
|
||||
t.decoder = enc.NewDecoder()
|
||||
} else {
|
||||
return errors.New("no support for charset " + t.charset)
|
||||
}
|
||||
return ErrNoCharset
|
||||
}
|
||||
ti := t.ti
|
||||
|
||||
@ -292,12 +285,6 @@ func (t *tScreen) SetCell(x, y int, style Style, ch ...rune) {
|
||||
|
||||
func (t *tScreen) encodeRune(r rune, buf []byte) []byte {
|
||||
|
||||
// all the character sets we care about are ASCII supersets
|
||||
if r < 0x80 {
|
||||
buf = append(buf, byte(r))
|
||||
return buf
|
||||
}
|
||||
|
||||
enc := t.encoder
|
||||
if enc == nil {
|
||||
// This is probably ASCII. Only append a filler character
|
||||
@ -341,7 +328,6 @@ func (t *tScreen) drawCell(x, y int) int {
|
||||
ti := t.ti
|
||||
|
||||
mainc, combc, style, width := t.cells.GetContent(x, y)
|
||||
|
||||
if !t.cells.Dirty(x, y) {
|
||||
return width
|
||||
}
|
||||
@ -394,14 +380,6 @@ func (t *tScreen) drawCell(x, y int) int {
|
||||
|
||||
var str string
|
||||
|
||||
switch t.charset {
|
||||
case "UTF-8":
|
||||
str = string(mainc)
|
||||
if combc != nil {
|
||||
str += string(combc)
|
||||
}
|
||||
default:
|
||||
// Non-Unicode systems. Make do.
|
||||
buf := make([]byte, 0, 6)
|
||||
|
||||
buf = t.encodeRune(mainc, buf)
|
||||
@ -413,7 +391,7 @@ func (t *tScreen) drawCell(x, y int) int {
|
||||
if width > 1 && str == "?" {
|
||||
// No FullWidth character support
|
||||
str = "? "
|
||||
}
|
||||
t.cx = -1
|
||||
}
|
||||
|
||||
// XXX: check for hazeltine not being able to display ~
|
||||
@ -426,6 +404,9 @@ func (t *tScreen) drawCell(x, y int) int {
|
||||
io.WriteString(t.out, str)
|
||||
t.cx += width
|
||||
t.cells.SetDirty(x, y, false)
|
||||
if width > 1 {
|
||||
t.cx = -1
|
||||
}
|
||||
|
||||
return width
|
||||
}
|
||||
@ -502,6 +483,14 @@ func (t *tScreen) draw() {
|
||||
for y := 0; y < t.h; y++ {
|
||||
for x := 0; x < t.w; x++ {
|
||||
width := t.drawCell(x, y)
|
||||
if width > 1 {
|
||||
if x+1 < t.w {
|
||||
// this is necessary so that if we ever
|
||||
// go back to drawing that cell, we
|
||||
// actually will *draw* it.
|
||||
t.cells.SetDirty(x+1, y, true)
|
||||
}
|
||||
}
|
||||
x += width - 1
|
||||
}
|
||||
}
|
||||
@ -750,7 +739,7 @@ func (t *tScreen) parseSgrMouse(buf *bytes.Buffer) (bool, bool) {
|
||||
state = 3
|
||||
|
||||
case '-':
|
||||
if state != 3 || state != 4 || state != 5 {
|
||||
if state != 3 && state != 4 && state != 5 {
|
||||
return false, false
|
||||
}
|
||||
if dig || neg {
|
||||
@ -897,45 +886,30 @@ func (t *tScreen) parseRune(buf *bytes.Buffer) (bool, bool) {
|
||||
}
|
||||
|
||||
if b[0] < 0x80 {
|
||||
// No encodings start with low numbered values
|
||||
// Low numbered values are control keys, not runes.
|
||||
return false, false
|
||||
}
|
||||
|
||||
switch t.charset {
|
||||
case "UTF-8":
|
||||
if utf8.FullRune(b) {
|
||||
r, _, e := buf.ReadRune()
|
||||
if e == nil {
|
||||
ev := NewEventKey(KeyRune, r, ModNone)
|
||||
t.PostEvent(ev)
|
||||
return true, true
|
||||
}
|
||||
}
|
||||
case "US-ASCII":
|
||||
// ASCII cannot generate this, so most likely it was
|
||||
// entered as an Alt sequence
|
||||
ev := NewEventKey(KeyRune, rune(b[0]-128), ModAlt)
|
||||
t.PostEvent(ev)
|
||||
buf.ReadByte()
|
||||
return true, true
|
||||
|
||||
default:
|
||||
utfb := make([]byte, 12)
|
||||
for l := 1; l <= len(b); l++ {
|
||||
t.decoder.Reset()
|
||||
nout, nin, _ := t.decoder.Transform(utfb, b[:l], true)
|
||||
nout, nin, e := t.decoder.Transform(utfb, b[:l], true)
|
||||
if e == transform.ErrShortSrc {
|
||||
continue
|
||||
}
|
||||
if nout != 0 {
|
||||
if r, _ := utf8.DecodeRune(utfb[:nout]); r != utf8.RuneError {
|
||||
r, _ := utf8.DecodeRune(utfb[:nout])
|
||||
if r != utf8.RuneError {
|
||||
ev := NewEventKey(KeyRune, r, ModNone)
|
||||
t.PostEvent(ev)
|
||||
}
|
||||
for eat := 0; eat < nin; eat++ {
|
||||
for nin > 0 {
|
||||
buf.ReadByte()
|
||||
nin--
|
||||
}
|
||||
return true, true
|
||||
}
|
||||
}
|
||||
}
|
||||
// Looks like potential escape
|
||||
return true, false
|
||||
}
|
||||
@ -983,8 +957,8 @@ func (t *tScreen) scanInput(buf *bytes.Buffer, expire bool) {
|
||||
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. Possibly we should only
|
||||
// do this for control characters such like ESC.
|
||||
// to the app & let them sort it out. Possibly we
|
||||
// should only do this for control characters like ESC.
|
||||
by, _ := buf.ReadByte()
|
||||
ev := NewEventKey(KeyRune, rune(by), ModNone)
|
||||
t.PostEvent(ev)
|
||||
|
@ -227,9 +227,8 @@ func (t *tScreen) getCharset() string {
|
||||
|
||||
func (t *tScreen) getWinSize() (int, int, error) {
|
||||
var cx, cy C.int
|
||||
if r, e := C.getwinsize(C.int(t.out.Fd()), &cx, &cy); r == 0 {
|
||||
return int(cx), int(cy), nil
|
||||
} else {
|
||||
if r, e := C.getwinsize(C.int(t.out.Fd()), &cx, &cy); r != 0 {
|
||||
return 0, 0, e
|
||||
}
|
||||
return int(cx), int(cy), nil
|
||||
}
|
||||
|
@ -16,16 +16,12 @@
|
||||
|
||||
package tcell
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// This stub file is for systems that have no termios.
|
||||
|
||||
type termiosPrivate struct{}
|
||||
|
||||
func (t *tScreen) termioInit() error {
|
||||
return errors.New("no termios support on this platform")
|
||||
return ErrNoScreen
|
||||
}
|
||||
|
||||
func (t *tScreen) termioFini() {
|
||||
@ -36,5 +32,5 @@ func (t *tScreen) getCharset() string {
|
||||
}
|
||||
|
||||
func (t *tScreen) getWinSize() (int, int, error) {
|
||||
return 0, 0, errors.New("no termios support on this platform")
|
||||
return 0, 0, ErrNoScreen
|
||||
}
|
||||
|
@ -16,26 +16,21 @@
|
||||
|
||||
package tcell
|
||||
|
||||
// On win32 we don't have support for termios. We probably could, and
|
||||
// On Windows we don't have support for termios. We probably could, and
|
||||
// may should, in a cygwin type environment. Its not clear how to make
|
||||
// this all work nicely with both cygwin and Windows console, so we
|
||||
// decline to do so here.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
func (t *tScreen) termioInit() error {
|
||||
return errors.New("no termios on Windows")
|
||||
return ErrNoScreen
|
||||
}
|
||||
|
||||
func (t *tScreen) termioFini() {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (t *tScreen) getWinSize() (int, int, error) {
|
||||
return 0, 0, errors.New("no temrios on Windows")
|
||||
return 0, 0, ErrNoScreen
|
||||
}
|
||||
|
||||
func (t *tScreen) getCharset() string {
|
||||
|
34
utf8.go
Normal file
34
utf8.go
Normal file
@ -0,0 +1,34 @@
|
||||
// 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 (
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
type validUtf8 struct{}
|
||||
|
||||
// UTF8 is an encoding for UTF-8. All it does is verify that the UTF-8
|
||||
// in is valid.
|
||||
var UTF8 encoding.Encoding = validUtf8{}
|
||||
|
||||
func (validUtf8) NewDecoder() transform.Transformer {
|
||||
return encoding.UTF8Validator
|
||||
}
|
||||
|
||||
func (validUtf8) NewEncoder() transform.Transformer {
|
||||
return encoding.UTF8Validator
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user