mirror of https://github.com/liamg/aminal.git
sixel working
This commit is contained in:
parent
0cc73f1b71
commit
20c3c0cb14
|
@ -1,8 +1,15 @@
|
||||||
package buffer
|
package buffer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
|
||||||
|
"github.com/go-gl/gl/all-core/gl"
|
||||||
|
)
|
||||||
|
|
||||||
type Cell struct {
|
type Cell struct {
|
||||||
r rune
|
r rune
|
||||||
attr CellAttributes
|
attr CellAttributes
|
||||||
|
image *image.RGBA
|
||||||
}
|
}
|
||||||
|
|
||||||
type CellAttributes struct {
|
type CellAttributes struct {
|
||||||
|
@ -16,6 +23,59 @@ type CellAttributes struct {
|
||||||
Hidden bool
|
Hidden bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cell *Cell) Image() *image.RGBA {
|
||||||
|
return cell.image
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cell *Cell) SetImage(img *image.RGBA) {
|
||||||
|
|
||||||
|
cell.image = img
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cell *Cell) DrawImage(x, y float32) {
|
||||||
|
|
||||||
|
if cell.image == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var tex uint32
|
||||||
|
gl.GenTextures(1, &tex)
|
||||||
|
gl.BindTexture(gl.TEXTURE_2D, tex)
|
||||||
|
gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
|
||||||
|
gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
|
||||||
|
|
||||||
|
gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
|
||||||
|
gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
|
||||||
|
|
||||||
|
gl.TexImage2D(
|
||||||
|
gl.TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
gl.RGBA,
|
||||||
|
int32(cell.image.Bounds().Size().X),
|
||||||
|
int32(cell.image.Bounds().Size().Y),
|
||||||
|
0,
|
||||||
|
gl.RGBA,
|
||||||
|
gl.UNSIGNED_BYTE,
|
||||||
|
gl.Ptr(cell.image.Pix),
|
||||||
|
)
|
||||||
|
gl.BindTexture(gl.TEXTURE_2D, 0)
|
||||||
|
|
||||||
|
var w float32 = float32(cell.image.Bounds().Size().X)
|
||||||
|
var h float32 = float32(cell.image.Bounds().Size().Y)
|
||||||
|
|
||||||
|
var readFboId uint32
|
||||||
|
gl.GenFramebuffers(1, &readFboId)
|
||||||
|
gl.BindFramebuffer(gl.READ_FRAMEBUFFER, readFboId)
|
||||||
|
gl.FramebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
|
||||||
|
gl.TEXTURE_2D, tex, 0)
|
||||||
|
gl.BlitFramebuffer(0, 0, int32(w), int32(h),
|
||||||
|
int32(x), int32(y), int32(x+w), int32(y+h),
|
||||||
|
gl.COLOR_BUFFER_BIT, gl.LINEAR)
|
||||||
|
gl.BindFramebuffer(gl.READ_FRAMEBUFFER, 0)
|
||||||
|
gl.DeleteFramebuffers(1, &readFboId)
|
||||||
|
}
|
||||||
|
|
||||||
func (cell *Cell) Attr() CellAttributes {
|
func (cell *Cell) Attr() CellAttributes {
|
||||||
return cell.attr
|
return cell.attr
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
Pq
|
||||||
|
#0;2;0;0;0#1;2;100;100;0#2;2;0;100;0
|
||||||
|
#1~~@@vv@@~~@@~~$
|
||||||
|
#2??}}GG}}??}}??-
|
||||||
|
#1!14@\
|
14
gui/gui.go
14
gui/gui.go
|
@ -81,6 +81,8 @@ func (gui *GUI) resize(w *glfw.Window, width int, height int) {
|
||||||
gui.logger.Debugf("Setting viewport size...")
|
gui.logger.Debugf("Setting viewport size...")
|
||||||
gl.Viewport(0, 0, int32(gui.width), int32(gui.height))
|
gl.Viewport(0, 0, int32(gui.width), int32(gui.height))
|
||||||
|
|
||||||
|
gui.terminal.SetCharSize(gui.renderer.cellWidth, gui.renderer.cellHeight)
|
||||||
|
|
||||||
gui.logger.Debugf("Resize complete!")
|
gui.logger.Debugf("Resize complete!")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -191,6 +193,8 @@ func (gui *GUI) Render() error {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
gui.terminal.SetProgram(program)
|
||||||
|
|
||||||
for !gui.window.ShouldClose() {
|
for !gui.window.ShouldClose() {
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
@ -242,10 +246,20 @@ func (gui *GUI) Render() error {
|
||||||
if hasText {
|
if hasText {
|
||||||
gui.renderer.DrawCellText(cell, uint(x), uint(y), nil)
|
gui.renderer.DrawCellText(cell, uint(x), uint(y), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cell.Image() != nil {
|
||||||
|
ix := float32(x) * gui.renderer.cellWidth
|
||||||
|
iy := float32(gui.height) - (float32(y+1) * gui.renderer.cellHeight)
|
||||||
|
iy -= float32(cell.Image().Bounds().Size().Y)
|
||||||
|
gl.UseProgram(program)
|
||||||
|
cell.DrawImage(ix, iy)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.window.SwapBuffers()
|
gui.window.SwapBuffers()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -214,6 +214,7 @@ func (r *OpenGLRenderer) DrawCellBg(cell buffer.Cell, col uint, row uint, cursor
|
||||||
rect.setColour(bg)
|
rect.setColour(bg)
|
||||||
rect.Draw()
|
rect.Draw()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *OpenGLRenderer) DrawCellText(cell buffer.Cell, col uint, row uint, colour *config.Colour) {
|
func (r *OpenGLRenderer) DrawCellText(cell buffer.Cell, col uint, row uint, colour *config.Colour) {
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 614 B |
|
@ -0,0 +1,240 @@
|
||||||
|
package sixel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-gl/gl/v2.1/gl"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Sixel struct {
|
||||||
|
px map[uint]map[uint]colour
|
||||||
|
width uint
|
||||||
|
height uint
|
||||||
|
}
|
||||||
|
|
||||||
|
type colour [3]uint8
|
||||||
|
|
||||||
|
func decompress(data string) string {
|
||||||
|
|
||||||
|
output := ""
|
||||||
|
|
||||||
|
inMarker := false
|
||||||
|
countStr := ""
|
||||||
|
|
||||||
|
for _, r := range data {
|
||||||
|
|
||||||
|
if !inMarker {
|
||||||
|
if r == '!' {
|
||||||
|
inMarker = true
|
||||||
|
countStr = ""
|
||||||
|
} else {
|
||||||
|
output = fmt.Sprintf("%s%c", output, r)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if r >= 0x30 && r <= 0x39 {
|
||||||
|
countStr = fmt.Sprintf("%s%c", countStr, r)
|
||||||
|
} else {
|
||||||
|
count, _ := strconv.Atoi(countStr)
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
output = fmt.Sprintf("%s%c", output, r)
|
||||||
|
}
|
||||||
|
inMarker = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
// pass in everything after ESC+P and before ST
|
||||||
|
func ParseString(data string) (*Sixel, error) {
|
||||||
|
|
||||||
|
data = decompress(data)
|
||||||
|
|
||||||
|
inHeader := true
|
||||||
|
inColour := false
|
||||||
|
|
||||||
|
six := Sixel{}
|
||||||
|
var x, y uint
|
||||||
|
|
||||||
|
colourStr := ""
|
||||||
|
|
||||||
|
colourMap := map[string]colour{}
|
||||||
|
var selectedColour colour
|
||||||
|
|
||||||
|
headerStr := ""
|
||||||
|
|
||||||
|
remainMode := false
|
||||||
|
|
||||||
|
var ratio uint
|
||||||
|
|
||||||
|
// read p1 p2 p3
|
||||||
|
for i, r := range data {
|
||||||
|
switch true {
|
||||||
|
case inHeader:
|
||||||
|
// todo read p1 p2 p3
|
||||||
|
if r == 'q' {
|
||||||
|
headers := strings.Split(headerStr, ";")
|
||||||
|
switch headers[0] {
|
||||||
|
case "0", "1":
|
||||||
|
ratio = 5
|
||||||
|
case "2":
|
||||||
|
ratio = 3
|
||||||
|
case "", "3", "4", "5", "6":
|
||||||
|
ratio = 2
|
||||||
|
case "7", "8", "9":
|
||||||
|
ratio = 1
|
||||||
|
}
|
||||||
|
if len(headers) > 1 {
|
||||||
|
remainMode = headers[1] == "1"
|
||||||
|
}
|
||||||
|
inHeader = false
|
||||||
|
} else {
|
||||||
|
headerStr = fmt.Sprintf("%s%c", headerStr, r)
|
||||||
|
}
|
||||||
|
case inColour:
|
||||||
|
colourStr = fmt.Sprintf("%s%c", colourStr, r)
|
||||||
|
if i+1 >= len(data) || data[i+1] < 0x30 || data[i+1] > 0x3b {
|
||||||
|
// process colour string
|
||||||
|
inColour = false
|
||||||
|
parts := strings.Split(colourStr, ";")
|
||||||
|
|
||||||
|
// select colour
|
||||||
|
if len(parts) == 1 {
|
||||||
|
c, ok := colourMap[parts[0]]
|
||||||
|
if ok {
|
||||||
|
selectedColour = c
|
||||||
|
}
|
||||||
|
} else if len(parts) == 5 {
|
||||||
|
switch parts[1] {
|
||||||
|
case "1":
|
||||||
|
// HSL
|
||||||
|
return nil, fmt.Errorf("HSL colours are not yet supported")
|
||||||
|
case "2":
|
||||||
|
// RGB
|
||||||
|
r, _ := strconv.Atoi(parts[2])
|
||||||
|
g, _ := strconv.Atoi(parts[3])
|
||||||
|
b, _ := strconv.Atoi(parts[4])
|
||||||
|
colourMap[parts[0]] = colour([3]uint8{
|
||||||
|
uint8(r & 0xff),
|
||||||
|
uint8(g & 0xff),
|
||||||
|
uint8(b & 0xff),
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("Unknown colour definition type: %s", parts[1])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("Invalid colour directive: #%s", colourStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
colourStr = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
switch r {
|
||||||
|
case '-':
|
||||||
|
y += 6
|
||||||
|
x = 0
|
||||||
|
case '$':
|
||||||
|
x = 0
|
||||||
|
case '#':
|
||||||
|
inColour = true
|
||||||
|
default:
|
||||||
|
if r < 63 || r > 126 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
b := (r & 0xff) - 0x3f
|
||||||
|
var bit int
|
||||||
|
for bit = 5; bit >= 0; bit-- {
|
||||||
|
if b&(1<<uint(bit)) > 0 {
|
||||||
|
six.setPixel(x, y+uint(bit), selectedColour, ratio)
|
||||||
|
} else if !remainMode {
|
||||||
|
// @todo use background colour here
|
||||||
|
//six.setPixel(x, y+uint(bit), selectedColour)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &six, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (six *Sixel) Draw() error {
|
||||||
|
rgba := six.RGBA()
|
||||||
|
|
||||||
|
var handle uint32
|
||||||
|
gl.GenTextures(1, &handle)
|
||||||
|
|
||||||
|
target := uint32(gl.TEXTURE_2D)
|
||||||
|
internalFmt := int32(gl.SRGB_ALPHA)
|
||||||
|
format := uint32(gl.RGBA)
|
||||||
|
width := int32(rgba.Rect.Size().X)
|
||||||
|
height := int32(rgba.Rect.Size().Y)
|
||||||
|
pixType := uint32(gl.UNSIGNED_BYTE)
|
||||||
|
dataPtr := gl.Ptr(rgba.Pix)
|
||||||
|
|
||||||
|
gl.ActiveTexture(gl.TEXTURE0)
|
||||||
|
gl.BindTexture(target, handle)
|
||||||
|
|
||||||
|
// set the texture wrapping/filtering options (applies to current bound texture obj)
|
||||||
|
// TODO-cs
|
||||||
|
//gl.TexParameteri(texture.target, gl.TEXTURE_WRAP_R, wrapR)
|
||||||
|
//gl.TexParameteri(texture.target, gl.TEXTURE_WRAP_S, wrapS)
|
||||||
|
gl.TexParameteri(target, gl.TEXTURE_MIN_FILTER, gl.LINEAR) // minification filter
|
||||||
|
gl.TexParameteri(target, gl.TEXTURE_MAG_FILTER, gl.LINEAR) // magnification filter
|
||||||
|
|
||||||
|
gl.TexImage2D(target, 0, internalFmt, width, height, 0, format, pixType, dataPtr)
|
||||||
|
|
||||||
|
// unbind
|
||||||
|
gl.BindTexture(target, 0)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (six *Sixel) setPixel(x, y uint, c colour, vhRatio uint) {
|
||||||
|
|
||||||
|
if six.px == nil {
|
||||||
|
six.px = map[uint]map[uint]colour{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, exists := six.px[x]; !exists {
|
||||||
|
six.px[x] = map[uint]colour{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if x+1 > six.width {
|
||||||
|
six.width = x
|
||||||
|
}
|
||||||
|
|
||||||
|
ay := vhRatio * y
|
||||||
|
|
||||||
|
var i uint
|
||||||
|
for i = 0; i < vhRatio; i++ {
|
||||||
|
if ay+i+1 > six.height {
|
||||||
|
six.height = ay + i + 1
|
||||||
|
}
|
||||||
|
six.px[x][ay+i] = c
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (six *Sixel) RGBA() *image.RGBA {
|
||||||
|
rgba := image.NewRGBA(image.Rect(0, 0, int(six.width), int(six.height)))
|
||||||
|
|
||||||
|
for x, r := range six.px {
|
||||||
|
for y, colour := range r {
|
||||||
|
rgba.SetRGBA(int(x), int(six.height)-int(y), color.RGBA{
|
||||||
|
colour[0],
|
||||||
|
colour[1],
|
||||||
|
colour[2],
|
||||||
|
255,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rgba
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package sixel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"golang.org/x/image/bmp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// from https://en.wikipedia.org/wiki/Sixel
|
||||||
|
func TestParsing(t *testing.T) {
|
||||||
|
|
||||||
|
//raw := `q"1;1;16;16#0;2;0;0;0#1;2;94;75;22#2;2;97;78;31#3;2;97;82;35#4;2;97;82;44#5;2;94;78;25#6;2;91;78;41#7;2;69;60;38#8;2;56;50;35#9;2;63;56;35#10;2;41;38;31#0NB@@!8?@@BN$#1oCA?@!6?@?ACo$#3?O??A?!4@?A??O$#4?_w{{!6}{{w_$#5?G#2CA?@!4?@?AC#5G-#1{_#6K!4?__!4?K#1_{$#5B#4FRrrrz^^zrrrRF#5B$#3?G_#7CCGC??CGCC#3_G$#2?O#9?G!8?G#2?O$#8!4?GC!4?CG-#0NKGG!8?GGKN$#1?BFE!8KEFB$#3???@!8?@$#4!4?@@!4?@@$#5!4?A!6?A$#2!5?A!4?A$#7!6?A??A$#10!6?!4@$#6!7?AA`
|
||||||
|
raw := `q
|
||||||
|
#0;2;0;0;0#1;2;100;100;0#2;2;0;100;0
|
||||||
|
#1~~@@vv@@~~@@~~$
|
||||||
|
#2??}}GG}}??}}??-
|
||||||
|
#1!14@`
|
||||||
|
six, err := ParseString(raw)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
img := six.RGBA()
|
||||||
|
require.NotNil(t, img)
|
||||||
|
|
||||||
|
var imageBuf bytes.Buffer
|
||||||
|
err = bmp.Encode(io.Writer(&imageBuf), img)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write to file.
|
||||||
|
fo, err := os.Create("img.bmp")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer fo.Close()
|
||||||
|
fo.Write(imageBuf.Bytes())
|
||||||
|
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ var ansiSequenceMap = map[rune]escapeSequenceHandler{
|
||||||
'8': restoreCursorHandler,
|
'8': restoreCursorHandler,
|
||||||
'D': indexHandler,
|
'D': indexHandler,
|
||||||
'M': reverseIndexHandler,
|
'M': reverseIndexHandler,
|
||||||
|
'P': sixelHandler,
|
||||||
'c': risHandler, //RIS
|
'c': risHandler, //RIS
|
||||||
'(': swallowHandler(1), // character set bullshit
|
'(': swallowHandler(1), // character set bullshit
|
||||||
')': swallowHandler(1), // character set bullshit
|
')': swallowHandler(1), // character set bullshit
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
package terminal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"github.com/go-gl/gl/all-core/gl"
|
||||||
|
"github.com/liamg/aminal/sixel"
|
||||||
|
)
|
||||||
|
|
||||||
|
func sixelHandler(pty chan rune, terminal *Terminal) error {
|
||||||
|
|
||||||
|
data := []rune{}
|
||||||
|
|
||||||
|
for {
|
||||||
|
b := <-pty
|
||||||
|
if b == 0x1b { // terminated by ESC bell or ESC \
|
||||||
|
_ = <-pty // swallow \ or bell
|
||||||
|
break
|
||||||
|
}
|
||||||
|
data = append(data, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
six, err := sixel.ParseString(string(data))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to parse sixel data: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
x, y := terminal.ActiveBuffer().CursorColumn(), terminal.ActiveBuffer().CursorLine()
|
||||||
|
terminal.ActiveBuffer().Write(' ')
|
||||||
|
cell := terminal.ActiveBuffer().GetCell(x, y)
|
||||||
|
if cell == nil {
|
||||||
|
return fmt.Errorf("Missing cell for sixel")
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.UseProgram(terminal.program)
|
||||||
|
cell.SetImage(six.RGBA())
|
||||||
|
|
||||||
|
imageHeight := float64(cell.Image().Bounds().Size().Y)
|
||||||
|
lines := int(math.Ceil(imageHeight / float64(terminal.charHeight)))
|
||||||
|
for l := 0; l <= int(lines+1); l++ {
|
||||||
|
terminal.ActiveBuffer().NewLine()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -32,6 +32,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Terminal struct {
|
type Terminal struct {
|
||||||
|
program uint32
|
||||||
buffers []*buffer.Buffer
|
buffers []*buffer.Buffer
|
||||||
activeBufferIndex uint8
|
activeBufferIndex uint8
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
|
@ -47,6 +48,8 @@ type Terminal struct {
|
||||||
mouseMode MouseMode
|
mouseMode MouseMode
|
||||||
bracketedPasteMode bool
|
bracketedPasteMode bool
|
||||||
isDirty bool
|
isDirty bool
|
||||||
|
charWidth float32
|
||||||
|
charHeight float32
|
||||||
}
|
}
|
||||||
|
|
||||||
type Modes struct {
|
type Modes struct {
|
||||||
|
@ -87,6 +90,11 @@ func New(pty *os.File, logger *zap.SugaredLogger, config *config.Config) *Termin
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (terminal *Terminal) SetProgram(program uint32) {
|
||||||
|
terminal.program = program
|
||||||
|
}
|
||||||
|
|
||||||
func (terminal *Terminal) SetBracketedPasteMode(enabled bool) {
|
func (terminal *Terminal) SetBracketedPasteMode(enabled bool) {
|
||||||
terminal.bracketedPasteMode = enabled
|
terminal.bracketedPasteMode = enabled
|
||||||
}
|
}
|
||||||
|
@ -137,6 +145,11 @@ func (terminal *Terminal) ScrollDown(lines uint16) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (terminal *Terminal) SetCharSize(w float32, h float32) {
|
||||||
|
terminal.charWidth = w
|
||||||
|
terminal.charHeight = h
|
||||||
|
}
|
||||||
|
|
||||||
func (terminal *Terminal) ScrollUp(lines uint16) {
|
func (terminal *Terminal) ScrollUp(lines uint16) {
|
||||||
terminal.logger.Infof("Scrolling up %d", lines)
|
terminal.logger.Infof("Scrolling up %d", lines)
|
||||||
terminal.ActiveBuffer().ScrollUp(lines)
|
terminal.ActiveBuffer().ScrollUp(lines)
|
||||||
|
|
Loading…
Reference in New Issue