Skip to content

Commit

Permalink
Redisplay interface on terminal resize events
Browse files Browse the repository at this point in the history
- Not implemented on Windows, because there is a weird behavior that
  makes it too unstable to be used.
  • Loading branch information
maxlandon committed Jun 2, 2023
1 parent 9408230 commit b872279
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 22 deletions.
23 changes: 1 addition & 22 deletions internal/core/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type Keys struct {
reading bool // Currently reading keys out of the main loop.
keysOnce chan []byte // Passing keys from the main routine.
cursor chan []byte // Cursor coordinates has been read on stdin.
resize chan bool // Resize events on Windows are sent on stdin.

cfg *inputrc.Config // Configuration file used for meta key settings
mutex sync.RWMutex // Concurrency safety
Expand Down Expand Up @@ -297,25 +298,3 @@ func (k *Keys) extractCursorPos(keys []byte) (cursor, remain []byte) {

return
}

func (k *Keys) readInputFiltered() (keys []byte, err error) {
// Start reading from os.Stdin in the background.
// We will either read keys from user, or an EOF
// send by ourselves, because we pause reading.
buf := make([]byte, keyScanBufSize)

read, err := Stdin.Read(buf)
if err != nil && errors.Is(err, io.EOF) {
return
}

// Always attempt to extract cursor position info.
// If found, strip it and keep the remaining keys.
cursor, keys := k.extractCursorPos(buf[:read])

if len(cursor) > 0 {
k.cursor <- cursor
}

return keys, nil
}
24 changes: 24 additions & 0 deletions internal/core/keys_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
package core

import (
"errors"
"fmt"
"io"
"os"
"strconv"
)
Expand Down Expand Up @@ -80,3 +82,25 @@ func (k *Keys) GetCursorPos() (x, y int) {

return x, y
}

func (k *Keys) readInputFiltered() (keys []byte, err error) {
// Start reading from os.Stdin in the background.
// We will either read keys from user, or an EOF
// send by ourselves, because we pause reading.
buf := make([]byte, keyScanBufSize)

read, err := Stdin.Read(buf)
if err != nil && errors.Is(err, io.EOF) {
return
}

// Always attempt to extract cursor position info.
// If found, strip it and keep the remaining keys.
cursor, keys := k.extractCursorPos(buf[:read])

if len(cursor) > 0 {
k.cursor <- cursor
}

return keys, nil
}
57 changes: 57 additions & 0 deletions internal/core/keys_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
package core

import (
"errors"
"io"
"unsafe"

"github.com/reeflective/readline/inputrc"
Expand Down Expand Up @@ -36,6 +38,12 @@ const (
VK_NEXT = 0x22
)

// Use an undefined Virtual Key sequence to pass
// Windows terminal resize events from the reader.
const (
WINDOWS_RESIZE = 0x07
)

const (
charTab = 9
charCtrlH = 8
Expand All @@ -46,6 +54,49 @@ func init() {
Stdin = newRawReader()
}

// GetTerminalResize sends booleans over a channel to notify resize events on Windows.
// This functions uses the keys reader because on Windows, resize events are sent through
// stdin, not with syscalls like unix's syscall.SIGWINCH.
func GetTerminalResize(keys *Keys) <-chan bool {
keys.resize = make(chan bool, 1)

return keys.resize
}

// readInputFiltered on Windows needs to check for terminal resize events.
func (k *Keys) readInputFiltered() (keys []byte, err error) {
for {
// Start reading from os.Stdin in the background.
// We will either read keys from user, or an EOF
// send by ourselves, because we pause reading.
buf := make([]byte, keyScanBufSize)

read, err := Stdin.Read(buf)
if err != nil && errors.Is(err, io.EOF) {
return keys, err
}

input := buf[:read]

// On Windows, windows resize events are sent through stdin,
// so if one is detected, send it back to the display engine.
if len(input) == 1 && input[0] == WINDOWS_RESIZE {
k.resize <- true
continue
}

// Always attempt to extract cursor position info.
// If found, strip it and keep the remaining keys.
cursor, keys := k.extractCursorPos(input)

if len(cursor) > 0 {
k.cursor <- cursor
}

return keys, nil
}
}

// rawReader translates Windows input to ANSI sequences,
// to provide the same behavior as Unix terminals.
type rawReader struct {
Expand Down Expand Up @@ -77,6 +128,12 @@ next:
if err != nil {
return 0, err
}

// Keep resize events for the display engine to use.
if ir.EventType == EVENT_WINDOW_BUFFER_SIZE {
return r.write(buf, WINDOWS_RESIZE)
}

if ir.EventType != EVENT_KEY {
goto next
}
Expand Down
31 changes: 31 additions & 0 deletions internal/display/display_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//go:build unix
// +build unix

package display

import (
"os"
"os/signal"
"syscall"
)

// WatchResize redisplays the interface on terminal resize events.
func WatchResize(eng *Engine) chan<- bool {
done := make(chan bool, 1)

resizeChannel := make(chan os.Signal)
signal.Notify(resizeChannel, syscall.SIGWINCH)

go func() {
for {
select {
case <-resizeChannel:
eng.Refresh()
case <-done:
return
}
}
}()

return done
}
46 changes: 46 additions & 0 deletions internal/display/display_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//go:build windows
// +build windows

package display

// WatchResize redisplays the interface on terminal resize events on Windows.
// Currently not implemented, see related issue in repo: too buggy right now.
func WatchResize(eng *Engine) chan<- bool {
return make(chan<- bool)
// resizeChannel := core.GetTerminalResize(eng.keys)

// for {
// select {
// case <-resizeChannel:
// // Weird behavior on Windows: when there is no autosuggested line,
// // the cursor moves at the end of the completions area, if non-empty.
// // We must manually go back to the input cursor position first.
// line, _ := eng.completer.Line()
// if eng.completer.IsInserting() {
// eng.suggested = *eng.line
// } else {
// eng.suggested = eng.histories.Suggest(eng.line)
// }
//
// if eng.suggested.Len() <= line.Len() {
// fmt.Println(term.HideCursor)
//
// compRows := completion.Coordinates(eng.completer)
// if compRows <= eng.AvailableHelperLines() {
// compRows++
// }
//
// term.MoveCursorBackwards(term.GetWidth())
// term.MoveCursorUp(compRows)
// term.MoveCursorUp(ui.CoordinatesHint(eng.hint))
// eng.cursorHintToLineStart()
// eng.lineStartToCursorPos()
// fmt.Println(term.ShowCursor)
// }
//
// eng.Refresh()
// case <-done:
// return
// }
// }
}
5 changes: 5 additions & 0 deletions readline.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,17 @@ func (rl *Shell) Readline() (string, error) {
}
defer term.Restore(descriptor, state)

// Prompts and cursor styles
rl.Display.PrintPrimaryPrompt()
defer rl.Display.RefreshTransient()
defer fmt.Print(keymap.CursorStyle("default"))

rl.init()

// Terminal resize events
resize := display.WatchResize(rl.Display)
defer close(resize)

for {
// Whether or not the command is resolved, let the macro
// engine record the keys if currently recording a macro.
Expand Down

0 comments on commit b872279

Please sign in to comment.