ruby: отправка нажатий клавиш в PTY в режиме raw - PullRequest
2 голосов
/ 31 октября 2019

Я пытаюсь добавить еще несколько автоматических тестов в модуль 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

1 Ответ

0 голосов
/ 31 октября 2019

Проблема в том, что C-библиотека newt открывает /dev/tty по умолчанию для ввода. Он не использует стандартный ввод. Вот почему ничего, что вы отправляете, похоже, не работает. Это не чтение вашего канала, это чтение /dev/tty.

Вот проблема более подробно:

  1. Рубиновые вызовы тритона newtInit() в C-lib
  2. newtInit() звонки SLang_init_tty
  3. SLang_init_tty всегда присоединяет ввод к /dev/tty по умолчанию.

Если вы читаете документацию изSLang_init_tty вы обнаружите, что переменная SLang_TT_Read_FD определяет, используется ли /dev/tty или нет.

Решение 1 :

Необходимо установить SLang_TT_Read_FDна stdin, перед вызовом newtInit() из Ruby.

Решение 2:

Используйте setsid и ioctl(TIOCSCTTY) для переназначения управляющего терминала в разветвленномпроцесс (см. документы для ioctl здесь ).

Рабочий пример:

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
...