12

Several times I have wanted to detect single keystrokes in R but have failed to find anything else than readline() or similar.

An example would be to do interactive plotting or data browsing and be able to change parameter values with the arrow keys and automatically update the plot. Of course I could use readline() and have the user input "u" then instead of up arrow but I don't find it very elegant.

Could it be done with a system() command reading stdin in some way?

EDIT: I have now been told elsewhere that stdin also wait for an enter-stroke before doing anything and that catching keystrokes immediately is system specific and tricky to accomplish. If anyone knows how to do it on ubuntu 10.10 or any other Linux/unix system I'd be glad to know.

Iterator
  • 20,250
  • 12
  • 75
  • 111
Backlin
  • 14,612
  • 2
  • 49
  • 81
  • 1
    ?scan will do what you want and see ?par with argument "ask" for plot interaction by mouse – mdsumner Jul 12 '11 at 10:54
  • Not quite I'm afraid. Maybe I wasn't specific enough. – Backlin Jul 12 '11 at 11:45
  • I want to do different things depending on which key is pressed e.g. increase/decrease the value of a variable if up/down or "u"/"d" is pressed (without having to press enter afterwards). – Backlin Jul 12 '11 at 11:51

4 Answers4

11

Very OS-dependent solution. First some C code in getkey3.c:

#include <stdio.h>
#include <termios.h>
#include <unistd.h>

void mygetch ( int *ch ) 
{
  struct termios oldt, newt;

  tcgetattr ( STDIN_FILENO, &oldt );
  newt = oldt;
  newt.c_lflag &= ~( ICANON | ECHO );
  tcsetattr ( STDIN_FILENO, TCSANOW, &newt );
  *ch = getchar();
  tcsetattr ( STDIN_FILENO, TCSANOW, &oldt );
  return;
}

Compile for R with R CMD SHLIB getkey3.c

That produces getkey3.so. Start R.

 > dyn.load("getkey3.so")
 > .C("mygetch",as.integer(0))

then press a key, it should return a list with the first element as the integer value of the ASCII code of that key. Store it in an R variable if you want.

Works for me on Ubuntu, you're on your own for any other OSes.

Barry

Spacedman
  • 92,590
  • 12
  • 140
  • 224
4

If you don't mind pulling in a graphical toolkit, like tcltk or RGtk2 then the gWidgets package can detect simple keystrokes in a text editing widget. For example:

require(gWidgets)
w <- gwindow("Detect keystrokes", visible=FALSE)
e <- gedit("", cont=w)
addHandlerKeystroke(e, handler=function(h,...) {
  key <- h$key
  print(key)
})
visible(w) <- TRUE

It doesn't do some basic things though: uparrow, downarrow may not show, modifiers etc.

jverzani
  • 5,600
  • 2
  • 21
  • 17
2

I haven't used the R FFI in a long time but you can call the C-function getchar() with it, right?

in R type: ?.C

I used to write a tiny .dll to wrap some custom C-functions and then call them using .C(), however that was on Win. The way to build the dll in Linux is different but I assume the way to call into the dll is the same.

Bernd Elkemann
  • 23,242
  • 4
  • 37
  • 66
  • Thanks, it worked! _However_ it turns out that although `getchar()` reads only one char from `stdin`, there is nothing in `stdin` until `` is pressed. To get around it I had to do a unix-only-hack and temporarily set the terminal to raw mode. It is not recommended though as it can leave the terminal in an unstable state. Barry a.k.a. Spacedman has some nice code for it. – Backlin Jul 12 '11 at 14:38
1

This is a more general solution: C non-blocking keyboard input. Especially it is restoring the terminal behavior after it has been killed or cashed.

Community
  • 1
  • 1
Pascal
  • 29
  • 3