Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I interact with a child process pretending to be a terminal?

Tags:

python

twisted

I'm trying to interact with an NCURSES program.

As an example I'm using GNU Screen and run aptitude inside. (you could try it with mc instead.)

The program below starts a screen session with -x to connect to my session.

I want to navigate by pressing Arrow-down and Arrow-up.

If I send 'q' for quit I see a box pop up in my other screen session.

What do I need to do to get special keys like arrow keys working?

It currently seems to ignore the VT102 sequence I'm sending.

from twisted.internet import protocol, reactor

class MyPP(protocol.ProcessProtocol):
    def connectionMade(self):
        reactor.callLater(1.0, self.foo)

    def foo(self):
        self.transport.write('\033[B')

    def processExited(self, reason):
        print "processExited, status %s" % (reason.value.exitCode,)

    def outReceived(self, data):
        print data

    def errReceived(self, data):
        print "errReceived!", data

pp = MyPP()
command = ['screen', '-x']
reactor.spawnProcess(pp, command[0], command, {'TERM':'xterm'}, usePTY=True)

reactor.run()

UPDATE:

  1. Ted told me walking in the command history with ESC [ A (up) and ESC [ B (down) works with bash.

  2. Wondering why in aptitude it doesn't I've changed TERM=xterm to TERM=ansi which fixes it. Why xterm doesn't work still puzzles me.

like image 546
Eddy Pronk Avatar asked Oct 04 '22 21:10

Eddy Pronk


1 Answers

I've changed TERM=xterm to TERM=ansi which fixes it. Why xterm doesn't work still puzzles me.

Using Ubuntu 13.04, it looks like the ansi and xterm control codes aren't quite the same.

$ infocmp ansi | grep cud
        cr=^M, cub=\E[%p1%dD, cub1=\E[D, cud=\E[%p1%dB, cud1=\E[B,
        kcud1=\E[B, kcuf1=\E[C, kcuu1=\E[A, khome=\E[H, kich1=\E[L,

$ infocmp xterm | grep cud
        cud=\E[%p1%dB, cud1=^J, cuf=\E[%p1%dC, cuf1=\E[C,
        kcub1=\EOD, kcud1=\EOB, kcuf1=\EOC, kcuu1=\EOA,

...so it looks like you need to send the string '\033OB' to emulate a down arrow with xterm.

The following code works for me...

import subprocess
import os
import time

# Set TERM=xterm in case it isn't already
os.environ['TERM'] = 'xterm'

# Spawn aptitude
p = subprocess.Popen('aptitude', stdin=subprocess.PIPE)

# Wait for a bit to let it load from cache
time.sleep(5)

# Control it using xterm control codes
p.stdin.write('\033OB') # arrow down
time.sleep(1)
p.stdin.write('\033OB') # arrow down
time.sleep(1)
p.stdin.write('\033OA') # arrow up
time.sleep(1)
p.stdin.write('\033OA') # arrow up
time.sleep(1)
p.stdin.write('q')      # quit
time.sleep(1)
p.stdin.write('y')      # confirm

...although it screwed up my terminal after completion, so I had to do...

$ stty sane

...to get it working again.


Update

Just found what might be an easier way to determine the correct control codes. If you load vi, go into insert mode, then press CTRL-V followed by the key you want to emulate, it shows the literal string sent from the terminal.

For example...

Down Arrow: ^[OB

Page Up: ^[[5~

...where ^[ is CTRL-[, i.e. '\033'.

like image 168
Aya Avatar answered Oct 07 '22 18:10

Aya