2018-08-27 22:53:10 +03:00
|
|
|
package screen
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"os"
|
2018-08-28 21:24:59 +03:00
|
|
|
"sync"
|
2018-08-27 22:53:10 +03:00
|
|
|
|
2019-02-04 07:17:24 +03:00
|
|
|
"github.com/zyedidia/micro/internal/config"
|
2020-01-02 05:29:18 +03:00
|
|
|
"github.com/zyedidia/micro/internal/util"
|
2020-01-01 04:15:45 +03:00
|
|
|
"github.com/zyedidia/tcell"
|
2018-08-27 22:53:10 +03:00
|
|
|
)
|
|
|
|
|
2018-12-31 22:46:04 +03:00
|
|
|
// Screen is the tcell screen we use to draw to the terminal
|
|
|
|
// Synchronization is used because we poll the screen on a separate
|
|
|
|
// thread and sometimes the screen is shut down by the main thread
|
|
|
|
// (for example on TermMessage) so we don't want to poll a nil/shutdown
|
|
|
|
// screen. TODO: maybe we should worry about polling and drawing at the
|
|
|
|
// same time too.
|
2018-08-27 22:53:10 +03:00
|
|
|
var Screen tcell.Screen
|
2020-01-02 06:40:51 +03:00
|
|
|
|
|
|
|
// The lock is necessary since the screen is polled on a separate thread
|
2018-08-28 21:24:59 +03:00
|
|
|
var lock sync.Mutex
|
2020-01-02 06:40:51 +03:00
|
|
|
|
|
|
|
// DrawChan is a channel that will cause the screen to redraw when
|
|
|
|
// written to even if no event user event has occurred
|
2019-01-11 00:37:05 +03:00
|
|
|
var DrawChan chan bool
|
2018-08-28 21:24:59 +03:00
|
|
|
|
2020-01-02 06:40:51 +03:00
|
|
|
// Lock locks the screen lock
|
2018-08-28 21:24:59 +03:00
|
|
|
func Lock() {
|
|
|
|
lock.Lock()
|
|
|
|
}
|
|
|
|
|
2020-01-02 06:40:51 +03:00
|
|
|
// Unlock unlocks the screen lock
|
2018-08-28 21:24:59 +03:00
|
|
|
func Unlock() {
|
|
|
|
lock.Unlock()
|
|
|
|
}
|
|
|
|
|
2020-01-02 06:40:51 +03:00
|
|
|
// Redraw schedules a redraw with the draw channel
|
2019-01-11 00:37:05 +03:00
|
|
|
func Redraw() {
|
|
|
|
DrawChan <- true
|
|
|
|
}
|
2018-08-28 21:24:59 +03:00
|
|
|
|
2020-01-02 06:40:51 +03:00
|
|
|
type screenCell struct {
|
|
|
|
x, y int
|
|
|
|
r rune
|
|
|
|
combc []rune
|
|
|
|
style tcell.Style
|
|
|
|
}
|
|
|
|
|
|
|
|
var lastCursor screenCell
|
|
|
|
|
|
|
|
// ShowFakeCursor displays a cursor at the given position by modifying the
|
|
|
|
// style of the given column instead of actually using the terminal cursor
|
|
|
|
// This can be useful in certain terminals such as the windows console where
|
|
|
|
// modifying the cursor location is slow and frequent modifications cause flashing
|
|
|
|
// This keeps track of the most recent fake cursor location and resets it when
|
|
|
|
// a new fake cursor location is specified
|
2020-01-02 05:29:18 +03:00
|
|
|
func ShowFakeCursor(x, y int) {
|
2020-01-02 06:40:51 +03:00
|
|
|
r, combc, style, _ := Screen.GetContent(x, y)
|
|
|
|
Screen.SetContent(lastCursor.x, lastCursor.y, lastCursor.r, lastCursor.combc, lastCursor.style)
|
|
|
|
Screen.SetContent(x, y, r, combc, config.DefStyle.Reverse(true))
|
|
|
|
|
|
|
|
lastCursor.x, lastCursor.y = x, y
|
|
|
|
lastCursor.r = r
|
|
|
|
lastCursor.combc = combc
|
|
|
|
lastCursor.style = style
|
|
|
|
}
|
|
|
|
|
|
|
|
// ShowFakeCursorMulti is the same as ShowFakeCursor except it does not
|
|
|
|
// reset previous locations of the cursor
|
|
|
|
// Fake cursors are also necessary to display multiple cursors
|
|
|
|
func ShowFakeCursorMulti(x, y int) {
|
2020-01-02 05:29:18 +03:00
|
|
|
r, _, _, _ := Screen.GetContent(x, y)
|
|
|
|
Screen.SetContent(x, y, r, nil, config.DefStyle.Reverse(true))
|
|
|
|
}
|
|
|
|
|
2020-01-02 06:40:51 +03:00
|
|
|
// ShowCursor puts the cursor at the given location using a fake cursor
|
|
|
|
// if enabled or using the terminal cursor otherwise
|
|
|
|
// By default only the windows console will use a fake cursor
|
2020-01-02 05:29:18 +03:00
|
|
|
func ShowCursor(x, y int) {
|
|
|
|
if util.FakeCursor {
|
|
|
|
ShowFakeCursor(x, y)
|
|
|
|
} else {
|
|
|
|
Screen.ShowCursor(x, y)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-02 06:40:51 +03:00
|
|
|
// SetContent sets a cell at a point on the screen and makes sure that it is
|
|
|
|
// synced with the last cursor location
|
|
|
|
func SetContent(x, y int, mainc rune, combc []rune, style tcell.Style) {
|
|
|
|
Screen.SetContent(x, y, mainc, combc, style)
|
|
|
|
if util.FakeCursor && lastCursor.x == x && lastCursor.y == y {
|
|
|
|
lastCursor.r = mainc
|
|
|
|
lastCursor.style = style
|
|
|
|
lastCursor.combc = combc
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-31 22:46:04 +03:00
|
|
|
// TempFini shuts the screen down temporarily
|
2019-01-11 00:37:05 +03:00
|
|
|
func TempFini() bool {
|
|
|
|
screenWasNil := Screen == nil
|
2018-08-28 21:24:59 +03:00
|
|
|
|
|
|
|
if !screenWasNil {
|
|
|
|
Screen.Fini()
|
2019-01-11 00:37:05 +03:00
|
|
|
Lock()
|
2018-08-28 21:24:59 +03:00
|
|
|
Screen = nil
|
|
|
|
}
|
2019-01-11 00:37:05 +03:00
|
|
|
return screenWasNil
|
2018-08-28 21:24:59 +03:00
|
|
|
}
|
|
|
|
|
2018-12-31 22:46:04 +03:00
|
|
|
// TempStart restarts the screen after it was temporarily disabled
|
2019-01-11 00:37:05 +03:00
|
|
|
func TempStart(screenWasNil bool) {
|
2018-08-28 21:24:59 +03:00
|
|
|
if !screenWasNil {
|
|
|
|
Init()
|
|
|
|
Unlock()
|
|
|
|
}
|
|
|
|
}
|
2018-08-27 22:53:10 +03:00
|
|
|
|
|
|
|
// Init creates and initializes the tcell screen
|
|
|
|
func Init() {
|
2019-01-15 03:57:19 +03:00
|
|
|
DrawChan = make(chan bool, 8)
|
2019-01-11 00:37:05 +03:00
|
|
|
|
2018-08-27 22:53:10 +03:00
|
|
|
// Should we enable true color?
|
|
|
|
truecolor := os.Getenv("MICRO_TRUECOLOR") == "1"
|
|
|
|
|
2020-01-01 07:09:33 +03:00
|
|
|
if !truecolor {
|
|
|
|
os.Setenv("TCELL_TRUECOLOR", "disable")
|
2018-08-27 22:53:10 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Initilize tcell
|
|
|
|
var err error
|
|
|
|
Screen, err = tcell.NewScreen()
|
|
|
|
if err != nil {
|
2020-01-01 07:09:33 +03:00
|
|
|
fmt.Println(err)
|
|
|
|
fmt.Println("Fatal: Micro could not initialize a Screen.")
|
|
|
|
os.Exit(1)
|
2018-08-27 22:53:10 +03:00
|
|
|
}
|
|
|
|
if err = Screen.Init(); err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
if config.GetGlobalOption("mouse").(bool) {
|
|
|
|
Screen.EnableMouse()
|
|
|
|
}
|
|
|
|
}
|