Simulating keystrokes for automated user input

I've written a simple console app (type command. get results. repeat.)
and I'm looking for a way to send it simulated keystrokes (hoping to
remotely send commands through a socket connection and have them "typed"
in the main console). I thought the solution would be easy but I'm
finding it much harder than anticipated.

The solution needs to generate a keystroke programmatically such that
the console has no idea whether it was physically typed by the user or
not.

Tried writing to a modified stdin, directly editing /dev/tty, redirected
echos... no luck yet.

Any ideas?

···

--
Posted via http://www.ruby-forum.com/.

Ryan Mohr wrote:

I've written a simple console app (type command. get results. repeat.)
and I'm looking for a way to send it simulated keystrokes (hoping to
remotely send commands through a socket connection and have them "typed"
in the main console). I thought the solution would be easy but I'm
finding it much harder than anticipated.

The solution needs to generate a keystroke programmatically such that
the console has no idea whether it was physically typed by the user or
not.

Tried writing to a modified stdin, directly editing /dev/tty, redirected
echos... no luck yet.

What about ruby's PTY lib?

   PTY.spawn 'your-app' do |r,w,cid| ... end

http://programming.ccp14.ac.uk/expect/
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/66884

http://www.devdaily.com/blog/post/ruby/ruby-jruby-program-that-runs-java-robot-class

···

On Tue, Jun 15, 2010 at 5:45 PM, Ryan Mohr <ryan.mohr@gmail.com> wrote:

I've written a simple console app (type command. get results. repeat.)
and I'm looking for a way to send it simulated keystrokes (hoping to
remotely send commands through a socket connection and have them "typed"
in the main console). I thought the solution would be easy but I'm
finding it much harder than anticipated.

The solution needs to generate a keystroke programmatically such that
the console has no idea whether it was physically typed by the user or
not.

Ryan Mohr wrote:

The solution needs to generate a keystroke programmatically such that
the console has no idea whether it was physically typed by the user or
not.

Tried writing to a modified stdin, directly editing /dev/tty, redirected
echos... no luck yet.

Any ideas?

Spawn a pty. There's pty.so in the standard library, which is a C
extension and unfortunately not well documented, but google for
"PTY.spawn" to find some examples, e.g.

http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8/ext/pty/expect_sample.rb?view=markup

···

--
Posted via http://www.ruby-forum.com/\.

Brian Candler wrote:

Spawn a pty. There's pty.so in the standard library, which is a C
extension and unfortunately not well documented, but google for
"PTY.spawn" to find some examples, e.g.

Thanks for the suggestion Brian but I don't think that will work for my
case. I need to send keystrokes to stdin of the current process itself.
Maybe there's a way to do that with PTY but I couldn't get it to work.

Here's a quick example that demonstrates the issue:

[code]
# prompt is already up and waiting
t = Thread.new {
   print "Enter command: "
   $stdout.flush

   # get the next command to run from the user
   command = gets

   puts "Executing command #{command}"
}

# want this to enter the desired command as though it
# was typed by the user himself (show up in stdout but
# also be picked up by any open 'gets' requests)

# ... ? (send "foo" to stdin of the current process)

t.join

puts "Done."
[/code]

···

--
Posted via http://www.ruby-forum.com/\.

Ryan Mohr wrote:

Brian Candler wrote:

Spawn a pty. There's pty.so in the standard library, which is a C
extension and unfortunately not well documented, but google for
"PTY.spawn" to find some examples, e.g.

Thanks for the suggestion Brian but I don't think that will work for my
case. I need to send keystrokes to stdin of the current process itself.

Then I think you're stuck. When your ruby process was started, its stdin
file descriptor was connected to something (e.g. a terminal or a pipe).
Only that thing can generate data for your app to read; e.g. you can't
write to the read end of a pipe.

You could connect forcibly reopen stdin on a different fd:

  rd, wr = IO.pipe
  STDIN.reopen(rd)
  Thread.new do
    wr.puts "hello"
  end
  line = gets
  puts line.inspect

That's really horrible though. It would be better to fork your program
entirely:

  IO.popen("-","rb+") do |pipe|
    if pipe
      # parent
      pipe.puts "hello"
      res = pipe.gets
      puts "got: #{res}"
    else
      # child
      line = gets
      puts line.inspect
    end
  end

If what you're trying to accomplish is unit testing of a CLI-driven
program, then I'd suggest either running the program under test as a
child (e.g. using IO.popen or PTY.spawn), or factor out your objects so
that they can read and write to arbitrary IO objects that you pass in,
so that you can pass them a StringIO or one end of a pipe.

Otherwise, perhaps you can explain what you are trying to achieve?

Regards,

Brian.

···

--
Posted via http://www.ruby-forum.com/\.

Brian Candler wrote

Otherwise, perhaps you can explain what you are trying to achieve?

In addition to the CLI, I'm developing a simple gui interface. As the
user interacts with the gui, commands are sent back to the CLI through a
socket connection.

At this point my CLI has nearly 100 commands and growing, so the easiest
approach would be for the GUI to just send the text command desired and
have it "typed" into the main app.

If I go the internal API route (eg. no simulated typing), the command
executes and the output is displayed, but now the old prompt is buried
above the new output and the prior 'gets' request is still waiting for
input. Since some of the commands have nested commands requesting input
of their own, this stale 'gets' call is problematic.

It's all pretty messy and probably hard to understand from my
description. The addition of the gui was an afterthought so the CLI
wasn't designed for this kind of use. The elegance of a simulated
typing solution has pushed me towards using applescript to solve the
issue (but even that has issues of its own such as the inability to send
text to specific background windows).

Thanks for all the help though. I'll keep hacking away at it.

···

--
Posted via http://www.ruby-forum.com/\.

In the GUI program run the CLI via PTY.spawn and let the GUI program
write ("type") commands (directly) to the CLI (instead of using a
socket). If the GUI and CLI must run on separate hosts, launch the
CLI remotely via, for example, Net::SSH. As a third alternative write
a small helper program to run on the server, it will run the actual
CLI via PTY.spawn and listen on the socket for connections from the
GUI.

···

On Wed, Jun 16, 2010 at 6:34 PM, Ryan Mohr <ryan.mohr@gmail.com> wrote:

Brian Candler wrote

Otherwise, perhaps you can explain what you are trying to achieve?

In addition to the CLI, I'm developing a simple gui interface. As the
user interacts with the gui, commands are sent back to the CLI through a
socket connection.

At this point my CLI has nearly 100 commands and growing, so the easiest
approach would be for the GUI to just send the text command desired and
have it "typed" into the main app.

I don't exactly understand what your trying to make, but in any
instance, whether it's gui or not, you might have a program turn your
input into a file, and then have your original program check if such a
file exists, read it, and then delete it... I haven't tried something
like it yet, but I plan on using a technique like it in one of my own
programs.

···

--
Posted via http://www.ruby-forum.com/.