fixed resizing and got basic input working

This commit is contained in:
Liam Galvin 2018-06-29 14:17:21 +01:00
parent 392a6239ef
commit 49e502cc0b
6 changed files with 447 additions and 142 deletions

58
gui/cell.go Normal file
View File

@ -0,0 +1,58 @@
package gui
import (
v41 "github.com/4ydx/gltext/v4.1"
"github.com/go-gl/mathgl/mgl32"
)
type Cell struct {
text *v41.Text
}
func NewCell(font *v41.Font, x float32, y float32, w float32, h float32) Cell {
cell := Cell{
text: v41.NewText(font, 1.0, 1.1),
}
cell.text.SetPosition(mgl32.Vec2{x, y})
return cell
}
func (cell *Cell) Draw() {
if cell.text != nil {
cell.text.Draw()
}
}
func (cell *Cell) Show() {
if cell.text != nil {
cell.text.Show()
}
}
func (cell *Cell) Hide() {
if cell.text != nil {
cell.text.Hide()
}
}
func (cell *Cell) SetRune(r rune) {
if cell.text != nil {
cell.text.SetString(string(r))
}
}
func (cell *Cell) SetColour(r float32, g float32, b float32) {
if cell.text != nil {
cell.text.SetColor(mgl32.Vec3{r, g, b})
}
}
func (cell *Cell) Release() {
if cell.text != nil {
cell.text.Release()
}
}

View File

