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 == '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 build-linux-travis; fi
|
||||||
- if [[ $TRAVIS_OS_NAME == 'linux' ]]; then make windows-cross-compile-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:
|
env:
|
||||||
global:
|
global:
|
||||||
- secure: "pdRpTOGQSUgbC9tK37voxUYJHMWDPJEmdMhNBsljpP9VnxxbR6JEFwvOQEmUHGlsYv8jma6a17jE60ngVQk8QP12cPh48i2bdbVgym/zTUOKFawCtPAzs8i7evh0di5eZ3uoyc42kG4skc+ePuVHbXC8jDxwaPpMqSHD7QyQc1/6ckI9LLkyWUqhnJJXkVwhmI74Aa1Im6QhywAWFMeTBRRL02cwr6k7VKSYOn6yrtzJRCALFGpZ/n58lPrpDxN7W8o+HRQP89wIDy8FyNeEPdmqGFNfMHDvI3oJRN4dGC4H9EkKf/iGuNJia1Bs+MgaG9kKlMHsI6Fkh5uw9KNTvC1llx43VRQJzm26cn1CpRxxRtF4F8lqkpY4tHjxxCitV+98ddW8jdmQYyx+LeueC5wqlO9g2M5L3oXsGMqZ++mDRDa8oQoQAVUSVtimeO8ODXFuVNR8TlupP0Cthgucil63VUZfAD8EHc2zpRSFxfYByDH53uMEinn20uovL6W42fqgboC43HOnR6aVfSANPsBFDlcpZFa2BY5RkcKyYdaLkucy0DKJ946UDfhOu6FNm0GPHq5HcgWkLojNF0dEFgG6J+SGQGiPjxTlHP/zoe61qMlWu+fYRXQnKWZN5Kk0T1TbAk6pKSE6wRLG8ddxvMg+eVpGLT+gAvQdrrkMFvs="
|
- 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.BindVertexArray(f.vao)
|
||||||
gl.BindBuffer(gl.ARRAY_BUFFER, f.vbo)
|
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")))
|
vertAttrib := uint32(gl.GetAttribLocation(f.program, gl.Str("vert\x00")))
|
||||||
gl.EnableVertexAttribArray(vertAttrib)
|
gl.EnableVertexAttribArray(vertAttrib)
|
||||||
|
|
17
gui/gui.go
17
gui/gui.go
|
@ -65,6 +65,8 @@ type GUI struct {
|
||||||
mouseMovedAfterSelectionStarted bool
|
mouseMovedAfterSelectionStarted bool
|
||||||
internalResize bool
|
internalResize bool
|
||||||
selectionRegionMode buffer.SelectionRegionMode
|
selectionRegionMode buffer.SelectionRegionMode
|
||||||
|
|
||||||
|
mainThreadFunc chan func()
|
||||||
}
|
}
|
||||||
|
|
||||||
func Min(x, y int) int {
|
func Min(x, y int) int {
|
||||||
|
@ -167,6 +169,8 @@ func New(config *config.Config, terminal *terminal.Terminal, logger *zap.Sugared
|
||||||
keyboardShortcuts: shortcuts,
|
keyboardShortcuts: shortcuts,
|
||||||
resizeLock: &sync.Mutex{},
|
resizeLock: &sync.Mutex{},
|
||||||
internalResize: false,
|
internalResize: false,
|
||||||
|
|
||||||
|
mainThreadFunc: make(chan func()),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -490,6 +494,8 @@ Buffer Size: %d lines
|
||||||
gui.resizeToTerminal()
|
gui.resizeToTerminal()
|
||||||
case reverse := <-reverseChan:
|
case reverse := <-reverseChan:
|
||||||
gui.generateDefaultCell(reverse)
|
gui.generateDefaultCell(reverse)
|
||||||
|
case funcForMainThread := <-gui.mainThreadFunc:
|
||||||
|
funcForMainThread()
|
||||||
default:
|
default:
|
||||||
break terminalEvents
|
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) {
|
func (gui *GUI) monitorChangeCallback(monitor *glfw.Monitor, event glfw.MonitorEvent) {
|
||||||
gui.SetDPIScale()
|
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)
|
logger.Fatalf("Cannot start: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
terminal.WindowManipulation = g
|
||||||
|
|
||||||
if unitTestfunc != nil {
|
if unitTestfunc != nil {
|
||||||
go unitTestfunc(terminal, g)
|
go unitTestfunc(terminal, g)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -425,10 +425,6 @@ func csiSetModeHandler(params []string, terminal *Terminal) error {
|
||||||
return csiSetModes(params, true, terminal)
|
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 {
|
func csiLinePositionAbsolute(params []string, terminal *Terminal) error {
|
||||||
row := 1
|
row := 1
|
||||||
if len(params) > 0 {
|
if len(params) > 0 {
|
||||||
|
|
|
@ -33,6 +33,20 @@ const (
|
||||||
MouseExtSGR
|
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 {
|
type Terminal struct {
|
||||||
program uint32
|
program uint32
|
||||||
buffers []*buffer.Buffer
|
buffers []*buffer.Buffer
|
||||||
|
@ -56,6 +70,8 @@ type Terminal struct {
|
||||||
terminalState *buffer.TerminalState
|
terminalState *buffer.TerminalState
|
||||||
platformDependentSettings platform.PlatformDependentSettings
|
platformDependentSettings platform.PlatformDependentSettings
|
||||||
dirty *notifier
|
dirty *notifier
|
||||||
|
|
||||||
|
WindowManipulation WindowManipulationInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
type Modes struct {
|
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