英文:
How to handle arrow keys in a Go terminal app running on Windows 2012 or earlier?
问题
我有一个Go终端应用程序,在进行远程连接后,将终端设置为TTY原始模式,并通过连接传递按键。
我遇到的问题是,在运行于2012年之前的Windows操作系统上,我无法传递箭头键。在较新的Windows版本中,您可以设置ENABLE_VIRTUAL_TERMINAL_INPUT
,然后检测并传递箭头键,但在2012年或更早的版本中,此选项不起作用。对于这些版本,无法检测到箭头键。
是否有一种解决方法,而不使用VS Studio?
澄清一下,我正在寻找操作系统中允许此功能的机制,而不是可能实现此机制的库。
英文:
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.
答案1
得分: 1
检查一下containerd/console
是否可以帮助,使用它的最新提交。
它被一个类似charmbracelet/wish
的库使用,并且可以在设置原始模式时检测并设置或不设置ENABLE_VIRTUAL_TERMINAL_INPUT
。
如果charmbracelet/wishlist
(基于wish)可以在你的环境中(在2012年之前的Windows操作系统)工作,那么containerd/console
也有可能对你自己的应用程序有所帮助。
两个重要的函数是:
func (m *master) initStdios() {
// 注意:我们忽略控制台模式警告,因为输入/输出可以被重定向。
//
// TODO: 调查打开CONOUT$/CONIN$以正确处理此问题
m.in = windows.Handle(os.Stdin.Fd())
if err := windows.GetConsoleMode(m.in, &m.inMode); err == nil {
// 验证是否支持windows.ENABLE_VIRTUAL_TERMINAL_INPUT,但不设置它。
if err = windows.SetConsoleMode(m.in, m.inMode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err == nil {
vtInputSupported = true
}
// 即使失败也无条件地将控制台模式设置回来,因为SetConsoleMode会记住输入句柄上的无效位。
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)
}
}
}
其中master
定义为:
type master struct {
in windows.Handle
inMode uint32
out windows.Handle
outMode uint32
err windows.Handle
errMode uint32
}
还有:
func makeInputRaw(fd windows.Handle, mode uint32) error
// makeInputRaw将与给定文件描述符连接的终端(Windows控制台)设置为原始模式
func makeInputRaw(fd windows.Handle, mode uint32) error {
// 参考
// -- 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
// 禁用这些模式
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
// 启用这些模式
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("无法将控制台设置为原始模式:%w", err)
}
return nil
}
英文:
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() {
// 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
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论