tcell/termbox/compat.go

383 lines
9.3 KiB
Go

// Copyright 2020 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 termbox is a compatibility layer to allow tcell to emulate
// the github.com/nsf/termbox package.
package termbox
import (
"errors"
"github.com/gdamore/tcell/v2"
)
var screen tcell.Screen
var outMode OutputMode
// Init initializes the screen for use.
func Init() error {
outMode = OutputNormal
if s, e := tcell.NewScreen(); e != nil {
return e
} else if e = s.Init(); e != nil {
return e
} else {
screen = s
return nil
}
}
// Close cleans up the terminal, restoring terminal modes, etc.
func Close() {
screen.Fini()
}
// Flush updates the screen.
func Flush() error {
screen.Show()
return nil
}
// SetCursor displays the terminal cursor at the given location.
func SetCursor(x, y int) {
screen.ShowCursor(x, y)
}
// HideCursor hides the terminal cursor.
func HideCursor() {
SetCursor(-1, -1)
}
// Size returns the screen size as width, height in character cells.
func Size() (int, int) {
return screen.Size()
}
// Attribute affects the presentation of characters, such as color, boldness,
// and so forth.
type Attribute uint16
// Colors first. The order here is significant.
const (
ColorDefault Attribute = iota
ColorBlack
ColorRed
ColorGreen
ColorYellow
ColorBlue
ColorMagenta
ColorCyan
ColorWhite
)
// Other attributes.
const (
AttrBold Attribute = 1 << (9 + iota)
AttrUnderline
AttrReverse
)
func fixColor(c tcell.Color) tcell.Color {
if c == tcell.ColorDefault {
return c
}
switch outMode {
case OutputNormal:
c = tcell.PaletteColor(int(c) & 0xf)
case Output256:
c = tcell.PaletteColor(int(c) & 0xff)
case Output216:
c = tcell.PaletteColor(int(c)%216 + 16)
case OutputGrayscale:
c %= tcell.PaletteColor(int(c)%24 + 232)
default:
c = tcell.ColorDefault
}
return c
}
func mkStyle(fg, bg Attribute) tcell.Style {
st := tcell.StyleDefault
f := tcell.PaletteColor(int(fg)&0x1ff - 1)
b := tcell.PaletteColor(int(bg)&0x1ff - 1)
f = fixColor(f)
b = fixColor(b)
st = st.Foreground(f).Background(b)
if (fg|bg)&AttrBold != 0 {
st = st.Bold(true)
}
if (fg|bg)&AttrUnderline != 0 {
st = st.Underline(true)
}
if (fg|bg)&AttrReverse != 0 {
st = st.Reverse(true)
}
return st
}
// Clear clears the screen with the given attributes.
func Clear(fg, bg Attribute) {
st := mkStyle(fg, bg)
w, h := screen.Size()
for row := 0; row < h; row++ {
for col := 0; col < w; col++ {
screen.SetContent(col, row, ' ', nil, st)
}
}
}
// InputMode is not used.
type InputMode int
// Unused input modes; here for compatibility.
const (
InputCurrent InputMode = iota
InputEsc
InputAlt
InputMouse
)
// SetInputMode does not do anything in this version.
func SetInputMode(mode InputMode) InputMode {
// We don't do anything else right now
return InputEsc
}
// OutputMode represents an output mode, which determines how colors
// are used. See the termbox documentation for an explanation.
type OutputMode int
// OutputMode values.
const (
OutputCurrent OutputMode = iota
OutputNormal
Output256
Output216
OutputGrayscale
)
// SetOutputMode is used to set the color palette used.
func SetOutputMode(mode OutputMode) OutputMode {
if screen.Colors() < 256 {
mode = OutputNormal
}
switch mode {
case OutputCurrent:
return outMode
case OutputNormal, Output256, Output216, OutputGrayscale:
outMode = mode
return mode
default:
return outMode
}
}
// Sync forces a resync of the screen.
func Sync() error {
screen.Sync()
return nil
}
// SetCell sets the character cell at a given location to the given
// content (rune) and attributes.
func SetCell(x, y int, ch rune, fg, bg Attribute) {
st := mkStyle(fg, bg)
screen.SetContent(x, y, ch, nil, st)
}
// EventType represents the type of event.
type EventType uint8
// Modifier represents the possible modifier keys.
type Modifier tcell.ModMask
// Key is a key press.
type Key tcell.Key
// Event represents an event like a key press, mouse action, or window resize.
type Event struct {
Type EventType
Mod Modifier
Key Key
Ch rune
Width int
Height int
Err error
MouseX int
MouseY int
N int
}
// Event types.
const (
EventNone EventType = iota
EventKey
EventResize
EventMouse
EventInterrupt
EventError
EventRaw
)
// Keys codes.
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)
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)
KeyCtrlUnderscore = Key(tcell.KeyCtrlUnderscore)
KeyBackspace = Key(tcell.KeyBackspace)
KeyBackspace2 = Key(tcell.KeyBackspace2)
KeyTab = Key(tcell.KeyTab)
KeyEnter = Key(tcell.KeyEnter)
KeyEsc = Key(tcell.KeyEscape)
KeyPgdn = Key(tcell.KeyPgDn)
KeyPgup = Key(tcell.KeyPgUp)
KeySpace = Key(tcell.Key(' '))
KeyTilde = Key(tcell.Key('~'))
KeyCtrlSpace = Key(tcell.KeyCtrlSpace)
// The following assignments are provided for termbox
// compatibility. Their use in applications is discouraged.
// The mouse keys are completely not supported as tcell uses
// a separate mouse event instead of key strokes.
MouseLeft = Key(tcell.KeyF63) // arbitrary assignments
MouseRight = Key(tcell.KeyF62)
MouseMiddle = Key(tcell.KeyF61)
MouseRelease = Key(tcell.KeyF60)
MouseWheelUp = Key(tcell.KeyF59)
MouseWheelDown = Key(tcell.KeyF58)
KeyCtrlTilde = Key(tcell.KeyCtrlSpace) // termbox defines a bunch of weird ones, don't use them
KeyCtrl2 = Key(tcell.KeyNUL)
KeyCtrl3 = Key(tcell.KeyEscape)
KeyCtrl4 = Key(tcell.KeyCtrlBackslash)
KeyCtrl5 = Key(tcell.KeyCtrlRightSq)
KeyCtrl6 = Key(tcell.KeyCtrlCarat)
KeyCtrl7 = Key(tcell.KeyCtrlUnderscore)
KeyCtrl8 = Key(tcell.KeyDEL)
KeyCtrlSlash = Key(tcell.KeyCtrlUnderscore)
KeyCtrlRsqBracket = Key(tcell.KeyCtrlRightSq)
KeyCtrlBackslash = Key(tcell.KeyCtrlBackslash)
KeyCtrlLsqBracket = Key(tcell.KeyCtrlLeftSq)
)
// Modifiers.
const (
ModAlt = Modifier(tcell.ModAlt)
)
func makeEvent(tev tcell.Event) Event {
switch tev := tev.(type) {
case *tcell.EventInterrupt:
return Event{Type: EventInterrupt}
case *tcell.EventResize:
w, h := tev.Size()
return Event{Type: EventResize, Width: w, Height: h}
case *tcell.EventKey:
k := tev.Key()
ch := rune(0)
if k == tcell.KeyRune {
ch = tev.Rune()
if ch == ' ' {
k = tcell.Key(' ')
} else {
k = tcell.Key(0)
}
}
mod := tev.Modifiers()
return Event{
Type: EventKey,
Key: Key(k),
Ch: ch,
Mod: Modifier(mod),
}
default:
return Event{Type: EventNone}
}
}
// ParseEvent is not supported.
func ParseEvent(data []byte) Event {
// Not supported
return Event{Type: EventError, Err: errors.New("no raw events")}
}
// PollRawEvent is not supported.
func PollRawEvent(data []byte) Event {
// Not supported
return Event{Type: EventError, Err: errors.New("no raw events")}
}
// PollEvent blocks until an event is ready, and then returns it.
func PollEvent() Event {
ev := screen.PollEvent()
return makeEvent(ev)
}
// Interrupt posts an interrupt event.
func Interrupt() {
screen.PostEvent(tcell.NewEventInterrupt(nil))
}
// Cell represents a single character cell on screen.
type Cell struct {
Ch rune
Fg Attribute
Bg Attribute
}