mirror of https://github.com/liamg/aminal.git
Implement window manipulation CSI sequences (#256)
* Implement window manipulation CSI sequences * Fix travis config to make gofmt only for go1.11 builds * remove commented out functions
This commit is contained in:
parent
44233f384e
commit
05c45c0892
|
@ -29,7 +29,7 @@ script:
|
|||
- if [[ $TRAVIS_OS_NAME == 'osx' ]]; then make build-darwin-native-travis; fi
|
||||
- if [[ $TRAVIS_OS_NAME == 'linux' ]]; then make build-linux-travis; fi
|
||||
- if [[ $TRAVIS_OS_NAME == 'linux' ]]; then make windows-cross-compile-travis; fi
|
||||
- if [[ $TRAVIS_OS_NAME == 'linux' ]]; then make check-gofmt; fi
|
||||
- if [[ $TRAVIS_OS_NAME == 'linux' && $TRAVIS_GO_VERSION =~ ^1\.11\. ]]; then echo 'check-gofmt'; make check-gofmt; fi
|
||||
env:
|
||||
global:
|
||||
- secure: "pdRpTOGQSUgbC9tK37voxUYJHMWDPJEmdMhNBsljpP9VnxxbR6JEFwvOQEmUHGlsYv8jma6a17jE60ngVQk8QP12cPh48i2bdbVgym/zTUOKFawCtPAzs8i7evh0di5eZ3uoyc42kG4skc+ePuVHbXC8jDxwaPpMqSHD7QyQc1/6ckI9LLkyWUqhnJJXkVwhmI74Aa1Im6QhywAWFMeTBRRL02cwr6k7VKSYOn6yrtzJRCALFGpZ/n58lPrpDxN7W8o+HRQP89wIDy8FyNeEPdmqGFNfMHDvI3oJRN4dGC4H9EkKf/iGuNJia1Bs+MgaG9kKlMHsI6Fkh5uw9KNTvC1llx43VRQJzm26cn1CpRxxRtF4F8lqkpY4tHjxxCitV+98ddW8jdmQYyx+LeueC5wqlO9g2M5L3oXsGMqZ++mDRDa8oQoQAVUSVtimeO8ODXFuVNR8TlupP0Cthgucil63VUZfAD8EHc2zpRSFxfYByDH53uMEinn20uovL6W42fqgboC43HOnR6aVfSANPsBFDlcpZFa2BY5RkcKyYdaLkucy0DKJ946UDfhOu6FNm0GPHq5HcgWkLojNF0dEFgG6J+SGQGiPjxTlHP/zoe61qMlWu+fYRXQnKWZN5Kk0T1TbAk6pKSE6wRLG8ddxvMg+eVpGLT+gAvQdrrkMFvs="
|
||||
|
|
|
@ -48,7 +48,7 @@ func LoadTrueTypeFont(program uint32, r io.Reader, scale float32) (*Font, error)
|
|||
gl.BindVertexArray(f.vao)
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, f.vbo)
|
||||
|
||||
gl.BufferData(gl.ARRAY_BUFFER, 6*4*4, nil, gl.STATIC_DRAW)
|
||||
gl.BufferData(gl.ARRAY_BUFFER, 6*4*4, nil, gl.DYNAMIC_DRAW)
|
||||
|
||||
vertAttrib := uint32(gl.GetAttribLocation(f.program, gl.Str("vert\x00")))
|
||||
gl.EnableVertexAttribArray(vertAttrib)
|
||||
|
|
17
gui/gui.go
17
gui/gui.go
|
@ -65,6 +65,8 @@ type GUI struct {
|
|||
mouseMovedAfterSelectionStarted bool
|
||||
internalResize bool
|
||||
selectionRegionMode buffer.SelectionRegionMode
|
||||
|
||||
mainThreadFunc chan func()
|
||||
}
|
||||
|
||||
func Min(x, y int) int {
|
||||
|
@ -167,6 +169,8 @@ func New(config *config.Config, terminal *terminal.Terminal, logger *zap.Sugared
|
|||
keyboardShortcuts: shortcuts,
|
||||
resizeLock: &sync.Mutex{},
|
||||
internalResize: false,
|
||||
|
||||
mainThreadFunc: make(chan func()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -490,6 +494,8 @@ Buffer Size: %d lines
|
|||
gui.resizeToTerminal()
|
||||
case reverse := <-reverseChan:
|
||||
gui.generateDefaultCell(reverse)
|
||||
case funcForMainThread := <-gui.mainThreadFunc:
|
||||
funcForMainThread()
|
||||
default:
|
||||
break terminalEvents
|
||||
}
|
||||
|
@ -825,3 +831,14 @@ func (gui *GUI) windowPosChangeCallback(w *glfw.Window, xpos int, ypos int) {
|
|||
func (gui *GUI) monitorChangeCallback(monitor *glfw.Monitor, event glfw.MonitorEvent) {
|
||||
gui.SetDPIScale()
|
||||
}
|
||||
|
||||
// Synchronously executes the argument function in the main thread.
|
||||
// Does not return until f() executed!
|
||||
func (gui *GUI) executeInMainThread(f func() error) error {
|
||||
resultChan := make(chan error, 1)
|
||||
gui.mainThreadFunc <- func() {
|
||||
resultChan <- f()
|
||||
}
|
||||
gui.terminal.NotifyDirty() // wake up the main thread to allow processing
|
||||
return <-resultChan
|
||||
}
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
package gui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/go-gl/glfw/v3.2/glfw"
|
||||
"github.com/liamg/aminal/terminal"
|
||||
)
|
||||
|
||||
//
|
||||
// Implementation of the terminal.WindowManipulationInterface
|
||||
//
|
||||
|
||||
func (gui *GUI) RestoreWindow(term *terminal.Terminal) error {
|
||||
return gui.executeInMainThread(func() error {
|
||||
gui.window.Restore()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *GUI) IconifyWindow(term *terminal.Terminal) error {
|
||||
return gui.executeInMainThread(func() error {
|
||||
return gui.window.Iconify()
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *GUI) MoveWindow(term *terminal.Terminal, pixelX int, pixelY int) error {
|
||||
return gui.executeInMainThread(func() error {
|
||||
gui.window.SetPos(pixelX, pixelY)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *GUI) ResizeWindowByPixels(term *terminal.Terminal, pixelsHeight int, pixelsWidth int) error {
|
||||
return gui.executeInMainThread(func() error {
|
||||
term.Unlock()
|
||||
gui.window.SetSize(pixelsWidth, pixelsHeight)
|
||||
term.Lock()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *GUI) BringWindowToFront(term *terminal.Terminal) error {
|
||||
var err error
|
||||
if gui.window.GetAttrib(glfw.Iconified) != 0 {
|
||||
err = gui.executeInMainThread(func() error {
|
||||
return gui.window.Restore()
|
||||
})
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err = gui.window.Focus()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (gui *GUI) ResizeWindowByChars(term *terminal.Terminal, charsHeight int, charsWidth int) error {
|
||||
return gui.executeInMainThread(func() error {
|
||||
return term.SetSize(uint(charsWidth), uint(charsHeight))
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *GUI) MaximizeWindow(term *terminal.Terminal) error {
|
||||
return gui.executeInMainThread(func() error {
|
||||
term.Lock()
|
||||
err := gui.window.Maximize()
|
||||
term.Unlock()
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *GUI) ReportWindowState(term *terminal.Terminal) error {
|
||||
// Report xterm window state. If the xterm window is open (non-iconified), it returns CSI 1 t .
|
||||
// If the xterm window is iconified, it returns CSI 2 t .
|
||||
if gui.window.GetAttrib(glfw.Iconified) != 0 {
|
||||
_ = term.Write([]byte("\x1b[2t"))
|
||||
} else {
|
||||
_ = term.Write([]byte("\x1b[1t"))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *GUI) ReportWindowPosition(term *terminal.Terminal) error {
|
||||
// Report xterm window position as CSI 3 ; x; yt
|
||||
x, y := gui.window.GetPos()
|
||||
|
||||
_ = term.Write([]byte(fmt.Sprintf("\x1b[3;%d;%dt", x, y)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *GUI) ReportWindowSizeInPixels(term *terminal.Terminal) error {
|
||||
// Report xterm window in pixels as CSI 4 ; height ; width t
|
||||
_ = term.Write([]byte(fmt.Sprintf("\x1b[4;%d;%dt", gui.height, gui.width)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *GUI) ReportWindowSizeInChars(term *terminal.Terminal) error {
|
||||
// Report the size of the text area in characters as CSI 8 ; height ; width t
|
||||
charsWidth, charsHeight := gui.renderer.GetTermSize()
|
||||
|
||||
_ = term.Write([]byte(fmt.Sprintf("\x1b[8;%d;%dt", charsHeight, charsWidth)))
|
||||
|
||||
return nil
|
||||
}
|
2
main.go
2
main.go
|
@ -71,6 +71,8 @@ func initialize(unitTestfunc callback) {
|
|||
logger.Fatalf("Cannot start: %s", err)
|
||||
}
|
||||
|
||||
terminal.WindowManipulation = g
|
||||
|
||||
if unitTestfunc != nil {
|
||||
go unitTestfunc(terminal, g)
|
||||
} else {
|
||||
|
|
|
@ -425,10 +425,6 @@ func csiSetModeHandler(params []string, terminal *Terminal) error {
|
|||
return csiSetModes(params, true, terminal)
|
||||
}
|
||||
|
||||
func csiWindowManipulation(params []string, terminal *Terminal) error {
|
||||
return fmt.Errorf("Window manipulation is not yet supported")
|
||||
}
|
||||
|
||||
func csiLinePositionAbsolute(params []string, terminal *Terminal) error {
|
||||
row := 1
|
||||
if len(params) > 0 {
|
||||
|
|
|
@ -33,6 +33,20 @@ const (
|
|||
MouseExtSGR
|
||||
)
|
||||
|
||||
type WindowManipulationInterface interface {
|
||||
RestoreWindow(term *Terminal) error
|
||||
IconifyWindow(term *Terminal) error
|
||||
MoveWindow(term *Terminal, pixelX int, pixelY int) error
|
||||
ResizeWindowByPixels(term *Terminal, pixelsHeight int, pixelsWidth int) error
|
||||
BringWindowToFront(term *Terminal) error
|
||||
ResizeWindowByChars(term *Terminal, charsHeight int, charsWidth int) error
|
||||
MaximizeWindow(term *Terminal) error
|
||||
ReportWindowState(term *Terminal) error
|
||||
ReportWindowPosition(term *Terminal) error
|
||||
ReportWindowSizeInPixels(term *Terminal) error
|
||||
ReportWindowSizeInChars(term *Terminal) error
|
||||
}
|
||||
|
||||
type Terminal struct {
|
||||
program uint32
|
||||
buffers []*buffer.Buffer
|
||||
|
@ -56,6 +70,8 @@ type Terminal struct {
|
|||
terminalState *buffer.TerminalState
|
||||
platformDependentSettings platform.PlatformDependentSettings
|
||||
dirty *notifier
|
||||
|
||||
WindowManipulation WindowManipulationInterface
|
||||
}
|
||||
|
||||
type Modes struct {
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
package terminal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func getOptionalIntegerParam(params []string, paramNo int, defValue int) (int, error) {
|
||||
result := defValue
|
||||
if len(params) >= paramNo+1 {
|
||||
var err error
|
||||
result, err = strconv.Atoi(params[paramNo])
|
||||
if err != nil {
|
||||
return defValue, err
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func getMandatoryIntegerParam(params []string, paramNo int) (int, error) {
|
||||
if len(params) < paramNo+1 {
|
||||
return 0, fmt.Errorf("no mandatory parameter")
|
||||
}
|
||||
|
||||
result, err := strconv.Atoi(params[paramNo])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func csiWindowManipulation(params []string, terminal *Terminal) error {
|
||||
if terminal.WindowManipulation == nil {
|
||||
return fmt.Errorf("Handler for CSI window manipulation commands is not set")
|
||||
}
|
||||
|
||||
operation, err := getMandatoryIntegerParam(params, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("CSI t ignored: %s", err.Error())
|
||||
}
|
||||
|
||||
switch operation {
|
||||
case 1:
|
||||
terminal.logger.Debug("De-iconify window")
|
||||
return terminal.WindowManipulation.RestoreWindow(terminal)
|
||||
|
||||
case 2:
|
||||
terminal.logger.Debug("Iconify window")
|
||||
return terminal.WindowManipulation.IconifyWindow(terminal)
|
||||
|
||||
case 3:
|
||||
terminal.logger.Debug("Move window")
|
||||
{
|
||||
x, err := getMandatoryIntegerParam(params, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
y, err := getMandatoryIntegerParam(params, 2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return terminal.WindowManipulation.MoveWindow(terminal, x, y)
|
||||
}
|
||||
|
||||
case 4:
|
||||
terminal.logger.Debug("Resize the window in pixels")
|
||||
{
|
||||
height, err := getMandatoryIntegerParam(params, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
width, err := getMandatoryIntegerParam(params, 2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return terminal.WindowManipulation.ResizeWindowByPixels(terminal, height, width)
|
||||
}
|
||||
|
||||
case 5:
|
||||
terminal.logger.Debug("Raise the window to the front")
|
||||
return terminal.WindowManipulation.BringWindowToFront(terminal)
|
||||
|
||||
case 6:
|
||||
return fmt.Errorf("Lowering the window to the bottom is not implemented")
|
||||
|
||||
case 7:
|
||||
// NB: On Windows this sequence seem handled by the system
|
||||
return fmt.Errorf("Refreshing the window is not implemented")
|
||||
|
||||
case 8:
|
||||
terminal.logger.Debug("Resize the text area in characters")
|
||||
{
|
||||
height, err := getMandatoryIntegerParam(params, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
width, err := getMandatoryIntegerParam(params, 2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return terminal.WindowManipulation.ResizeWindowByChars(terminal, height, width)
|
||||
}
|
||||
|
||||
case 9:
|
||||
{
|
||||
p, err := getMandatoryIntegerParam(params, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if p == 0 {
|
||||
terminal.logger.Debug("Restore maximized window")
|
||||
return terminal.WindowManipulation.RestoreWindow(terminal)
|
||||
} else if p == 1 {
|
||||
terminal.logger.Debug("Maximize window")
|
||||
return terminal.WindowManipulation.MaximizeWindow(terminal)
|
||||
}
|
||||
}
|
||||
|
||||
case 11:
|
||||
terminal.logger.Debug("Report the window state")
|
||||
return terminal.WindowManipulation.ReportWindowState(terminal)
|
||||
|
||||
case 13:
|
||||
terminal.logger.Debug("Report the window position")
|
||||
return terminal.WindowManipulation.ReportWindowPosition(terminal)
|
||||
|
||||
case 14:
|
||||
terminal.logger.Debug("Report the window size in pixels")
|
||||
return terminal.WindowManipulation.ReportWindowSizeInPixels(terminal)
|
||||
|
||||
case 18:
|
||||
terminal.logger.Debug("Report the window size in characters as CSI 8")
|
||||
return terminal.WindowManipulation.ReportWindowSizeInChars(terminal)
|
||||
|
||||
case 19:
|
||||
return fmt.Errorf("Reporting the screen size in characters is not implemented")
|
||||
|
||||
case 20:
|
||||
return fmt.Errorf("Reporting the window icon label is not implemented")
|
||||
|
||||
case 21:
|
||||
return fmt.Errorf("Reporting the window title is not implemented")
|
||||
|
||||
default:
|
||||
return fmt.Errorf("not supported CSI t")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue