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

fixes #20 Wide characters & combining characters busted

fixes #21 Want unicode test program
fixes #15 Add ACS drawing support
This commit is contained in:
Garrett D'Amore 2015-10-04 19:53:38 -07:00
parent 8eba8adb55
commit 061768321e
13 changed files with 1319 additions and 799 deletions

View File

@ -42,8 +42,23 @@ func drawBox(s tcell.Screen, x1, y1, x2, y2 int, style tcell.Style, r rune) {
x1, x2 = x2, x1
}
for row := y1; row <= y2; row++ {
for col := x1; col <= x2; col++ {
for col := x1; col <= x2; col++ {
s.SetCell(col, y1, style, tcell.RuneHLine)
s.SetCell(col, y2, style, tcell.RuneHLine)
}
for row := y1 + 1; row < y2; row++ {
s.SetCell(x1, row, style, tcell.RuneVLine)
s.SetCell(x2, row, style, tcell.RuneVLine)
}
if y1 != y2 && x1 != x2 {
// Only add corners if we need to
s.SetCell(x1, y1, style, tcell.RuneULCorner)
s.SetCell(x2, y1, style, tcell.RuneURCorner)
s.SetCell(x1, y2, style, tcell.RuneLLCorner)
s.SetCell(x2, y2, style, tcell.RuneLRCorner)
}
for row := y1 + 1; row < y2; row++ {
for col := x1 + 1; col < x2; col++ {
s.SetCell(col, row, style, r)
}
}
@ -107,11 +122,11 @@ func main() {
ecnt := 0
for {
drawBox(s, 1, 1, 40, 4, white, ' ')
emitStr(s, 1, 1, white, "Press ESC twice to exit, C to clear.")
emitStr(s, 1, 2, white, fmt.Sprintf(posfmt, mx, my))
emitStr(s, 1, 3, white, fmt.Sprintf(btnfmt, bstr))
emitStr(s, 1, 4, white, fmt.Sprintf(keyfmt, lks))
drawBox(s, 1, 1, 42, 6, white, ' ')
emitStr(s, 2, 2, white, "Press ESC twice to exit, C to clear.")
emitStr(s, 2, 3, white, fmt.Sprintf(posfmt, mx, my))
emitStr(s, 2, 4, white, fmt.Sprintf(btnfmt, bstr))
emitStr(s, 2, 5, white, fmt.Sprintf(keyfmt, lks))
s.Show()
bstr = ""

167
_demos/unicode.go Normal file
View File

@ -0,0 +1,167 @@
// 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.
// unicode just displays a Unicode test on your screen.
// Press ESC to exit the program.
package main
import (
"fmt"
"os"
"github.com/gdamore/tcell"
"github.com/mattn/go-runewidth"
)
var row = 0
var style = tcell.StyleDefault
func Putln(s tcell.Screen, str string) {
Puts(s, style, 1, row, str)
row++
}
func Puts(s tcell.Screen, style tcell.Style, x, y int, str string) {
i := 0
var deferred []rune
dwidth := 0
for _, r := range str {
switch runewidth.RuneWidth(r) {
case 0:
if len(deferred) == 0 {
deferred = append(deferred, ' ')
dwidth = 1
}
case 1:
if len(deferred) != 0 {
s.SetCell(x+i, y, style, deferred...)
i += dwidth
}
deferred = deferred[0:0]
dwidth = 1
case 2:
if len(deferred) != 0 {
s.SetCell(x+i, y, style, deferred...)
i += dwidth
}
deferred = deferred[0:0]
dwidth = 2
}
deferred = append(deferred, r)
}
if len(deferred) != 0 {
s.SetCell(x+i, y, style, deferred...)
i += dwidth
}
}
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)
}
plain := tcell.StyleDefault
bold := style.Bold(true)
s.SetStyle(tcell.StyleDefault.
Foreground(tcell.ColorBlack).
Background(tcell.ColorWhite))
s.Clear()
quit := make(chan struct{})
style = bold
Putln(s, "Press ESC to Exit")
Putln(s, "Character set: "+s.CharacterSet())
style = plain
Putln(s, "English: October")
Putln(s, "Icelandic: október")
Putln(s, "Arabic: أكتوبر")
Putln(s, "Russian: октября")
Putln(s, "Greek: Οκτωβρίου")
Putln(s, "Chinese: 十月 (note, two double wide characters)")
Putln(s, "Combining: A\u030a (should look like Angstrom)")
Putln(s, "Emoticon: \U0001f618 (blowing a kiss)")
Putln(s, "Airplane: \u2708 (fly away)")
Putln(s, "Command: \u2318 (mac clover key)")
Putln(s, "Enclose: !\u20e3 (should be enclosed exclamation)")
Putln(s, "")
Putln(s, "Box:")
Putln(s, string([]rune{
tcell.RuneULCorner,
tcell.RuneHLine,
tcell.RuneTTee,
tcell.RuneHLine,
tcell.RuneURCorner,
}))
Putln(s, string([]rune{
tcell.RuneVLine,
tcell.RuneBullet,
tcell.RuneVLine,
tcell.RuneLantern,
tcell.RuneVLine,
}))
Putln(s, string([]rune{
tcell.RuneLTee,
tcell.RuneHLine,
tcell.RunePlus,
tcell.RuneHLine,
tcell.RuneRTee,
}))
Putln(s, string([]rune{
tcell.RuneVLine,
tcell.RuneDiamond,
tcell.RuneVLine,
tcell.RuneUArrow,
tcell.RuneVLine,
}))
Putln(s, string([]rune{
tcell.RuneLLCorner,
tcell.RuneHLine,
tcell.RuneBTee,
tcell.RuneHLine,
tcell.RuneLRCorner,
}))
s.Show()
go func() {
for {
ev := s.PollEvent()
switch ev := ev.(type) {
case *tcell.EventKey:
switch ev.Key() {
case tcell.KeyEscape, tcell.KeyEnter:
close(quit)
return
case tcell.KeyCtrlL:
s.Sync()
}
case *tcell.EventResize:
s.Sync()
}
}
}()
<-quit
s.Fini()
}

View File

@ -108,6 +108,11 @@ func (s *cScreen) Init() error {
return nil
}
func (s *cScreen) CharacterSet() string {
// We are always UTF-16LE on Windows
return "UTF-16LE"
}
func (s *cScreen) EnableMouse() {
s.setInMode(modeResizeEn | modeMouseEn)
}

View File

@ -1,4 +1,4 @@
// Generated by ./mkinfo (darwin/amd64) on Sun Oct 4 12:25:12 PDT 2015.
// Generated by ./mkinfo (darwin/amd64) on Sun Oct 4 16:17:53 PDT 2015.
// DO NOT HAND-EDIT
package tcell
@ -33,6 +33,7 @@ func init() {
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
PadChar: "\x00",
AltChars: "jjkkllmmnnqqttuuvvwwxx",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
CursorBack1: "\b",
CursorUp1: "\x1b[A",
@ -101,6 +102,9 @@ func init() {
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
PadChar: "\x00",
AltChars: "+\x10,\x11-\x18.\x190\xdb`\x04a\xb1f\xf8g\xf1h\xb0j\xd9k\xbfl\xdam\xc0n\xc5o~p\xc4q\xc4r\xc4s_t\xc3u\xb4v\xc1w\xc2x\xb3y\xf3z\xf2{\xe3|\xd8}\x9c~\xfe",
EnterAcs: "\x1b[11m",
ExitAcs: "\x1b[10m",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
CursorBack1: "\x1b[D",
CursorUp1: "\x1b[A",
@ -134,6 +138,9 @@ func init() {
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
PadChar: "\x00",
AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
Mouse: "\x1b[M",
MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
@ -254,6 +261,9 @@ func init() {
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
PadChar: "\x00",
AltChars: "+\x10,\x11-\x18.\x190\xdb`\x04a\xb1f\xf8g\xf1h\xb0j\xd9k\xbfl\xdam\xc0n\xc5o~p\xc4q\xc4r\xc4s_t\xc3u\xb4v\xc1w\xc2x\xb3y\xf3z\xf2{\xe3|\xd8}\x9c~\xfe",
EnterAcs: "\x1b[11m",
ExitAcs: "\x1b[10m",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
CursorBack1: "\b",
CursorUp1: "\x1b[A",
@ -283,6 +293,9 @@ func init() {
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
PadChar: "\x00",
AltChars: "+\x10,\x11-\x18.\x190\xdb`\x04a\xb1f\xf8g\xf1h\xb0j\xd9k\xbfl\xdam\xc0n\xc5o~p\xc4q\xc4r\xc4s_t\xc3u\xb4v\xc1w\xc2x\xb3y\xf3z\xf2{\xe3|\xd8}\x9c~\xfe",
EnterAcs: "\x1b[11m",
ExitAcs: "\x1b[10m",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
CursorBack1: "\b",
CursorUp1: "\x1b[A",
@ -505,6 +518,9 @@ func init() {
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
PadChar: "\x00",
AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
CursorBack1: "\b",
CursorUp1: "\x1b[A",
@ -559,6 +575,9 @@ func init() {
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
PadChar: "\x00",
AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
Mouse: "\x1b[M",
MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
@ -640,6 +659,9 @@ 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",
PadChar: "\x00",
AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
Mouse: "\x1b[M",
MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
@ -739,6 +761,9 @@ func init() {
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
PadChar: "\x00",
AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
Mouse: "\x1b[M",
MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
@ -840,6 +865,9 @@ 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",
PadChar: "\x00",
AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
Mouse: "\x1b[M",
MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
@ -936,6 +964,8 @@ func init() {
EnterKeypad: "\x1b&s1A",
ExitKeypad: "\x1b&s0A",
PadChar: "\x00",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
SetCursor: "\x1b&a%p1%dy%p2%dC",
CursorBack1: "\b",
CursorUp1: "\x1bA",
@ -994,6 +1024,9 @@ func init() {
ExitKeypad: "\x1b[?1l\x1b>",
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
Mouse: "\x1b[M",
MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
@ -1093,6 +1126,9 @@ func init() {
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
PadChar: "\x00",
AltChars: "``aajjkkllmmnnooppqqrrssttuuvvwwxx~~",
EnterAcs: "\x1b(0",
ExitAcs: "\x1b(B",
Mouse: "\x1b[M",
MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
@ -1146,6 +1182,9 @@ func init() {
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
PadChar: "\x00",
AltChars: "+\x10,\x11-\x18.\x190\xdb`\x04a\xb1f\xf8g\xf1h\xb0i\xcej\xd9k\xbfl\xdam\xc0n\xc5o~p\xc4q\xc4r\xc4s_t\xc3u\xb4v\xc1w\xc2x\xb3y\xf3z\xf2{\xe3|\xd8}\x9c~\xfe",
EnterAcs: "\x1b[11m",
ExitAcs: "\x1b[10m",
Mouse: "\x1b[M",
MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
@ -1199,6 +1238,9 @@ func init() {
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
PadChar: "\x00",
AltChars: "+\x10,\x11-\x18.\x190\xdb`\x04a\xb1f\xf8g\xf1h\xb0j\xd9k\xbfl\xdam\xc0n\xc5o~p\xc4q\xc4r\xc4s_t\xc3u\xb4v\xc1w\xc2x\xb3y\xf3z\xf2{\xe3|\xd8}\x9c~\xfe",
EnterAcs: "\x1b[12m",
ExitAcs: "\x1b[10m",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
CursorBack1: "\x1b[D",
CursorUp1: "\x1b[A",
@ -1230,6 +1272,9 @@ func init() {
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
PadChar: "\x00",
AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
Mouse: "\x1b[M",
MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
@ -1313,6 +1358,9 @@ func init() {
SetFg: "\x1b[%?%p1%{8}%<%t%p1%{30}%+%e%p1%'R'%+%;%dm",
SetBg: "\x1b[%?%p1%{8}%<%t%p1%'('%+%e%p1%{92}%+%;%dm",
PadChar: "\x00",
AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
Mouse: "\x1b[M",
MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
@ -1396,6 +1444,9 @@ 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",
PadChar: "\x00",
AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
Mouse: "\x1b[M",
MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
@ -1479,6 +1530,9 @@ func init() {
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
PadChar: "\x00",
AltChars: "++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
Mouse: "\x1b[M",
MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
@ -1650,6 +1704,8 @@ func init() {
Underline: "\x1bG8",
Reverse: "\x1bG4",
PadChar: "\x00",
EnterAcs: "\x1b$",
ExitAcs: "\x1b%%",
SetCursor: "\x1b=%p1%' '%+%c%p2%' '%+%c$<3/>",
CursorBack1: "\b",
CursorUp1: "\v",
@ -1705,6 +1761,9 @@ func init() {
Underline: "\x1bG8",
Reverse: "\x1bG4",
PadChar: "\x00",
AltChars: "b\tc\fd\re\ni\v",
EnterAcs: "\x15",
ExitAcs: "\x18",
SetCursor: "\x1b=%p1%' '%+%c%p2%' '%+%c",
CursorBack1: "\b",
CursorUp1: "\v",
@ -1737,6 +1796,8 @@ func init() {
AttrOff: "\x1b[m",
Underline: "\x1b[4m",
PadChar: "\x00",
EnterAcs: "\x1b(B",
ExitAcs: "\x1b(B",
SetCursor: "\x1b[%i%p1%d;%p2%df",
CursorBack1: "\b",
CursorUp1: "\x1bM",
@ -1763,6 +1824,9 @@ func init() {
Bell: "\a",
Clear: "\x1bH\x1bJ",
PadChar: "\x00",
AltChars: "ffgghhompoqqss.k",
EnterAcs: "\x1bF",
ExitAcs: "\x1bG",
SetCursor: "\x1bY%p1%' '%+%c%p2%' '%+%c",
CursorBack1: "\x1bD",
CursorUp1: "\x1bA",
@ -1787,6 +1851,9 @@ func init() {
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
PadChar: "\x00",
AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
SetCursor: "\x1b[%i%p1%d;%p2%dH$<5>",
CursorBack1: "\b",
CursorUp1: "\x1b[A$<2>",
@ -1820,6 +1887,9 @@ func init() {
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
PadChar: "\x00",
AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
SetCursor: "\x1b[%i%p1%d;%p2%dH$<5>",
CursorBack1: "\b",
CursorUp1: "\x1b[A$<2>",
@ -1852,6 +1922,9 @@ func init() {
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
PadChar: "\x00",
AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x1b(0$<2>",
ExitAcs: "\x1b(B$<4>",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
CursorBack1: "\b",
CursorUp1: "\x1b[A",
@ -1899,6 +1972,9 @@ func init() {
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
PadChar: "\x00",
AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x1b(0",
ExitAcs: "\x1b(B",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
CursorBack1: "\b",
CursorUp1: "\x1b[A",
@ -1948,6 +2024,9 @@ func init() {
EnterKeypad: "\x1b[?1h\x1b=",
ExitKeypad: "\x1b[?1l\x1b>",
PadChar: "\x00",
AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x1b(0",
ExitAcs: "\x1b(B",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
CursorBack1: "\b",
CursorUp1: "\x1b[A",
@ -1979,6 +2058,9 @@ func init() {
EnterKeypad: "\x1b=",
ExitKeypad: "\x1b>",
PadChar: "\x00",
AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x1b(0$<2>",
ExitAcs: "\x1b(B$<4>",
SetCursor: "\x1b[%i%p1%d;%p2%dH$<10>",
CursorBack1: "\b",
CursorUp1: "\x1b[A",
@ -2015,6 +2097,9 @@ func init() {
Dim: "\x1b`7\x1b)",
Reverse: "\x1b`6\x1b)",
PadChar: "\x00",
AltChars: "0wa_h[jukslrmqnxqzttuyv]wpxv",
EnterAcs: "\x1bH\x02",
ExitAcs: "\x1bH\x03",
SetCursor: "\x1b=%p1%' '%+%c%p2%' '%+%c",
CursorBack1: "\b",
CursorUp1: "\v",
@ -2064,6 +2149,9 @@ func init() {
Blink: "\x1bG2",
Reverse: "\x1bG4",
PadChar: "\x00",
AltChars: "+/,.0[iha2fxgqh1jYk?lZm@nEqDtCu4vAwBx3yszr{c~~",
EnterAcs: "\x1bcE",
ExitAcs: "\x1bcD",
SetCursor: "\x1b=%p1%' '%+%c%p2%' '%+%c",
CursorBack1: "\b",
CursorUp1: "\v",
@ -2113,6 +2201,9 @@ func init() {
EnterKeypad: "\x1b[?1h",
ExitKeypad: "\x1b[?1l",
PadChar: "\x00",
AltChars: "``aaffggjjkkllmmnnooqqssttuuvvwwxx{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
CursorBack1: "\b$<1>",
CursorUp1: "\x1bM",
@ -2160,6 +2251,9 @@ func init() {
EnterKeypad: "\x1b[?1h",
ExitKeypad: "\x1b[?1l",
PadChar: "\x00",
AltChars: "``aaffggjjkkllmmnnooqqssttuuvvwwxx{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
CursorBack1: "\b$<1>",
CursorUp1: "\x1bM",
@ -2210,6 +2304,9 @@ func init() {
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
PadChar: "\x00",
AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x0e",
ExitAcs: "\x0f",
Mouse: "\x1b[M",
MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
@ -2335,6 +2432,9 @@ func init() {
ExitKeypad: "\x1b[?1l\x1b>",
SetFg: "\x1b[3%p1%dm",
SetBg: "\x1b[4%p1%dm",
AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x1b(0",
ExitAcs: "\x1b(B",
Mouse: "\x1b[M",
MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
@ -2436,6 +2536,9 @@ 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",
AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
EnterAcs: "\x1b(0",
ExitAcs: "\x1b(B",
Mouse: "\x1b[M",
MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c",
SetCursor: "\x1b[%i%p1%d;%p2%dH",

File diff suppressed because it is too large Load Diff

View File

@ -208,6 +208,9 @@ func getinfo(name string) (*tcell.Terminfo, error) {
t.KeyPrint = tigetstr("kprt")
t.KeyHelp = tigetstr("khlp")
t.KeyClear = tigetstr("kclr")
t.AltChars = tigetstr("acsc")
t.EnterAcs = tigetstr("smacs")
t.ExitAcs = tigetstr("rmacs")
t.Mouse = tigetstr("kmous")
// If the kmous entry is present, then we need to record the
// the codes to enter and exit mouse mode. Sadly, this is not
@ -225,7 +228,7 @@ func getinfo(name string) (*tcell.Terminfo, error) {
// 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.MouseMode = "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;"+
t.MouseMode = "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;" +
"\x1b[?1000%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c"
}
// We only support colors in ANSI 8 or 256 color mode.
@ -318,6 +321,9 @@ func dotGoInfo(w io.Writer, t *tcell.Terminfo) {
dotGoAddStr(w, "SetFg", t.SetFg)
dotGoAddStr(w, "SetBg", t.SetBg)
dotGoAddStr(w, "PadChar", t.PadChar)
dotGoAddStr(w, "AltChars", t.AltChars)
dotGoAddStr(w, "EnterAcs", t.EnterAcs)
dotGoAddStr(w, "ExitAcs", t.ExitAcs)
dotGoAddStr(w, "Mouse", t.Mouse)
dotGoAddStr(w, "MouseMode", t.MouseMode)
dotGoAddStr(w, "SetCursor", t.SetCursor)

55
runes.go Normal file
View File

@ -0,0 +1,55 @@
// 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
// The names of these constants are chosen to match Terminfo names,
// modulo case, and changing the prefix from ACS_ to Rune. These are
// the runes we provide extra special handling for, with ASCII fallbacks
// for terminals that lack them.
const (
RuneSterling = '£'
RuneDArrow = '↓'
RuneLArrow = '←'
RuneRArrow = '→'
RuneUArrow = '↑'
RuneBullet = '·'
RuneBoard = '░'
RuneCkBoard = '▒'
RuneDegree = '°'
RuneDiamond = '◆'
RuneGEqual = '≥'
RunePi = 'π'
RuneHLine = '─'
RuneLantern = '§'
RunePlus = '┼'
RuneLEqual = '≤'
RuneLLCorner = '└'
RuneLRCorner = '┘'
RuneNEqual = '≠'
RunePlMinus = '±'
RuneS1 = '⎺'
RuneS3 = '⎻'
RuneS7 = '⎼'
RuneS9 = '⎽'
RuneBlock = '█'
RuneTTee = '┬'
RuneRTee = '┤'
RuneLTee = '├'
RuneBTee = '┴'
RuneULCorner = '┌'
RuneURCorner = '┐'
RuneVLine = '│'
)

View File

@ -110,6 +110,13 @@ type Screen interface {
// (e.g. to clear up on screen corruption caused by some other program),
// or during a resize event.
Sync()
// CharacterSet() returns information about the character set.
// This isn't the full locale, but it does give us the input/ouput
// character set. Note that this is just for diagnostic purposes,
// we normally translate input/output to/from UTF-8, regardless of
// what the user's environment is.
CharacterSet() string
}
func NewScreen() (Screen, error) {

View File

@ -266,8 +266,8 @@ const (
KeyEnter = Key(tcell.KeyEnter)
KeySpace = Key(tcell.KeySpace)
KeyEsc = Key(tcell.KeyEscape)
KeyPgdn = Key(tcell.KeyPgDn)
KeyPgup = Key(tcell.KeyPgUp)
KeyPgdn = Key(tcell.KeyPgDn)
KeyPgup = Key(tcell.KeyPgUp)
MouseLeft = Key(tcell.KeyF63) // arbitrary assignments
MouseRight = Key(tcell.KeyF62)
MouseMiddle = Key(tcell.KeyF61)

View File

@ -138,6 +138,9 @@ type Terminfo struct {
KeyCancel string `json:"kcan,omitempty"` // kcan
Mouse string `json:"kmous,omitempty"` // kmous
MouseMode string `json:"XM,omitempty"` // XM
AltChars string `json:"acsc,omitempty"` // acsc
EnterAcs string `json:"smacs,omitempty"` // smacs
ExitAcs string `json:"rmacs,omitempty"` // rmacs
}
type stack []string

View File

@ -35,6 +35,7 @@ func NewTerminfoScreen() (Screen, error) {
t.mouse = []byte(ti.Mouse)
}
t.prepareKeys()
t.buildAcsMap()
t.w = ti.Columns
t.h = ti.Lines
t.sigwinch = make(chan os.Signal, 1)
@ -73,6 +74,8 @@ type tScreen struct {
tiosp *termiosPrivate
baud int
wasbtn bool
acs map[rune]string
charset string
sync.Mutex
}
@ -80,6 +83,7 @@ type tScreen struct {
func (t *tScreen) Init() error {
t.evch = make(chan Event, 2)
t.indoneq = make(chan struct{})
t.charset = "UTF-8"
if e := t.termioInit(); e != nil {
return e
}
@ -310,16 +314,50 @@ func (t *tScreen) drawCell(x, y int, cell *Cell) {
width := int(cell.Width)
var str string
if len(cell.Ch) == 0 {
str = " "
} else {
str = string(cell.Ch)
}
if width == 2 && x >= t.w-1 {
// too wide to fit; emit space instead
width = 1
str = " "
switch t.charset {
case "UTF-8":
if len(cell.Ch) == 0 {
str = " "
width = 1
} else {
str = string(cell.Ch)
}
if width == 2 && x >= t.w-1 {
// too wide to fit; emit space instead
width = 1
str = " "
}
default:
// Non-Unicode systems. Make do.
switch len(cell.Ch) {
case 0:
str = " "
width = 1
default:
// For combining characters, we just drop the
// combining mark -- its probably the best we can
// do in terms of compromises.
if r := cell.Ch[0]; r < 0x80 {
str = string(r)
} else if repl, ok := t.acs[cell.Ch[0]]; ok {
str = repl
} else {
str = "?"
}
}
if cell.Width > 1 {
// No FullWidth character support
if x < t.w-1 {
str = "? "
width = 2
} else {
str = "?"
width = 1
}
}
}
io.WriteString(t.out, str)
t.cy = y
t.cx = x + width
@ -391,6 +429,9 @@ func (t *tScreen) draw() {
continue
}
t.drawCell(col, row, cell)
if cell.Width > 1 {
col++
}
cell.Dirty = false
}
}
@ -450,6 +491,73 @@ func (t *tScreen) PollEvent() Event {
}
}
// bulidAcsMap builds a map of characters that we translate from Unicode to
// alternate character encodings. To do this, we use the standard VT100 ACS
// maps. This is only done if the terminal lacks support for Unicode; we
// always prefer to emit Unicode glyphs when we are able.
type acsMap struct {
utf rune // UTF-8 glyph
vt100 rune // VT100 name
ascii string // ASCII default
}
func (t *tScreen) buildAcsMap() {
acsstr := t.ti.AltChars
vtNames := []acsMap{
{RuneSterling, '}', "f"},
{RuneDArrow, '.', "v"},
{RuneLArrow, ',', "<"},
{RuneRArrow, '+', ">"},
{RuneUArrow, '-', "^"},
{RuneBullet, '~', "o"},
{RuneBoard, 'h', "#"},
{RuneCkBoard, 'a', ":"},
{RuneDegree, 'f', "\\"},
{RuneDiamond, '`', "+"},
{RuneGEqual, 'z', ">"},
{RunePi, '{', "*"},
{RuneHLine, 'q', "-"},
{RuneLantern, 'i', "#"},
{RunePlus, 'n', "+"},
{RuneLEqual, 'y', "<"},
{RuneLLCorner, 'm', "+"},
{RuneLRCorner, 'j', "+"},
{RuneNEqual, '|', "!"},
{RunePlMinus, 'g', "#"},
{RuneS1, 'o', "~"},
{RuneS3, 'p', "-"},
{RuneS7, 'r', "-"},
{RuneS9, 's', "_"},
{RuneBlock, '0', "#"},
{RuneTTee, 'w', "+"},
{RuneRTee, 'u', "+"},
{RuneLTee, 't', "+"},
{RuneBTee, 'v', "+"},
{RuneULCorner, 'l', "+"},
{RuneURCorner, 'k', "+"},
{RuneVLine, 'x', "|"},
}
t.acs = make(map[rune]string)
for i := range vtNames {
// prefill defaults
t.acs[vtNames[i].utf] = vtNames[i].ascii
}
for len(acsstr) > 2 {
srcv := rune(acsstr[0])
dstv := string(acsstr[1])
// O(n*2), but n is pretty small (30)
for i := range vtNames {
if srcv == vtNames[i].vt100 {
t.acs[vtNames[i].utf] =
t.ti.EnterAcs + dstv + t.ti.ExitAcs
break
}
}
acsstr = acsstr[2:]
}
}
func (t *tScreen) PostEvent(ev Event) {
t.evch <- ev
}
@ -715,13 +823,31 @@ func (t *tScreen) parseRune(buf *bytes.Buffer) (bool, bool) {
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
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:
// This would be an excellent place to transform
// these into UTF-8
ev := NewEventKey(KeyRune, rune(b[0]), ModNone)
t.PostEvent(ev)
buf.ReadByte()
return true, true
}
// Looks like potential escape
return true, false
@ -829,3 +955,7 @@ func (t *tScreen) Sync() {
t.draw()
t.Unlock()
}
func (t *tScreen) CharacterSet() string {
return t.charset
}

View File

@ -19,6 +19,7 @@ package tcell
import (
"os"
"os/signal"
"strings"
"syscall"
)
@ -171,6 +172,8 @@ func (t *tScreen) termioInit() error {
t.h = h
}
t.charset = t.getCharset()
return nil
failed:
@ -199,6 +202,32 @@ func (t *tScreen) termioFini() {
}
}
func (t *tScreen) getCharset() string {
// Let's also determine the character set. This can help us later.
// Per POSIX, we search for LC_ALL first, then LC_CTYPE, and
// finally LANG. First one set wins.
locale := ""
if locale = os.Getenv("LC_ALL"); locale == "" {
if locale = os.Getenv("LC_CTYPE"); locale == "" {
locale = os.Getenv("LANG")
}
}
if locale == "POSIX" || locale == "C" {
return "US-ASCII"
}
if i := strings.IndexRune(locale, '@'); i >= 0 {
locale = locale[:i]
}
if i := strings.IndexRune(locale, '.'); i >= 0 {
locale = locale[i+1:]
}
if locale == "" {
return "UTF-8"
}
// XXX: add support for aliases
return locale
}
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 {

View File

@ -20,7 +20,7 @@ import (
"errors"
)
type termiosPrivate struct {}
type termiosPrivate struct{}
func (t *tScreen) termioInit() error {
return errors.New("no termios support on this platform")