如何在运行在Windows 2012或更早版本的Go终端应用程序中处理箭头键?

huangapple go评论77阅读模式
英文:

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()

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()

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
}

huangapple
  • 本文由 发表于 2023年3月9日 22:34:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/75686000.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定