Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: moby/term
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 1aeaba8
Choose a base ref
...
head repository: moby/term
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v0.5.0
Choose a head ref

Commits on Apr 30, 2023

  1. windows: IsConsole(): fix deprecation comment

    This function was deprecated in 57a2131,
    but the comment was not formatted correctly, causing it to not be documented
    as deprecated on pkg.go.dev.
    
    Also changing this back to an actual function wrapper, so that it doesn't
    get documented in the "Variables" section.
    
    Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
    thaJeztah committed Apr 30, 2023
    Copy the full SHA
    a32e4b2 View commit details
  2. gha: check go mod

    Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
    thaJeztah committed Apr 30, 2023
    Copy the full SHA
    f617544 View commit details
  3. windows: keyToString(): fix string conversion

        Error: windows\ansi_reader.go:198:47: conversion from uint16 to string yields a string of one rune, not a string of digits (did you mean fmt.Sprint(x)?)
        Error: windows\ansi_reader.go:201:9: conversion from uint16 to string yields a string of one rune, not a string of digits (did you mean fmt.Sprint(x)?)
    
    Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
    thaJeztah committed Apr 30, 2023
    Copy the full SHA
    2ce69ef View commit details
  4. gha: also test on macOS

    Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
    thaJeztah committed Apr 30, 2023
    Copy the full SHA
    dbac8a0 View commit details
  5. gha: add go1.20

    Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
    thaJeztah committed Apr 30, 2023
    Copy the full SHA
    ed5581a View commit details
  6. gha: update to actions/setup-go@v4

    Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
    thaJeztah committed Apr 30, 2023
    Copy the full SHA
    8f2ba97 View commit details
  7. Merge pull request #38 from thaJeztah/ci_windows

    gha: update actions, add macOS, and add Go1.20
    thaJeztah authored Apr 30, 2023
    Copy the full SHA
    1c456eb View commit details
  8. Merge pull request #36 from thaJeztah/fix_deprecation_comment

    windows: IsConsole(): fix deprecation comment
    thaJeztah authored Apr 30, 2023
    Copy the full SHA
    13e9088 View commit details
  9. Merge pull request #39 from thaJeztah/windows_fix_string_conversion

    windows: keyToString(): fix string conversion
    thaJeztah authored Apr 30, 2023
    Copy the full SHA
    0878220 View commit details
  10. gha: add windows

    Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
    thaJeztah committed Apr 30, 2023
    Copy the full SHA
    eb35db6 View commit details
  11. Merge pull request #40 from thaJeztah/ci_windows2

    gha: add windows
    thaJeztah authored Apr 30, 2023
    Copy the full SHA
    1849d9c View commit details
  12. deprecate Termios in favor of unix.Termios

    Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
    thaJeztah committed Apr 30, 2023
    Copy the full SHA
    80ddd80 View commit details
  13. Merge pull request #37 from thaJeztah/deprecate_termios

    deprecate Termios in favor of unix.Termios
    thaJeztah authored Apr 30, 2023
    Copy the full SHA
    0564e01 View commit details
  14. windows: keyToString(): fix string conversion (for real)

    I must've been sleeping while writing 2ce69ef,
    because while it "fixed" the error, the intent here obviously is to return the
    actual character, not the number ':-)
    
    Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
    thaJeztah committed Apr 30, 2023
    Copy the full SHA
    abe3d01 View commit details

Commits on May 1, 2023

  1. Merge pull request #42 from thaJeztah/fix_windows_conversion_again

    windows: keyToString(): fix string conversion (for real)
    thaJeztah authored May 1, 2023
    Copy the full SHA
    2ff7073 View commit details

Commits on May 2, 2023

  1. deprecate ErrInvalidState

    This error was introduced in moby/moby@aa68656,
    returned on Unix only, but was never used as a sentinel value anywhere.
    It doesn't appear to be used anywhere outside of this package, but let's
    deprecate it first before removing.
    
    Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
    thaJeztah committed May 2, 2023
    Copy the full SHA
    fddc34e View commit details
  2. Merge pull request #43 from thaJeztah/deprecate_ErrInvalidState

    deprecate ErrInvalidState
    thaJeztah authored May 2, 2023
    Copy the full SHA
    4793886 View commit details
  3. merge / split some files

    Aligning functions to be in the same files for each platform.
    
    Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
    thaJeztah committed May 2, 2023
    Copy the full SHA
    e1ddf4f View commit details
  4. rename unix-only files

    Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
    thaJeztah committed May 2, 2023
    Copy the full SHA
    df80986 View commit details
  5. add doc.go

    Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
    thaJeztah committed May 2, 2023
    Copy the full SHA
    1b64da1 View commit details
  6. split exported functions from implementation

    Put the exported functions in a non-platform-specific file, and
    un-export the implementations. This allows us to maintain the
    GoDoc for exported functions in a single place (not having to
    repeat those), and make sure that the signatures between platforms
    are identical (unless not possible).
    
    Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
    thaJeztah committed May 2, 2023
    Copy the full SHA
    bb04910 View commit details
  7. Merge pull request #41 from thaJeztah/split_implementations

    split exported functions from implementation
    thaJeztah authored May 2, 2023
    Copy the full SHA
    9c3c875 View commit details
