4

I have a Go terminal application that after it makes remote connect, puts the terminal into a TTY Raw mode and just passes keystrokes through the conenction.

The issue I am having is that I cannot pass arrow keys when running on an Windows OS before 2012. In later Windows versions you can set ENABLE_VIRTUAL_TERMINAL_INPUT and arrows keys are detected then passed, but this option does not on 2012 or earlier. For those versions no arrow key is detected.

Is their a work around for this without using VS Studio?

To clarify, I am looking for the mechanism in the OS to allow this not a library may implement the mechanism.

Liam Kelly
  • 3,524
  • 1
  • 17
  • 41

1 Answers1

1

Check if containerd/console can help, using its latest commits.
It is used by a library like charmbracelet/wish, and can detect and set or not ENABLE_VIRTUAL_TERMINAL_INPUT when setting raw mode.

If can make charmbracelet/wishlist (based on wish) work in your environment (with a Windows OS before 2012), chances are containerd/console can also help with you own application.


The two important functions are:

func (m *master) initStdios()

func (m *master) initStdios() {
    // Note: We discard console mode warnings, because in/out can be redirected.
    //
    // TODO: Investigate opening CONOUT$/CONIN$ to handle this correctly

    m.in = windows.Handle(os.Stdin.Fd())
    if err := windows.GetConsoleMode(m.in, &m.inMode); err == nil {
        // Validate that windows.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it.
        if err = windows.SetConsoleMode(m.in, m.inMode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err == nil {
            vtInputSupported = true
        }
        // Unconditionally set the console mode back even on failure because SetConsoleMode
        // remembers invalid bits on input handles.
        windows.SetConsoleMode(m.in, m.inMode)
    }

    m.out = windows.Handle(os.Stdout.Fd())
    if err := windows.GetConsoleMode(m.out, &m.outMode); err == nil {
        if err := windows.SetConsoleMode(m.out, m.outMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil {
            m.outMode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
        } else {
            windows.SetConsoleMode(m.out, m.outMode)
        }
    }

    m.err = windows.Handle(os.Stderr.Fd())
    if err := windows.GetConsoleMode(m.err, &m.errMode); err == nil {
        if err := windows.SetConsoleMode(m.err, m.errMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil {
            m.errMode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
        } else {
            windows.SetConsoleMode(m.err, m.errMode)
        }
    }
}

with master being:

type master struct {
    in     windows.Handle
    inMode uint32

    out     windows.Handle
    outMode uint32

    err     windows.Handle
    errMode uint32
}

And:

func makeInputRaw(fd windows.Handle, mode uint32) error

// makeInputRaw puts the terminal (Windows Console) connected to the given
// file descriptor into raw mode
func makeInputRaw(fd windows.Handle, mode uint32) error {
    // 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
    }

    if err := windows.SetConsoleMode(fd, mode); err != nil {
        return fmt.Errorf("unable to set console to raw mode: %w", err)
    }

    return nil
}
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • Clarified the question, really looking for the mechanism not a library that may implement it – Liam Kelly Mar 15 '23 at 17:50
  • @LiamKelly That is why I linked to the console source code: it illustrates how the raw mode is set, in other words: its mechanism. – VonC Mar 15 '23 at 17:58
  • If you mine that source and extract the mechanism which answers the question above, your answer will be marked as correct – Liam Kelly Mar 15 '23 at 18:00
  • @LiamKelly i have edited the answer accordingly, but obviously test it first before marking anything as "accepted" ;) – VonC Mar 15 '23 at 18:10
  • unfortunately it does not work, as it relies on the mechanism i explicitly mention does not work in 2012: 'windows.ENABLE_VIRTUAL_TERMINAL_INPUT' – Liam Kelly Mar 15 '23 at 18:17
  • @LiamKelly The *all* point of this answer is to illustrate a mechanism which **detects** if `ENABLE_VIRTUAL_TERMINAL_INPUT` and then set a raw mode with *or without* `ENABLE_VIRTUAL_TERMINAL_INPUT`, depending if it is supported or not. Wouldn't that be what you are looking for? – VonC Mar 15 '23 at 18:31