mirror of https://github.com/liamg/aminal.git
commit
978d5067f6
38
README.md
38
README.md
|
@ -10,9 +10,14 @@ The project is experimental at the moment, so you probably won't want to rely on
|
||||||
|
|
||||||
Ensure you have your latest graphics card drivers installed before use.
|
Ensure you have your latest graphics card drivers installed before use.
|
||||||
|
|
||||||
|
Sixels are now supported.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
## Aims
|
## Aims
|
||||||
|
|
||||||
- Full unicode support
|
- Unicode support
|
||||||
- OpenGL rendering
|
- OpenGL rendering
|
||||||
- Full customisation options
|
- Full customisation options
|
||||||
- True colour support
|
- True colour support
|
||||||
|
@ -23,6 +28,7 @@ Ensure you have your latest graphics card drivers installed before use.
|
||||||
- Resize logic that wraps/unwraps lines _correctly_
|
- Resize logic that wraps/unwraps lines _correctly_
|
||||||
- Bullshit graphical effects
|
- Bullshit graphical effects
|
||||||
- Multi platform support
|
- Multi platform support
|
||||||
|
- Sixel support
|
||||||
|
|
||||||
## What isn't supported?
|
## What isn't supported?
|
||||||
|
|
||||||
|
@ -32,13 +38,6 @@ Ensure you have your latest graphics card drivers installed before use.
|
||||||
<img alt="Overheating" src="https://imgs.xkcd.com/comics/workflow.png"/>
|
<img alt="Overheating" src="https://imgs.xkcd.com/comics/workflow.png"/>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## Build Dependencies
|
|
||||||
|
|
||||||
- Go 1.10.3+
|
|
||||||
- On macOS, you need Xcode or Command Line Tools for Xcode (`xcode-select --install`) for required headers and libraries.
|
|
||||||
- On Ubuntu/Debian-like Linux distributions, you need `libgl1-mesa-dev xorg-dev`.
|
|
||||||
- On CentOS/Fedora-like Linux distributions, you need `libX11-devel libXcursor-devel libXrandr-devel libXinerama-devel mesa-libGL-devel libXi-devel`.
|
|
||||||
|
|
||||||
## Platform Support
|
## Platform Support
|
||||||
|
|
||||||
| Platform | Supported |
|
| Platform | Supported |
|
||||||
|
@ -47,25 +46,12 @@ Ensure you have your latest graphics card drivers installed before use.
|
||||||
| MacOSX | ⏳ |
|
| MacOSX | ⏳ |
|
||||||
| Windows | ⏳ |
|
| Windows | ⏳ |
|
||||||
|
|
||||||
## Planned Features
|
## Build Dependencies
|
||||||
|
|
||||||
| Feature | Done | Notes |
|
- Go 1.10.3+
|
||||||
|-----------------------------|------|-------|
|
- On macOS, you need Xcode or Command Line Tools for Xcode (`xcode-select --install`) for required headers and libraries.
|
||||||
| Pty allocation | ✔ |
|
- On Ubuntu/Debian-like Linux distributions, you need `libgl1-mesa-dev xorg-dev`.
|
||||||
| OpenGL rendering | ✔ |
|
- On CentOS/Fedora-like Linux distributions, you need `libX11-devel libXcursor-devel libXrandr-devel libXinerama-devel mesa-libGL-devel libXi-devel`.
|
||||||
| 8-bit (256) colour | ✔ |
|
|
||||||
| 24-bit (true) colour | ✔ |
|
|
||||||
| Resizing/content reordering | ✔ |
|
|
||||||
| ANSI escape codes | ✔ |
|
|
||||||
| UTF-8 input | ✔ |
|
|
||||||
| UTF-8 output | ✔ |
|
|
||||||
| Copy/paste | ✔ |
|
|
||||||
| Customisable colour schemes | ✔ |
|
|
||||||
| Config file | ✔ |
|
|
||||||
| Scrolling | ✔ |
|
|
||||||
| Mouse interaction | ✔ |
|
|
||||||
| Clickable URLs | ✔ |
|
|
||||||
| Sweet render effects | |
|
|
||||||
|
|
||||||
## Keyboard Shortcuts
|
## Keyboard Shortcuts
|
||||||
|
|
||||||
|
|
|
@ -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,64 @@ 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.Enable(gl.TEXTURE_2D)
|
||||||
|
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)
|
||||||
|
gl.Disable(gl.TEXTURE_2D)
|
||||||
|
|
||||||
|
gl.Disable(gl.BLEND)
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
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,205 @@
|
||||||
|
package sixel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
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 += string(r)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if r >= 0x30 && r <= 0x39 {
|
||||||
|
countStr = fmt.Sprintf("%s%c", countStr, r)
|
||||||
|
} else {
|
||||||
|
count, _ := strconv.Atoi(countStr)
|
||||||
|
output += strings.Repeat(string(r), count)
|
||||||
|
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) 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.Set(int(x), int(six.height)-int(y), color.RGBA{
|
||||||
|
R: colour[0],
|
||||||
|
G: colour[1],
|
||||||
|
B: colour[2],
|
||||||
|
A: 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,72 @@
|
||||||
|
package terminal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/draw"
|
||||||
|
"math"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
if b >= 33 {
|
||||||
|
data = append(data, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
six, err := sixel.ParseString(string(data))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to parse sixel data: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
originalImage := six.RGBA()
|
||||||
|
|
||||||
|
w := originalImage.Bounds().Size().X
|
||||||
|
h := originalImage.Bounds().Size().Y
|
||||||
|
|
||||||
|
x, y := terminal.ActiveBuffer().CursorColumn(), terminal.ActiveBuffer().CursorLine()
|
||||||
|
|
||||||
|
fromBottom := int(terminal.ActiveBuffer().ViewHeight() - y)
|
||||||
|
lines := int(math.Ceil(float64(h) / float64(terminal.charHeight)))
|
||||||
|
if fromBottom < lines+2 {
|
||||||
|
y -= (uint16(lines+2) - uint16(fromBottom))
|
||||||
|
}
|
||||||
|
for l := 0; l <= int(lines); l++ {
|
||||||
|
terminal.ActiveBuffer().Write([]rune(strings.Repeat(" ", int(terminal.ActiveBuffer().ViewWidth())))...)
|
||||||
|
terminal.ActiveBuffer().NewLine()
|
||||||
|
}
|
||||||
|
cols := int(math.Ceil(float64(w) / float64(terminal.charWidth)))
|
||||||
|
|
||||||
|
for offsetY := 0; offsetY < lines-1; offsetY++ {
|
||||||
|
for offsetX := 0; offsetX < cols-1; offsetX++ {
|
||||||
|
|
||||||
|
cell := terminal.ActiveBuffer().GetCell(x+uint16(offsetX), y+uint16((lines-2)-offsetY))
|
||||||
|
if cell == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
img := originalImage.SubImage(image.Rect(
|
||||||
|
offsetX*int(terminal.charWidth),
|
||||||
|
offsetY*int(terminal.charHeight),
|
||||||
|
(offsetX*int(terminal.charWidth))+int(terminal.charWidth),
|
||||||
|
(offsetY*int(terminal.charHeight))+int(terminal.charHeight),
|
||||||
|
))
|
||||||
|
|
||||||
|
rgba := image.NewRGBA(image.Rect(0, 0, int(terminal.charWidth), int(terminal.charHeight)))
|
||||||
|
draw.Draw(rgba, rgba.Bounds(), img, img.Bounds().Min, draw.Src)
|
||||||
|
cell.SetImage(rgba)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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