Showing with 249 additions and 188 deletions.
  1. +7 −3 .github/workflows/test.yml
  2. +3 −0 doc.go
  3. +0 −20 tc.go
  4. +43 −58 term.go
  5. +98 −0 term_unix.go
  6. +24 −75 term_windows.go
  7. +6 −7 termios.go → termios_unix.go
  8. +37 −0 termios_windows.go
  9. +2 −2 windows/ansi_reader.go
  10. +24 −0 windows/ansi_reader_test.go
  11. +5 −2 windows/console.go
  12. +0 −21 winsize.go
10 changes: 7 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -4,12 +4,12 @@ jobs:
test:
strategy:
matrix:
go: ["1.18.x", "1.19.x"]
platform: [ubuntu-latest]
go: ["1.18.x", "1.19.x", "1.20.x"]
platform: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.platform }}
steps:
- name: Install Go ${{ matrix.go }}
uses: actions/setup-go@v3
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go }}
- name: Checkout code
@@ -21,6 +21,10 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: go mod tidy
run: |
go mod tidy
git diff --exit-code
- name: Lint
run: |
docker run --rm -v `pwd`:/go/src/github.com/moby/term -w /go/src/github.com/moby/term \
3 changes: 3 additions & 0 deletions doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Package term provides structures and helper functions to work with
// terminal (state, sizes).
package term
20 changes: 0 additions & 20 deletions tc.go

This file was deleted.

101 changes: 43 additions & 58 deletions term.go
Original file line number Diff line number Diff line change
@@ -1,100 +1,85 @@
//go:build !windows
// +build !windows

// Package term provides structures and helper functions to work with
// terminal (state, sizes).
package term

import (
"errors"
"io"
"os"

"golang.org/x/sys/unix"
)

// ErrInvalidState is returned if the state of the terminal is invalid.
var ErrInvalidState = errors.New("Invalid terminal state")
import "io"

// State represents the state of the terminal.
type State struct {
termios Termios
}
// State holds the platform-specific state / console mode for the terminal.
type State terminalState

// Winsize represents the size of the terminal window.
type Winsize struct {
Height uint16
Width uint16
x uint16
y uint16

// Only used on Unix
x uint16
y uint16
}

// StdStreams returns the standard streams (stdin, stdout, stderr).
//
// On Windows, it attempts to turn on VT handling on all std handles if
// supported, or falls back to terminal emulation. On Unix, this returns
// the standard [os.Stdin], [os.Stdout] and [os.Stderr].
func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
return os.Stdin, os.Stdout, os.Stderr
return stdStreams()
}

// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
func GetFdInfo(in interface{}) (uintptr, bool) {
var inFd uintptr
var isTerminalIn bool
if file, ok := in.(*os.File); ok {
inFd = file.Fd()
isTerminalIn = IsTerminal(inFd)
}
return inFd, isTerminalIn
func GetFdInfo(in interface{}) (fd uintptr, isTerminal bool) {
return getFdInfo(in)
}

// GetWinsize returns the window size based on the specified file descriptor.
func GetWinsize(fd uintptr) (*Winsize, error) {
return getWinsize(fd)
}

// SetWinsize tries to set the specified window size for the specified file
// descriptor. It is only implemented on Unix, and returns an error on Windows.
func SetWinsize(fd uintptr, ws *Winsize) error {
return setWinsize(fd, ws)
}

// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd uintptr) bool {
_, err := tcget(fd)
return err == nil
return isTerminal(fd)
}

// RestoreTerminal restores the terminal connected to the given file descriptor
// to a previous state.
func RestoreTerminal(fd uintptr, state *State) error {
if state == nil {
return ErrInvalidState
}
return tcset(fd, &state.termios)
return restoreTerminal(fd, state)
}

// SaveState saves the state of the terminal connected to the given file descriptor.
func SaveState(fd uintptr) (*State, error) {
termios, err := tcget(fd)
if err != nil {
return nil, err
}
return &State{termios: *termios}, nil
return saveState(fd)
}

