ruby:以原始模式向 PTY 发送击键

ruby: Sending keystrokes to PTY in raw mode

我正在尝试向 ruby-newt 模块添加一些自动化测试。我的代码似乎可以工作,但仍然需要在终端手动点击 ENTER 才能完成。

例如下面的代码,\t会切换到下一个按钮,\r会按下按钮,两个命令都执行成功,但是ENTER键还需要在终端手动按下,否则程序将无限期挂起。

如果wr.write "\t\r"行被注释掉,则程序超时,10秒后成功退出。我试过 wr.flush,但这没有用。我也试过在命令中包含 \n

我应该在 write 命令中包含任何其他内容以确保子程序成功接收它吗?

require 'newt'
require 'pty'

def newt_run
  begin
    Newt::Screen.new
    Newt::Screen.centered_window(20, 15, 'Button')

    b1 = Newt::Button.new(1, 1, 'Button1')
    b2 = Newt::Button.new(1, 6, 'Button2')

    b = Newt::Button.new(1, 11, 'Exit')

    f = Newt::Form.new
    f.set_timer(10000)
    f.add(b1, b2, b) 

    rv = f.run
  ensure
    Newt::Screen.finish
  end
end

master, slave = PTY.open
rd, wr = IO.pipe

if fork.nil? then
  master.close
  wr.close

  $stdin.reopen(rd)
  $stdout.reopen(slave)
  $stderr.reopen(slave)

  newt_run
else
  slave.close
  rd.close

  wr.write "\t\r"
  Process.wait
end

问题是 newt C 库默认打开 /dev/tty 进行输入。它不使用标准输入。这就是为什么您发送的任何内容似乎都不起作用。它不是在读取您的管道,而是在读取 /dev/tty.

这里是更详细的问题:

  1. Ruby newt 在 C-lib
  2. 中调用 newtInit()
  3. newtInit() 呼叫 SLang_init_tty
  4. SLang_init_tty 默认始终将输入附加到 /dev/tty

如果你 SLang_init_ttyread the documentation 你会发现变量 SLang_TT_Read_FD 决定是否使用 /dev/tty

解决方案 1:

在从 Ruby 调用 newtInit() 之前,您需要将 SLang_TT_Read_FD 设置为 stdin

方案二:

使用setsidioctl(TIOCSCTTY)重新分配派生进程中的控制终端(参见docs for ioctl here)。

工作示例:

TIOCSCTTY = 0x540E
master, slave = PTY.open

if fork.nil? then
  # Child process
  # Make group leader. Required for aquiring controlling TTY.
  Process.setsid

  master.close               # Close master side    
  STDIN.reopen(slave)        # Reassign STDIN
  STDIN.ioctl(TIOCSCTTY, 0)  # Reassign controlling TTY (important part)

  # Ensure master is ready
  slave.gets

  # Now we can run the UI
  newt_run
else
  # Parent process
  slave.close

  # Sync up with slave
  master.puts 'hello'

  # Allow for UI setup
  sleep 1

  master.write "\e[B" # Arrow down
  master.write "\e[B" # Arrow down
  master.write "\t"   # Tab
  master.write "\r"   # Enter

  Process.wait 
end