@ -5,7 +5,6 @@ import (
"math" "math"
"os" "os"
"runtime" "runtime"
"sync"
"time" "time"
"github.com/4ydx/gltext" "github.com/4ydx/gltext"
@ -29,10 +28,10 @@ type GUI struct {
height int height int
charWidth float32 charWidth float32
charHeight float32 charHeight float32
texts [][]*v41.Text cells [][]Cell
textLock sync.Mutex
cols int cols int
rows int rows int
capslock bool
} }
func New(config config.Config, terminal *terminal.Terminal, logger *zap.SugaredLogger) *GUI { func New(config config.Config, terminal *terminal.Terminal, logger *zap.SugaredLogger) *GUI {
@ -44,17 +43,46 @@ func New(config config.Config, terminal *terminal.Terminal, logger *zap.SugaredL
width: 600, width: 600,
height: 300, height: 300,
terminal: terminal, terminal: terminal,
texts: [][]*v41.Text{}, cells: [][]Cell{},
} }
} }
// inspired by https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-1-hello-opengl // inspired by https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-1-hello-opengl
func (gui *GUI) SetSize(w int, h int) { func (gui *GUI) key(w *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
gui.window.SetSize(w, h)
gui.resize(gui.window, w, h) caps := gui.capslock
if mods&glfw.ModShift > 0 {
caps = !caps
}
if action == glfw.Repeat || action == glfw.Press {
switch key {
case glfw.KeyCapsLock:
gui.capslock = !gui.capslock
case glfw.KeyEnter:
gui.terminal.Write([]byte{0x0a})
default:
if key >= 0x41 && key <= 0x5a { // A-Z, normalise to lower
key += 0x20
}
if key >= 0x61 && key <= 0x7a { // a-z
if caps {
key -= 0x20
}
}
gui.terminal.Write([]byte{byte(key)})
}
//gui.logger.Debugf("Key pressed: 0x%X %q", key, string([]byte{byte(key)}))
//gui.terminal.Write([]byte{byte(scancode)})
}
} }
// can only be called on OS thread
func (gui *GUI) resize(w *glfw.Window, width int, height int) { func (gui *GUI) resize(w *glfw.Window, width int, height int) {
if width == gui.width && height == gui.height { if width == gui.width && height == gui.height {
@ -90,10 +118,10 @@ func (gui *GUI) getTermSize() (int, int) {
return gui.cols, gui.rows return gui.cols, gui.rows
} }
// checks if the terminals cells have been updated, and updates the text objects if needed // checks if the terminals cells have been updated, and updates the text objects if needed - only call on OS thread
func (gui *GUI) updateTexts() { func (gui *GUI) updateTexts() {
gui.textLock.Lock()
defer gui.textLock.Unlock() // runtime.LockOSThread() ?
//gui.logger.Debugf("Updating texts...") //gui.logger.Debugf("Updating texts...")
@ -102,69 +130,56 @@ func (gui *GUI) updateTexts() {
for row := 0; row < rows; row++ { for row := 0; row < rows; row++ {
for col := 0; col < cols; col++ { for col := 0; col < cols; col++ {
r, err := gui.terminal.GetRuneAtPos(terminal.Position{Row: row, Col: col}) c, err := gui.terminal.GetCellAtPos(terminal.Position{Line: row, Col: col})
if err != nil {
gui.logger.Errorf("Failed to read rune: %s", err) if err != nil || c == nil {
gui.cells[row][col].Hide()
continue
} }
if r > 0 {
gui.texts[row][col].SetString(string(r)) if c.IsHidden() {
gui.texts[row][col].SetColor(mgl32.Vec3{1, 1, 1})
// @todo set colour gui.cells[row][col].Hide()
gui.texts[row][col].Show()
} else { // debug
//gui.texts[row][col].Hide() //gui.texts[row][col].SetColor(c.GetColourVec())
gui.texts[row][col].SetString("?") //gui.texts[row][col].SetString("?")
gui.texts[row][col].SetColor(mgl32.Vec3{0.1, 0.1, 0.15}) //gui.texts[row][col].Show()
// end debug
continue
} }
gui.cells[row][col].SetColour(c.GetColour())
gui.cells[row][col].SetRune(c.GetRune())
gui.cells[row][col].Show()
} }
} }
} }
// builds text objects // builds text objects - only call on OS thread
func (gui *GUI) createTexts() { func (gui *GUI) createTexts() {
gui.textLock.Lock()
scaleMin, scaleMax := float32(1.0), float32(1.1)
cols, rows := gui.getTermSize() cols, rows := gui.getTermSize()
texts := [][]*v41.Text{} cells := [][]Cell{}
for row := 0; row < rows; row++ { for row := 0; row < rows; row++ {
if len(texts) <= row { if len(cells) <= row {
texts = append(texts, []*v41.Text{}) cells = append(cells, []Cell{})
} }
for col := 0; col < cols; col++ { for col := 0; col < cols; col++ {
if len(texts[row]) <= col { if len(cells[row]) <= col {
text := v41.NewText(gui.font, scaleMin, scaleMax)
text.Hide()
if row < len(gui.texts) {
if col < len(gui.texts[row]) {
text.SetString(gui.texts[row][col].String)
gui.texts[row][col].Release()
if gui.texts[row][col].String != "" {
text.Show()
}
}
}
x := ((float32(col) * gui.charWidth) - (float32(gui.width) / 2)) + (gui.charWidth / 2) x := ((float32(col) * gui.charWidth) - (float32(gui.width) / 2)) + (gui.charWidth / 2)
y := -(((float32(row) * gui.charHeight) - (float32(gui.height) / 2)) + (gui.charHeight / 2)) y := -(((float32(row) * gui.charHeight) - (float32(gui.height) / 2)) + (gui.charHeight / 2))
if col == 0 && row == 0 { cells[row] = append(cells[row], NewCell(gui.font, x, y, gui.charWidth, gui.charHeight))
gui.logger.Debugf("0,0 is at %f,%f", x, y)
} else if col == cols-1 && row == rows-1 {
gui.logger.Debugf("%d,%d is at %f,%f", col, row, x, y)
}
text.SetPosition(mgl32.Vec2{x, y})
texts[row] = append(texts[row], text)
} }
} }
} }
gui.texts = texts gui.cells = cells
gui.textLock.Unlock()
gui.updateTexts() gui.updateTexts()
} }
@ -195,13 +210,19 @@ func (gui *GUI) Render() error {
} }
gui.window.SetFramebufferSizeCallback(gui.resize) gui.window.SetFramebufferSizeCallback(gui.resize)
gui.window.SetKeyCallback(gui.key)
w, h := gui.window.GetSize() w, h := gui.window.GetSize()
gui.resize(gui.window, w, h) gui.resize(gui.window, w, h)
gl.Viewport(0, 0, int32(gui.width), int32(gui.height)) gl.Viewport(0, 0, int32(gui.width), int32(gui.height))
gui.logger.Debugf("Starting pty read handling...") gui.logger.Debugf("Starting pty read handling...")
gui.terminal.OnUpdate(gui.updateTexts)
updateChan := make(chan bool, 1024)
gui.terminal.OnUpdate(func() {
updateChan <- true
})
go gui.terminal.Read() go gui.terminal.Read()
scaleMin, scaleMax := float32(1.0), float32(1.1) scaleMin, scaleMax := float32(1.0), float32(1.1)
@ -218,40 +239,57 @@ func (gui *GUI) Render() error {
// stop smoothing fonts // stop smoothing fonts
gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
updateRequired := false
gui.logger.Debugf("Starting render...") gui.logger.Debugf("Starting render...")
gl.ClearColor(0.1, 0.1, 0.1, 1.0)
for !gui.window.ShouldClose() { for !gui.window.ShouldClose() {
select { updateRequired = false
case <-ticker.C:
text.SetString(fmt.Sprintf("%d fps | %d, %d | %s", frames, gui.terminal.GetPosition().Col, gui.terminal.GetPosition().Row, gui.texts[0][0].String)) CheckUpdate:
frames = 0 for {
default: select {
case <-updateChan:
updateRequired = true
case <-ticker.C:
text.SetString(fmt.Sprintf("%d fps | %dx%d", frames, gui.cols, gui.rows))
frames = 0
updateRequired = true
default:
break CheckUpdate
}
} }
gl.UseProgram(program) gl.UseProgram(program)
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
// Render the string. if updateRequired {
gui.window.SetTitle(gui.terminal.GetTitle())
gui.textLock.Lock() gui.updateTexts()
cols, rows := gui.getTermSize()
for row := 0; row < rows; row++ { // Render the string.
for col := 0; col < cols; col++ { gui.window.SetTitle(gui.terminal.GetTitle())
gui.texts[row][col].SetColor(mgl32.Vec3{1, 1, 1})
//gui.texts[row][col].SetString("?") gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
gui.texts[row][col].Draw()
cols, rows := gui.getTermSize()
for row := 0; row < rows; row++ {
for col := 0; col < cols; col++ {
gui.cells[row][col].Draw()
}
} }
}
gui.textLock.Unlock()
text.Draw() text.Draw()
}
frames++
glfw.PollEvents() glfw.PollEvents()
gui.window.SwapBuffers() gui.window.SwapBuffers()
frames++
} }
gui.logger.Debugf("Stopping render...") gui.logger.Debugf("Stopping render...")
@ -320,5 +358,6 @@ func (gui *GUI) createProgram() (uint32, error) {
prog := gl.CreateProgram() prog := gl.CreateProgram()
gl.LinkProgram(prog) gl.LinkProgram(prog)
return prog, nil return prog, nil
} }

View File

@ -41,7 +41,7 @@ func main() {
terminal := terminal.New(pty, sugaredLogger) terminal := terminal.New(pty, sugaredLogger)
go func() { go func() {
time.Sleep(time.Second * 5) time.Sleep(time.Second * 1)
terminal.Write([]byte("tput cols && tput lines\n")) terminal.Write([]byte("tput cols && tput lines\n"))
terminal.Write([]byte("ls -la\n")) terminal.Write([]byte("ls -la\n"))
}() }()

BIN
raft

Binary file not shown.

View File

@ -1,5 +1,40 @@
package terminal package terminal
import "github.com/go-gl/mathgl/mgl32"
type Cell struct { type Cell struct {
r rune r rune
wrapper bool
isWrapped bool
}
func (cell *Cell) GetRune() rune {
return cell.r
}
func (cell *Cell) IsHidden() bool {
return cell.r == 0
}
func (cell *Cell) GetColour() (r float32, g float32, b float32) {
if cell.wrapper {
return 0, 1, 0
}
if cell.isWrapped {
return 1, 1, 0
}
if cell.IsHidden() {
return 0, 0, 1
}
return 1, 1, 1
}
func (cell *Cell) GetColourVec() mgl32.Vec3 {
r, g, b := cell.GetColour()
return mgl32.Vec3{r, g, b}
} }

View File

@ -3,6 +3,7 @@ package terminal
import ( import (
"fmt" "fmt"
"os" "os"
"strconv"
"sync" "sync"
"syscall" "syscall"
"unsafe" "unsafe"
@ -11,13 +12,64 @@ import (
) )
type Terminal struct { type Terminal struct {
cells [][]Cell // y, x lines []Line // lines, where 0 is earliest, n is latest
position Position // line and col
lock sync.Mutex lock sync.Mutex
pty *os.File pty *os.File
logger *zap.SugaredLogger logger *zap.SugaredLogger
title string title string
position Position
onUpdate []func() onUpdate []func()
size Winsize
}
type Line struct {
Cells []Cell
wrapped bool
}
func NewLine() Line {
return Line{
Cells: []Cell{},
}
}
func (line *Line) String() string {
s := ""
for _, c := range line.Cells {
s += string(c.r)
}
return s
}
func (line *Line) CutCellsAfter(n int) []Cell {
cut := line.Cells[n:]
line.Cells = line.Cells[:n]
return cut
}
func (line *Line) CutCellsFromBeginning(n int) []Cell {
if n > len(line.Cells) {
n = len(line.Cells)
}
cut := line.Cells[:n]
line.Cells = line.Cells[n:]
return cut
}
func (line *Line) CutCellsFromEnd(n int) []Cell {
cut := line.Cells[len(line.Cells)-n:]
line.Cells = line.Cells[:len(line.Cells)-n]
return cut
}
func (line *Line) GetRenderedLength() int {
l := 0
for x, c := range line.Cells {
if c.r > 0 {
l = x
}
}
return l
} }
type Winsize struct { type Winsize struct {
@ -28,13 +80,13 @@ type Winsize struct {
} }
type Position struct { type Position struct {
Col int Line int
Row int Col int
} }
func New(pty *os.File, logger *zap.SugaredLogger) *Terminal { func New(pty *os.File, logger *zap.SugaredLogger) *Terminal {
return &Terminal{ return &Terminal{
cells: [][]Cell{}, lines: []Line{},
pty: pty, pty: pty,
logger: logger, logger: logger,
onUpdate: []func(){}, onUpdate: []func(){},
@ -51,6 +103,25 @@ func (terminal *Terminal) triggerOnUpdate() {
} }
} }
func (terminal *Terminal) getPosition() Position {
return terminal.position
}
func (terminal *Terminal) incrementPosition() {
position := terminal.getPosition()
if position.Col+1 >= int(terminal.size.Width) {
position.Line++
position.Col = 0
} else {
position.Col++
}
terminal.SetPosition(position)
}
func (terminal *Terminal) SetPosition(position Position) {
terminal.position = position
}
func (terminal *Terminal) GetTitle() string { func (terminal *Terminal) GetTitle() string {
return terminal.title return terminal.title
} }
@ -61,13 +132,29 @@ func (terminal *Terminal) Write(data []byte) error {
return err return err
} }
func (terminal *Terminal) ClearToEndOfLine() error { func (terminal *Terminal) ClearToEndOfLine() {
w, _ := terminal.GetSize()
for i := terminal.position.Col; i < w; i++ { position := terminal.getPosition()
// @todo handle errors?
terminal.setRuneAtPos(Position{Row: terminal.position.Row, Col: i}, 0) if position.Line < len(terminal.lines) {
if position.Col < len(terminal.lines[position.Line].Cells) {
terminal.lines[position.Line].Cells = terminal.lines[position.Line].Cells[:position.Col]
}
} }
return nil }
// we have thousands of lines of output. if the terminal is X lines high, we just want to lookat the most recent X lines to render (unless scroll etc)
func (terminal *Terminal) getBufferedLine(line int) *Line {
if len(terminal.lines) >= int(terminal.size.Height) {
line = len(terminal.lines) - int(terminal.size.Height) + line
}
if line >= len(terminal.lines) {
return nil
}
return &terminal.lines[line]
} }
// Read needs to be run on a goroutine, as it continually reads output to set on the terminal // Read needs to be run on a goroutine, as it continually reads output to set on the terminal
@ -104,6 +191,30 @@ func (terminal *Terminal) Read() error {
} }
switch final { switch final {
case byte('A'):
distance := 1
if len(params) > 0 {
var err error
distance, err = strconv.Atoi(string([]byte{params[0]}))
if err != nil {
distance = 1
}
}
if terminal.position.Line-distance >= 0 {
terminal.position.Line -= distance
}
case byte('B'):
distance := 1
if len(params) > 0 {
var err error
distance, err = strconv.Atoi(string([]byte{params[0]}))
if err != nil {
distance = 1
}
}
terminal.position.Line += distance
case 0x4b: // K - EOL - Erase to end of line case 0x4b: // K - EOL - Erase to end of line
if len(params) == 0 || params[0] == byte('0') { if len(params) == 0 || params[0] == byte('0') {
terminal.ClearToEndOfLine() terminal.ClearToEndOfLine()
@ -151,9 +262,7 @@ func (terminal *Terminal) Read() error {
default: default:
// render character at current location // render character at current location
// fmt.Printf("%s\n", string([]byte{b})) // fmt.Printf("%s\n", string([]byte{b}))
if err := terminal.writeRune([]rune(string([]byte{b}))[0]); err != nil { terminal.writeRune([]rune(string([]byte{b}))[0])
terminal.logger.Errorf("Failed to write rune %s", string([]byte{b}))
}
} }
} }
@ -178,102 +287,166 @@ func (terminal *Terminal) Read() error {
} }
} }
func (terminal *Terminal) writeRune(r rune) error { func (terminal *Terminal) writeRune(r rune) {
terminal.setRuneAtPos(terminal.position, r)
terminal.incrementPosition()
err := terminal.setRuneAtPos(terminal.position, r)
if err != nil {
return err
}
w, h := terminal.GetSize()
if terminal.position.Col < w-1 {
terminal.position.Col++
} else {
terminal.position.Col = 0
if terminal.position.Row <= h-1 {
terminal.position.Row++
} else {
panic(fmt.Errorf("Not implemented - need to shuffle all rows up one"))
}
}
return nil
} }
func (terminal *Terminal) newLine() { func (terminal *Terminal) newLine() {
_, h := terminal.GetSize() if terminal.position.Line >= len(terminal.lines) {
terminal.position.Col = 0 terminal.lines = append(terminal.lines, NewLine())
if terminal.position.Row <= h-1 {
terminal.position.Row++
} else {
panic(fmt.Errorf("Not implemented - need to shuffle all rows up one"))
} }
terminal.position.Col = 0
terminal.position.Line++
} }
func (terminal *Terminal) Clear() { func (terminal *Terminal) Clear() {
for y := range terminal.cells { // @todo actually should just add a bunch of newlines?
for x := range terminal.cells[y] { for i := 0; i < int(terminal.size.Height); i++ {
terminal.cells[y][x].r = 0 terminal.newLine()
}
} }
terminal.position = Position{0, 0} terminal.SetPosition(Position{Line: 0, Col: 0})
} }
func (terminal *Terminal) GetPosition() Position { func (terminal *Terminal) GetCellAtPos(pos Position) (*Cell, error) {
return terminal.position
}
func (terminal *Terminal) GetRuneAtPos(pos Position) (rune, error) { if int(terminal.size.Height) <= pos.Line {
if len(terminal.cells) <= pos.Row { terminal.logger.Errorf("Line %d does not exist", pos.Line)
return 0, fmt.Errorf("Row %d does not exist", pos.Row) return nil, fmt.Errorf("Line %d does not exist", pos.Line)
} }
if len(terminal.cells) < 1 || len(terminal.cells[0]) <= pos.Col { if int(terminal.size.Width) <= pos.Col {
return 0, fmt.Errorf("Col %d does not exist", pos.Col) terminal.logger.Errorf("Col %d does not exist", pos.Col)
return nil, fmt.Errorf("Col %d does not exist", pos.Col)
} }
return terminal.cells[pos.Row][pos.Col].r, nil line := terminal.getBufferedLine(pos.Line)
if line == nil {
return nil, fmt.Errorf("Line missing")
}
for pos.Col >= len(line.Cells) {
line.Cells = append(line.Cells, Cell{})
}
return &line.Cells[pos.Col], nil
} }
func (terminal *Terminal) setRuneAtPos(pos Position, r rune) error { func (terminal *Terminal) setRuneAtPos(pos Position, r rune) error {
if len(terminal.cells) <= pos.Row { if int(terminal.size.Height) <= pos.Line {
return fmt.Errorf("Row %d does not exist", pos.Row) terminal.logger.Errorf("Line %d does not exist", pos.Line)
return fmt.Errorf("Line %d does not exist", pos.Line)
} }
if len(terminal.cells) < 1 || len(terminal.cells[0]) <= pos.Col { if int(terminal.size.Width) <= pos.Col {
terminal.logger.Errorf("Col %d does not exist", pos.Col)
return fmt.Errorf("Col %d does not exist", pos.Col) return fmt.Errorf("Col %d does not exist", pos.Col)
} }
if pos.Row == 0 && pos.Col == 0 { if pos.Line == 0 && pos.Col == 0 {
fmt.Printf("\n\nSetting %d %d to %q\n\n\n", pos.Row, pos.Col, string(r)) fmt.Printf("\n\nSetting %d %d to %q\n\n\n", pos.Line, pos.Col, string(r))
} }
terminal.cells[pos.Row][pos.Col].r = r line := terminal.getBufferedLine(pos.Line)
if line == nil {
for pos.Line >= len(terminal.lines) {
terminal.lines = append(terminal.lines, NewLine())
}
line = terminal.getBufferedLine(pos.Line)
if line == nil {
panic(fmt.Errorf("Impossible?"))
}
}
for pos.Col >= len(line.Cells) {
line.Cells = append(line.Cells, Cell{})
}
line.Cells[pos.Col].r = r
return nil return nil
} }
func (terminal *Terminal) GetSize() (int, int) { func (terminal *Terminal) GetSize() (int, int) {
terminal.lock.Lock() return int(terminal.size.Width), int(terminal.size.Height)
defer terminal.lock.Unlock()
if len(terminal.cells) == 0 {
return 0, 0
}
return len(terminal.cells[0]), len(terminal.cells)
} }
func (terminal *Terminal) SetSize(cols int, rows int) error { func (terminal *Terminal) SetSize(newCols int, newLines int) error {
terminal.lock.Lock() terminal.lock.Lock()
defer terminal.lock.Unlock() defer terminal.lock.Unlock()
cells := make([][]Cell, rows)
for i := range cells { oldCols := int(terminal.size.Width)
cells[i] = make([]Cell, cols) oldLines := int(terminal.size.Height)
if oldLines > 0 && oldCols > 0 { // only bother resizing content if there is some
if newCols < oldCols { // if the width decreased, we need to do some line trimming
for l := range terminal.lines {
if terminal.lines[l].GetRenderedLength() > newCols {
cells := terminal.lines[l].CutCellsAfter(newCols)
line := Line{
Cells: cells,
wrapped: true,
}
terminal.lines = append(terminal.lines[:l+1], append([]Line{line}, terminal.lines[l+1:]...)...)
if terminal.getPosition().Line > l {
terminal.position.Line++
} else if terminal.getPosition().Line == l {
if terminal.getPosition().Col >= newCols {
terminal.position.Line++
}
}
}
}
} else if newCols > oldCols { // if width increased, we need to potentially unwrap some lines
for l := 0; l < len(terminal.lines); l++ {
if terminal.lines[l].GetRenderedLength() < newCols { // there is space here to unwrap a line if needed
if l+1 < len(terminal.lines) {
if terminal.lines[l+1].wrapped {
wrapSize := newCols - terminal.lines[l].GetRenderedLength()
cells := terminal.lines[l+1].CutCellsFromBeginning(wrapSize)
terminal.lines[l].Cells = append(terminal.lines[l].Cells, cells...)
if terminal.lines[l+1].GetRenderedLength() == 0 {
// remove line
terminal.lines = append(terminal.lines[:l+1], terminal.lines[l+2:]...)
if terminal.getPosition().Line >= l+1 {
terminal.position.Line--
}
}
}
}
}
}
}
} }
terminal.cells = cells
terminal.size.Width = uint16(newCols)
terminal.size.Height = uint16(newLines)
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(terminal.pty.Fd()), _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(terminal.pty.Fd()),
uintptr(syscall.TIOCSWINSZ), uintptr(unsafe.Pointer(&Winsize{Width: uint16(cols), Height: uint16(rows)}))) uintptr(syscall.TIOCSWINSZ), uintptr(unsafe.Pointer(&terminal.size)))
if err != 0 { if err != 0 {
return fmt.Errorf("Failed to set terminal size vai ioctl: Error no %d", err) return fmt.Errorf("Failed to set terminal size vai ioctl: Error no %d", err)
} }
return nil return nil
} }
/*
------------------ ->
ssssssssssssssssss
ssssPPPPPPPPPPPPPP
xxxxxxxxx
xxxxxxxxxxxxxxxxxx
--------------------------
ssssssssssssssssss
SsssPPPPPPPPPPPPPP
xxxxxxxxx
xxxxxxxxxxxxxxxxxx
*/