// DisableEcho applies the specified state to the terminal connected to the file
// descriptor, with echo disabled.
func DisableEcho(fd uintptr, state *State) error {
newState := state.termios
newState.Lflag &^= unix.ECHO

if err := tcset(fd, &newState); err != nil {
return err
}
return nil
return disableEcho(fd, state)
}

// SetRawTerminal puts the terminal connected to the given file descriptor into
// raw mode and returns the previous state. On UNIX, this puts both the input
// and output into raw mode. On Windows, it only puts the input into raw mode.
func SetRawTerminal(fd uintptr) (*State, error) {
oldState, err := MakeRaw(fd)
if err != nil {
return nil, err
}
return oldState, err
// raw mode and returns the previous state. On UNIX, this is the equivalent of
// [MakeRaw], and puts both the input and output into raw mode. On Windows, it
// only puts the input into raw mode.
func SetRawTerminal(fd uintptr) (previousState *State, err error) {
return setRawTerminal(fd)
}

// SetRawTerminalOutput puts the output of terminal connected to the given file
// descriptor into raw mode. On UNIX, this does nothing and returns nil for the
// state. On Windows, it disables LF -> CRLF translation.
func SetRawTerminalOutput(fd uintptr) (*State, error) {
return nil, nil
func SetRawTerminalOutput(fd uintptr) (previousState *State, err error) {
return setRawTerminalOutput(fd)
}

// MakeRaw puts the terminal (Windows Console) connected to the
// given file descriptor into raw mode and returns the previous state of
// the terminal so that it can be restored.
func MakeRaw(fd uintptr) (previousState *State, err error) {
return makeRaw(fd)
}
98 changes: 98 additions & 0 deletions term_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
//go:build !windows
// +build !windows

package term

import (
"errors"
"io"
"os"

"golang.org/x/sys/unix"
)

// ErrInvalidState is returned if the state of the terminal is invalid.
//
// Deprecated: ErrInvalidState is no longer used.
var ErrInvalidState = errors.New("Invalid terminal state")

// terminalState holds the platform-specific state / console mode for the terminal.
type terminalState struct {
termios unix.Termios
}

func stdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
return os.Stdin, os.Stdout, os.Stderr
}

func getFdInfo(in interface{}) (uintptr, bool) {
var inFd uintptr
var isTerminalIn bool
if file, ok := in.(*os.File); ok {
inFd = file.Fd()
isTerminalIn = isTerminal(inFd)
}
return inFd, isTerminalIn
}

func getWinsize(fd uintptr) (*Winsize, error) {
uws, err := unix.IoctlGetWinsize(int(fd), unix.TIOCGWINSZ)
ws := &Winsize{Height: uws.Row, Width: uws.Col, x: uws.Xpixel, y: uws.Ypixel}
return ws, err
}

func setWinsize(fd uintptr, ws *Winsize) error {
return unix.IoctlSetWinsize(int(fd), unix.TIOCSWINSZ, &unix.Winsize{
Row: ws.Height,
Col: ws.Width,
Xpixel: ws.x,
Ypixel: ws.y,
})
}

func isTerminal(fd uintptr) bool {
_, err := tcget(fd)
return err == nil
}

func restoreTerminal(fd uintptr, state *State) error {
if state == nil {
return errors.New("invalid terminal state")
}
return tcset(fd, &state.termios)
}

func saveState(fd uintptr) (*State, error) {
termios, err := tcget(fd)
if err != nil {
return nil, err
}
return &State{termios: *termios}, nil
}

func disableEcho(fd uintptr, state *State) error {
newState := state.termios
newState.Lflag &^= unix.ECHO

return tcset(fd, &newState)
}

func setRawTerminal(fd uintptr) (*State, error) {
return makeRaw(fd)
}

func setRawTerminalOutput(fd uintptr) (*State, error) {
return nil, nil
}

func tcget(fd uintptr) (*unix.Termios, error) {
p, err := unix.IoctlGetTermios(int(fd), getTermios)
if err != nil {
return nil, err
}
return p, nil
}

func tcset(fd uintptr, p *unix.Termios) error {
return unix.IoctlSetTermios(int(fd), setTermios, p)
}
99 changes: 24 additions & 75 deletions term_windows.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package term

import (
"fmt"
"io"
"os"
"os/signal"
@@ -9,22 +10,15 @@ import (
"golang.org/x/sys/windows"
)

// State holds the console mode for the terminal.
type State struct {
// terminalState holds the platform-specific state / console mode for the terminal.
type terminalState struct {
mode uint32
}

// Winsize is used for window size.
type Winsize struct {
Height uint16
Width uint16
}

// vtInputSupported is true if winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported by the console
var vtInputSupported bool

// StdStreams returns the standard streams (stdin, stdout, stderr).
func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
func stdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
// Turn on VT handling on all std handles, if possible. This might
// fail, in which case we will fall back to terminal emulation.
var (
@@ -87,16 +81,14 @@ func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
stdErr = os.Stderr
}

return
return stdIn, stdOut, stdErr
}

// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
func GetFdInfo(in interface{}) (uintptr, bool) {
func getFdInfo(in interface{}) (uintptr, bool) {
return windowsconsole.GetHandleInfo(in)
}

// GetWinsize returns the window size based on the specified file descriptor.
func GetWinsize(fd uintptr) (*Winsize, error) {
func getWinsize(fd uintptr) (*Winsize, error) {
var info windows.ConsoleScreenBufferInfo
if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
return nil, err
@@ -110,21 +102,21 @@ func GetWinsize(fd uintptr) (*Winsize, error) {
return winsize, nil
}

// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd uintptr) bool {
func setWinsize(fd uintptr, ws *Winsize) error {
return fmt.Errorf("not implemented on Windows")
}

func isTerminal(fd uintptr) bool {
var mode uint32
err := windows.GetConsoleMode(windows.Handle(fd), &mode)
return err == nil
}

// RestoreTerminal restores the terminal connected to the given file descriptor
// to a previous state.
func RestoreTerminal(fd uintptr, state *State) error {
func restoreTerminal(fd uintptr, state *State) error {
return windows.SetConsoleMode(windows.Handle(fd), state.mode)
}

// SaveState saves the state of the terminal connected to the given file descriptor.
func SaveState(fd uintptr) (*State, error) {
func saveState(fd uintptr) (*State, error) {
var mode uint32

if err := windows.GetConsoleMode(windows.Handle(fd), &mode); err != nil {
@@ -134,9 +126,8 @@ func SaveState(fd uintptr) (*State, error) {
return &State{mode: mode}, nil
}

// DisableEcho disables echo for the terminal connected to the given file descriptor.
// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
func DisableEcho(fd uintptr, state *State) error {
func disableEcho(fd uintptr, state *State) error {
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
mode := state.mode
mode &^= windows.ENABLE_ECHO_INPUT
mode |= windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT
@@ -150,69 +141,27 @@ func DisableEcho(fd uintptr, state *State) error {
return nil
}

// SetRawTerminal puts the terminal connected to the given file descriptor into
// raw mode and returns the previous state. On UNIX, this puts both the input
// and output into raw mode. On Windows, it only puts the input into raw mode.
func SetRawTerminal(fd uintptr) (*State, error) {
state, err := MakeRaw(fd)
func setRawTerminal(fd uintptr) (*State, error) {
oldState, err := MakeRaw(fd)
if err != nil {
return nil, err
}

// Register an interrupt handler to catch and restore prior state
restoreAtInterrupt(fd, state)
return state, err
restoreAtInterrupt(fd, oldState)
return oldState, err
}

// SetRawTerminalOutput puts the output of terminal connected to the given file
// descriptor into raw mode. On UNIX, this does nothing and returns nil for the
// state. On Windows, it disables LF -> CRLF translation.
func SetRawTerminalOutput(fd uintptr) (*State, error) {
state, err := SaveState(fd)
func setRawTerminalOutput(fd uintptr) (*State, error) {
oldState, err := saveState(fd)
if err != nil {
return nil, err
}

// Ignore failures, since winterm.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this
// version of Windows.
_ = windows.SetConsoleMode(windows.Handle(fd), state.mode|windows.DISABLE_NEWLINE_AUTO_RETURN)
return state, err
}

// MakeRaw puts the terminal (Windows Console) connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be restored.
func MakeRaw(fd uintptr) (*State, error) {
state, err := SaveState(fd)
if err != nil {
return nil, err
}

mode := state.mode

// See
// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx

// Disable these modes
mode &^= windows.ENABLE_ECHO_INPUT
mode &^= windows.ENABLE_LINE_INPUT
mode &^= windows.ENABLE_MOUSE_INPUT
mode &^= windows.ENABLE_WINDOW_INPUT
mode &^= windows.ENABLE_PROCESSED_INPUT

// Enable these modes
mode |= windows.ENABLE_EXTENDED_FLAGS
mode |= windows.ENABLE_INSERT_MODE
mode |= windows.ENABLE_QUICK_EDIT_MODE
if vtInputSupported {
mode |= windows.ENABLE_VIRTUAL_TERMINAL_INPUT
}

err = windows.SetConsoleMode(windows.Handle(fd), mode)
if err != nil {
return nil, err
}
return state, nil
_ = windows.SetConsoleMode(windows.Handle(fd), oldState.mode|windows.DISABLE_NEWLINE_AUTO_RETURN)
return oldState, err
}

func restoreAtInterrupt(fd uintptr, state *State) {
13 changes: 6 additions & 7 deletions termios.go → termios_unix.go
Original file line number Diff line number Diff line change
@@ -8,23 +8,22 @@ import (
)

// Termios is the Unix API for terminal I/O.
//
// Deprecated: use [unix.Termios].
type Termios = unix.Termios

// MakeRaw puts the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd uintptr) (*State, error) {
func makeRaw(fd uintptr) (*State, error) {
termios, err := tcget(fd)
if err != nil {
return nil, err
}

oldState := State{termios: *termios}

termios.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON)
termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
termios.Oflag &^= unix.OPOST
termios.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN)
termios.Cflag &^= (unix.CSIZE | unix.PARENB)
termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
termios.Cflag &^= unix.CSIZE | unix.PARENB
termios.Cflag |= unix.CS8
termios.Cc[unix.VMIN] = 1
termios.Cc[unix.VTIME] = 0
37 changes: 37 additions & 0 deletions termios_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package term

import "golang.org/x/sys/windows"

func makeRaw(fd uintptr) (*State, error) {
state, err := SaveState(fd)
if err != nil {
return nil, err
}

mode := state.mode

// See
// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx

// Disable these modes
mode &^= windows.ENABLE_ECHO_INPUT
mode &^= windows.ENABLE_LINE_INPUT
mode &^= windows.ENABLE_MOUSE_INPUT
mode &^= windows.ENABLE_WINDOW_INPUT
mode &^= windows.ENABLE_PROCESSED_INPUT

// Enable these modes
mode |= windows.ENABLE_EXTENDED_FLAGS
mode |= windows.ENABLE_INSERT_MODE
mode |= windows.ENABLE_QUICK_EDIT_MODE
if vtInputSupported {
mode |= windows.ENABLE_VIRTUAL_TERMINAL_INPUT
}

err = windows.SetConsoleMode(windows.Handle(fd), mode)
if err != nil {
return nil, err
}
return state, nil
}
4 changes: 2 additions & 2 deletions windows/ansi_reader.go
Original file line number Diff line number Diff line change
@@ -195,10 +195,10 @@ func keyToString(keyEvent *winterm.KEY_EVENT_RECORD, escapeSequence []byte) stri

// <Alt>+Key generates ESC N Key
if !control && alt {
return ansiterm.KEY_ESC_N + strings.ToLower(string(keyEvent.UnicodeChar))
return ansiterm.KEY_ESC_N + strings.ToLower(string(rune(keyEvent.UnicodeChar)))
}

return string(keyEvent.UnicodeChar)
return string(rune(keyEvent.UnicodeChar))
}

// formatVirtualKey converts a virtual key (e.g., up arrow) into the appropriate ANSI string.
24 changes: 24 additions & 0 deletions windows/ansi_reader_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//go:build windows
// +build windows

package windowsconsole

import (
"testing"

"github.com/Azure/go-ansiterm"
"github.com/Azure/go-ansiterm/winterm"
)

func TestKeyToString(t *testing.T) {
ke := &winterm.KEY_EVENT_RECORD{
ControlKeyState: winterm.LEFT_ALT_PRESSED,
UnicodeChar: 65, // capital A
}

const expected = ansiterm.KEY_ESC_N + "a"
out := keyToString(ke, nil)
if out != expected {
t.Errorf("expected %s, got %s", expected, out)
}
}
7 changes: 5 additions & 2 deletions windows/console.go
Original file line number Diff line number Diff line change
@@ -30,8 +30,11 @@ func GetHandleInfo(in interface{}) (uintptr, bool) {

// IsConsole returns true if the given file descriptor is a Windows Console.
// The code assumes that GetConsoleMode will return an error for file descriptors that are not a console.
// Deprecated: use golang.org/x/sys/windows.GetConsoleMode() or golang.org/x/term.IsTerminal()
var IsConsole = isConsole
//
// Deprecated: use [windows.GetConsoleMode] or [golang.org/x/term.IsTerminal].
func IsConsole(fd uintptr) bool {
return isConsole(fd)
}

func isConsole(fd uintptr) bool {
var mode uint32
21 changes: 0 additions & 21 deletions winsize.go

This file was deleted.