Non-blocking keyboard read - C/C++ - c++

I got this following function with me working now. But what I need to improve is that it would read input from the keyboard (on the terminal) EVEN THOUGH IT IS NOT BEING PRESSED. I need to know when it is NOT pressed (idle) so that the switch case block will fall into the default section. At this point, the read() function waits until there's an input from the user. Can anyone give a suggestion just based on modifying this following code?
NOTE: I'm a Java programmer, and still learning C/C++ so it might be hard to get in my head a little bit. Thanks guys..
EDIT: I found this link, and seems to have something related to what i'm looking for at the line of fcntl(STDIN_FILENO,F_SETFL,flags | O_NONBLOCK); . But since I barely know anything in C, I completely have no Idea what it's saying, yet.
http://www.codeguru.com/forum/showthread.php?t=367082
int kfd = 0;
struct termios cooked, raw;
char c;
bool dirty = false;
//get the console in raw mode
tcgetattr(kfd, &cooked);
memcpy(&raw, &cooked, sizeof(struct termios));
raw.c_lflag &=~ (ICANON | ECHO);
// Setting a new line, then end of file
raw.c_cc[VEOL] = 1;
raw.c_cc[VEOF] = 2;
tcsetattr(kfd, TCSANOW, &raw);
puts("Reading from keyboard");
puts("=====================");
puts("Use arrow keys to navigate");
while(true){
//get the next event from the keyboard
if(read(kfd, &c, 1) < 0)
{
perror("read():");
exit(-1);
}
linear_ = angular_ = 0;
ROS_DEBUG("value: 0x%02X\n", c);
switch(c)
{
case KEYCODE_L:
ROS_DEBUG("LEFT");
angular_ = -1.0;
dirty = true;
break;
case KEYCODE_R:
ROS_DEBUG("RIGHT");
angular_ = 1.0;
dirty = true;
break;
case KEYCODE_U:
ROS_DEBUG("UP");
linear_ = 1.0;
dirty = true;
break;
case KEYCODE_D:
ROS_DEBUG("DOWN");
linear_ = -1.0;
dirty = true;
break;
default:
ROS_DEBUG("RELEASE");
linear_ = 0;
angular_ = 0;
dirty = true;
break;
}

The OP seems to have answered their question:
I think I solved my problem. Please, anyone, verify and let me know if this is the right way to do it, or is it the complete way to do it (am I missing any other addition step e.g. resetting it back again -if that even makes sense) .
So what i found is to add this 3 lines before entering the while loop:
flags = fcntl(0, F_GETFL, 0); /* get current file status flags */
flags |= O_NONBLOCK; /* turn off blocking flag */
fcntl(0, F_SETFL, flags); /* set up non-blocking read */

Related

How to get real time key input in C++? (Linux) [duplicate]

how to detect a keyboard event in C without prompting the user in linux? That is the program running should terminate by pressing any key.
can anyone please help with this?
You have to modify terminal settings using termios. See Stevens & Rago 2nd Ed 'Advanced Programming in the UNIX Environment' it explains why tcsetattr() can return successfuly without having set all terminal characteristcs, and why you see what looks to be redundant calls to tcsetattr().
This is ANSI C in UNIX:
#include <sys/types.h>
#include <sys/time.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <termios.h>
#include <errno.h>
int checktty(struct termios *p, int term_fd)
{
struct termios ck;
return (
tcgetattr(term_fd, &ck) == 0 &&
(p->c_lflag == ck.c_lflag) &&
(p->c_cc[VMIN] == ck.c_cc[VMIN]) &&
(p->c_cc[VTIME] == ck.c_cc[VMIN])
);
}
int
keypress(int term_fd)
{
unsigned char ch;
int retval=read(term_fd, &ch, sizeof ch);
return retval;
}
int /* TCSAFLUSH acts like fflush for stdin */
flush_term(int term_fd, struct termios *p)
{
struct termios newterm;
errno=0;
tcgetattr(term_fd, p); /* get current stty settings*/
newterm = *p;
newterm.c_lflag &= ~(ECHO | ICANON);
newterm.c_cc[VMIN] = 0;
newterm.c_cc[VTIME] = 0;
return(
tcgetattr(term_fd, p) == 0 &&
tcsetattr(term_fd, TCSAFLUSH, &newterm) == 0 &&
checktty(&newterm, term_fd) != 0
);
}
void
term_error(void)
{
fprintf(stderr, "unable to set terminal characteristics\n");
perror("");
exit(1);
}
void
wait_and_exit(void)
{
struct timespec tsp={0,500}; /* sleep 500 usec (or likely more ) */
struct termios attr;
struct termios *p=&attr;
int keepon=0;
int term_fd=fileno(stdin);
fprintf(stdout, "press any key to continue:");
fflush(stdout);
if(!flush_term(term_fd, p) )
term_error();
for(keepon=1; keepon;)
{
nanosleep(&tsp, NULL);
switch(keypress(term_fd) )
{
case 0:
default:
break;
case -1:
fprintf(stdout, "Read error %s", strerror(errno));
exit(1);
break;
case 1: /* time to quit */
keepon=0;
fprintf(stdout, "\n");
break;
}
}
if( tcsetattr(term_fd, TCSADRAIN, p) == -1 &&
tcsetattr(term_fd, TCSADRAIN, p) == -1 )
term_error();
exit(0);
}
int main()
{
wait_and_exit();
return 0; /* never reached */
}
The nanosleep call is there to prevent the code from gobbling up system resources. You could call nice() and not use nanosleep(). All this does is sit and wait for a keystroke, then exit.
If you want to do that in a graphical application, you should use some libraries to do this.
Such a simple task can be easily done with whatever library (even low level ones like Xlib).
Just choose one and look for a tutorial that shows how to handle keyboard events.
no way with ANSI C. Look at ncurses lib.
Here’s code from /usr/src/bin/stty/key.c:
f_cbreak(struct info *ip)
{
if (ip->off)
f_sane(ip);
else {
ip->t.c_iflag |= BRKINT|IXON|IMAXBEL;
ip->t.c_oflag |= OPOST;
ip->t.c_lflag |= ISIG|IEXTEN;
ip->t.c_lflag &= ~ICANON;
ip->set = 1;
}
}
At a minimum, you have to get out of ICANON mode before your select(2) syscall or your FIONREAD ioctl will work.
I have an ancient, 20-year-old perl4 program that clears CBREAK and ECHO mode this way. It is doing curses stuff without resorting to the curses library:
sub BSD_cbreak {
local($on) = shift;
local(#sb);
local($sgttyb);
# global $sbttyb_t
$sgttyb_t = &sgttyb'typedef() unless defined $sgttyb_t;
# native BSD stuff by author (tsc)
ioctl(TTY,&TIOCGETP,$sgttyb)
|| die "Can't ioctl TIOCGETP: $!";
#sb = unpack($sgttyb_t,$sgttyb);
if ($on) {
$sb[&sgttyb'sg_flags] |= &CBREAK;
$sb[&sgttyb'sg_flags] &= ~&ECHO;
} else {
$sb[&sgttyb'sg_flags] &= ~&CBREAK;
$sb[&sgttyb'sg_flags] |= &ECHO;
}
$sgttyb = pack($sgttyb_t,#sb);
ioctl(TTY,&TIOCSETN,$sgttyb)
|| die "Can't ioctl TIOCSETN: $!";
}
sub SYSV_cbreak {
# SysV code contributed by Jeff Okamoto <okamoto#hpcc25.corp.hp.com>
local($on) = shift;
local($termio,#termio);
# global termio_t ???
$termio_t = &termio'typedef() unless defined $termio_t;
ioctl(TTY,&TCGETA,$termio)
|| die "Can't ioctl TCGETA: $!";
#termio = unpack($termio_t, $termio);
if ($on) {
$termio[&termio'c_lflag] &= ~(&ECHO | &ICANON);
$termio[&termio'c_cc + &VMIN] = 1;
$termio[&termio'c_cc + &VTIME] = 1;
} else {
$termio[&termio'c_lflag] |= (&ECHO | &ICANON);
# In HP-UX, it appears that turning ECHO and ICANON back on is
# sufficient to re-enable cooked mode. Therefore I'm not bothering
# to reset VMIN and VTIME (VEOF and VEOL above). This might be a
# problem on other SysV variants.
}
$termio = pack($termio_t, #termio);
ioctl(TTY, &TCSETA, $termio)
|| die "Can't ioctl TCSETA: $!";
}
sub POSIX_cbreak {
local($on) = shift;
local(#termios, $termios, $bitmask);
# "file statics" for package cbreak:
# $savebits, $save_vtime, $save_vmin, $is_on
$termios_t = &termios'typedef() unless defined $termios_t;
$termios = pack($termios_t, ()); # for Sun SysVr4, which dies w/o this
ioctl(TTY,&$GETIOCTL,$termios)
|| die "Can't ioctl GETIOCTL ($GETIOCTL): $!";
#termios = unpack($termios_t,$termios);
$bitmask = &ICANON | &IEXTEN | &ECHO;
if ($on && $cbreak'ison == 0) {
$cbreak'ison = 1;
$cbreak'savebits = $termios[&termios'c_lflag] & $bitmask;
$termios[&termios'c_lflag] &= ~$bitmask;
$cbreak'save_vtime = $termios[&termios'c_cc + &VTIME];
$termios[&termios'c_cc + &VTIME] = 0;
$cbreak'save_vmin = $termios[&termios'c_cc + &VMIN];
$termios[&termios'c_cc + &VMIN] = 1;
} elsif ( !$on && $cbreak'ison == 1 ) {
$cbreak'ison = 0;
$termios[&termios'c_lflag] |= $cbreak'savebits;
$termios[&termios'c_cc + &VTIME] = $cbreak'save_vtime;
$termios[&termios'c_cc + &VMIN] = $cbreak'save_vmin;
} else {
return 1;
}
$termios = pack($termios_t,#termios);
ioctl(TTY,&$SETIOCTL,$termios)
|| die "Can't ioctl SETIOCTL ($SETIOCTL): $!";
}
sub DUMB_cbreak {
local($on) = shift;
if ($on) {
system("stty cbreak -echo");
} else {
system("stty -cbreak echo");
}
}
And it elsewhere says that for POSIX,
($GETIOCTL, $SETIOCTL) = (TIOCGETA, TIOCSETA);
RE-translation back into the original C is left as an exercise for the reader, because I can't remember where the 20-years-ago-me snagged it from originally. :(
Once you're out of ICANON mode on the tty, now your select(2) syscall works properly again. When select's read mask returns that that descriptor is ready, then you do a FIONREAD ioctl to discover exactly how many bytes are waiting for you on that file descriptor. Having got that, you can do a read(2) syscall for just that many bytes, preferably on an O_NONBLOCK descriptor, although by now that should no longer be necessary.
Hm, here’s a foreboding note in /usr/src/usr.bin/vi/cl/README.signal:
Run in cbreak mode. There are two problems in this area. First, the
current curses implementations (both System V and Berkeley) don't give
you clean cbreak modes. For example, the IEXTEN bit is left on, turning
on DISCARD and LNEXT. To clarify, what vi WANTS is 8-bit clean, with
the exception that flow control and signals are turned on, and curses
cbreak mode doesn't give you this.
We can either set raw mode and twiddle the tty, or cbreak mode and
twiddle the tty. I chose to use raw mode, on the grounds that raw
mode is better defined and I'm less likely to be surprised by a curses
implementation down the road. The twiddling consists of setting ISIG,
IXON/IXOFF, and disabling some of the interrupt characters (see the
comments in cl_init.c). This is all found in historic System V (SVID
3) and POSIX 1003.1-1992, so it should be fairly portable.
If you do a recursive grep for \b(TIOC[SG]ET[NP]|TC[SG]ET[SA]|tc[sg]etattr)\b on the non-kernel portions of /usr/src/, you should find stuff you can use. For example:
% grep -Pr '\b(TIOC[SG]ET[NP]|TC[SG]ET[SA]|tc[sg]etattr)\b' /usr/src/{{s,}bin,usr.{s,}bin,etc,gnu}
I would look at /usr/src/usr.bin/less/screen.c, down in the raw_mode() function. Riddled with ifdefs though it is in a quest for portability, that looks like the cleanest code for what you want to do. There’s also stuff lurking down in GNU.
OH MY, look in /usr/src/gnu/usr.bin/perl/h2pl/cbreak.pl! That must be my old code that I posted above. Interesting that it’s trickled out to every src system in the world. Scary, too, since it is twenty years out of date. Gosh, it's weird to see echoes of a younger self. Really hard to remember such particulars from 20 years ago.
I also see in /usr/src/lib/libcurses/term.h this line:
#define tcgetattr(fd, arg) ioctl(fd, TCGETA, arg)
in a bunch of ifdefs that are trying to infer termio or termios availability.
This should be enough to get you started.

Catch input from another shell

I currently have a C/C++ program which uses a barcode scanner as a keyboard, catches the input and does something with it. Here's the relevant parts of code:
int get_InStream() {
struct timeval tv;
fd_set fds;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
return FD_ISSET(STDIN_FILENO, &fds);
}
void nonblock(int state) {
struct termios ttystate;
tcgetattr(STDIN_FILENO, &ttystate);
if (state == 1) {
// ~ICANON: turn off canonical mode
// ~ECHO: not display character
//~ ttystate.c_lflag &= ~ECHO; // (ICANON & ECHO);
tcgetattr( 0, &ttystate); /* read curr. setting */
original_mode = ttystate; /* remember these */
ttystate.c_lflag &= ~ICANON; /* no buffering */
//~ ttystate.c_lflag &= ~ECHO; /* no echo either */
tcsetattr( 0 , TCSANOW, &ttystate); /* install settings */
//minimum of number input read.
ttystate.c_cc[VMIN] = 1;
}
else if (state == 0) {
//~ // turn on canonical mode
//~ ttystate.c_lflag |= ECHO;
tcsetattr(0, TCSANOW, &original_mode); /* undo -icanon, -echo */
}
// set the terminal attributes.
tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
}
bool keyState(int key) { // Uses ASCII table
bool pressed = false;
int i = get_InStream(); // Allows to read from terminal
if (i != 0) {
char c = fgetc(stdin);
if (c == (char) key) {
pressed = true;
} else {
pressed = false;
char string_key = c;
pthread_mutex_lock(&id_mutex);
// Append character to content buffer
strcat(content, string_key);
pthread_mutex_unlock(&id_mutex);
}
}
return pressed;
}
void* get_inputContent(void* arg) {
pthread_detach(pthread_self());
nonblock(1);
while (1) {
// if this returns True, content contains data
if (keyState(0x0A)) { // 0x0A in ASCII table corresponds to New Line
pthread_mutex_lock(&id_mutex);
printf("Read this %d characters through barcode scanner: %s\n", strlen(content), content); //DEBUG
// doSomething()
strcpy(content, "\0"); // empty out content
pthread_mutex_unlock(&id_mutex);
}
}
nonblock(0);
pthread_exit(NULL);
}
Right now this works well as a separate thread from the main program, but if I open another terminal while the main program is running and I leave the focus on the new one, the barcode input is not caught by the thread.
So I'd like, either in C/C++ or in Bash, to let's say share the input accross various terminals, so that my thread can use it. Is there any way to do this?
I've searched for various options:
another descriptor to use in select()
using export in Bash
writing to a shared file
but I'm not so sure for any of those. Any suggestions?
EDIT: the program is being run on Raspberry Pi OS, previously Raspbian if I'm not mistaken
This is a XY problem situation right here. Your problem 'X' is
How can I access the keyboard device as which the barcode scanner presents itself to the system regardless of the current state of the system?
But you think, that by solving the problem 'Y'
How can I keygrab input directed to a different terminal?
Problem Y is hard, because it has security implications. Problem X is easy, but its solution depends on the operating system being used.
You mention bash and POSIX style file descriptor. So I'm guessing, that you're on some flavor of Linux. And with that, the problem is easy to solve! Each and every input device presents itself as a evdev device under /dev/input/event<X>. Your barcode scanner will show up there, as well.
You could either implement the evdev protocol yourself. Or just use libinput to do the heavy lifting.
And it being present as an independent input device allows you to do a few things:
Use udev to control which user accounts get access to it.
Use udev to actually detach it from the terminals, so that the barcode scanner can not be used to input (possibly harmful) commands.

Sampling keyboard input at a certain rate in C++

What I want to do is sampling the keyboard input at a certain rate (e.g. 10-20 Hz). I use a while loop that read from stdin (I use read because I want to read asynchronously, e.i. I don't want to press enter every time) and then I have a pause before a new cycle starts to keep the sampling frequency stable.
The user press left/right arrow to give a command (to a robot). If nothing is pressed, the output is 0.
The problem is that, during the pause, the stdin buffer is written (I suppose), and so the read will return an old input. The final result is that the output is delayed. So if I press left the output immediately change to 1, but when I release it takes some seconds to return to 0. I want to remove this delay.
My aim is to sample just the more recent key pressed, in order to synchronize user input and output command without delays. Is there a way? Thank you in advance.
This is the method I'm using:
void key_reader::keyLoop()
{
char c;
bool dirty = false;
int read_flag;
// get the console in raw mode
tcgetattr(kfd, &cooked);
memcpy(&raw, &cooked, sizeof(struct termios));
raw.c_lflag &= ~(ICANON | ECHO);
// Setting a new line, then end of file
raw.c_cc[VEOL] = 1;
raw.c_cc[VEOF] = 2;
tcsetattr(kfd, TCSANOW, &raw);
//FD_ZERO(&set); /* clear the set */
//FD_SET(kfd, &set); /* add our file descriptor to the set */
//timeout.tv_sec = 0;
//timeout.tv_usec = 10000;
if (fcntl(kfd, F_SETFL, O_NONBLOCK) == -1)
{
perror("fcntl:"); // an error accured
exit(-1);
}
puts("Reading from keyboard");
puts("---------------------------");
puts("Use arrow keys to move the turtle.");
ros::Rate r(10);
while (ros::ok())
{
read_flag = read(kfd, &c, 1);
switch (read_flag)
{
case -1:
// case -1: is empty and errono
// set EAGAIN
if (errno == EAGAIN)
{
//no input yet
direction = 0;
break;
}
else
{
perror("read:");
exit(2);
}
// case 0 means all bytes are read and EOF(end of conv.)
case 0:
//no input yet
direction = 0;
break;
default:
ROS_DEBUG("value: 0x%02X\n", c);
switch (c)
{
case KEYCODE_L:
ROS_DEBUG("LEFT");
direction = 1;
dirty = true;
break;
case KEYCODE_R:
ROS_DEBUG("RIGHT");
direction = -1;
dirty = true;
break;
}
}
continuos_input::input_command cmd;
cmd.type = "Keyboard";
cmd.command = direction;
cmd.stamp = ros::Time::now();
key_pub.publish(cmd);
r.sleep();
}
}
I feel that the issue is with your subscriber rather than publisher. I can see that you have used rate to limit the publishing rate to 10Hz. Do confirm the publishing rate using Topic Monitor in rqt. Also setting a lower queue size for the publisher might help. Can't give a more definitive answer without referring to your subscriber node.

C++ write to stdin (in Windows)

I have a multi-threaded Windows console app whose control thread runs a user input loop like this:
char c;
do {
cin >> c;
// Alter activity based on c
} while(c != 'q')
// Tell other threads to close, .join(), and do cleanup
However at a certain time I want the program itself to be able to gracefully quit. The most obvious way to do so would be to put a "q\n" onto the stdin stream. Is there a reasonable way to do that?
Or a good alternative for a callback to force exit the main control loop (on the primary thread) so that the program falls through to the subsequent cleanup methods?
(The closest I have found so far is this which requires spawning a child process and seems like kludgy overkill at best.)
You can use another flag as loop break condition for all of your threads. The problem with interthread-communication can be solved by synchronization objects which came with C++11 like atomics or mutex.
I ended up using the suggestion from #Captain Obvlious, but even that was tricky (thanks Windows!): As soon as a control signal is handled it causes all sorts of problems with the unchecked char input loop. However, with the following additions any thread can gracefully end the program by calling GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, 0);
bool exiting = false;
bool cleanedUp = false;
void Cleanup() {
if(!cleanedUp) cleanedUp = true;
else return;
// Tell other threads to close, .join(), and do cleanup
}
// https://msdn.microsoft.com/en-us/library/ms683242(v=vs.85).aspx
bool ConsoleControl(DWORD dwCtrlType) {
exiting = true; // This will cause the stdin loop to break
Cleanup();
return false;
}
int main() {
SetConsoleCtrlHandler((PHANDLER_ROUTINE) ConsoleControl, true);
.
.
.
// Main user control loop
char c;
do {
cin >> c;
if(exiting) break;
if(c < 0) continue; // This must be a control character
// Alter activity based on c
} while(c != 'q')
Cleanup();
return 0;
}
This is an old question but I stumbled upon it and ended up solving the original problem.
I used the WriteConsoleInput function to simulate a user entering keystrokes to the console. This code sample is based off of This question.
HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
DWORD dwTmp;
INPUT_RECORD ir[2];
ir[0].EventType = KEY_EVENT;
ir[0].Event.KeyEvent.bKeyDown = TRUE;
ir[0].Event.KeyEvent.dwControlKeyState = 0;
ir[0].Event.KeyEvent.uChar.UnicodeChar = 'q';
ir[0].Event.KeyEvent.wRepeatCount = 1;
ir[0].Event.KeyEvent.wVirtualKeyCode = 'Q';
ir[0].Event.KeyEvent.wVirtualScanCode = MapVirtualKey('Q', MAPVK_VK_TO_VSC);
ir[1].EventType = KEY_EVENT;
ir[1].Event.KeyEvent.bKeyDown = TRUE;
ir[1].Event.KeyEvent.dwControlKeyState = 0;
ir[1].Event.KeyEvent.uChar.UnicodeChar = VK_RETURN;
ir[1].Event.KeyEvent.wRepeatCount = 1;
ir[1].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
ir[1].Event.KeyEvent.wVirtualScanCode = MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC);
WriteConsoleInput(hStdin, ir, 2, &dwTmp);
On my system MAPVK_VK_TO_VSC wasn't defined so I used 0 instead.
You should add error checking where appropriate.
I hope this helps.

Test whether key is pressed without blocking on Linux

I'm searching how to test if a key is pressed. The test shouldn't block the program. I can use a little library if it is not too heavy, but unfortunately ncurses is too much of a dependency to bring in.
I found a solution :
int khbit() const
{
struct timeval tv;
fd_set fds;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
return FD_ISSET(STDIN_FILENO, &fds);
}
void nonblock(int state) const
{
struct termios ttystate;
tcgetattr(STDIN_FILENO, &ttystate);
if ( state == 1)
{
ttystate.c_lflag &= (~ICANON & ~ECHO); //Not display character
ttystate.c_cc[VMIN] = 1;
}
else if (state == 0)
{
ttystate.c_lflag |= ICANON;
}
tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
}
bool keyState(int key) const //Use ASCII table
{
bool pressed;
int i = khbit(); //Alow to read from terminal
if (i != 0)
{
char c = fgetc(stdin);
if (c == (char) key)
{
pressed = true;
}
else
{
pressed = false;
}
}
return pressed;
}
int main()
{
nonblock(1);
int i = 0;
while (!i)
{
if (cmd.keyState(32)) //32 in ASCII table correspond to Space Bar
{
i = 1;
}
}
nonblock(0);
return 0;
}
It works well. Thanks for helping me. I hope it will help someone :)
I don't know what exactly you are looking for.
I recently did a Proof-of-Concept reading from "raw" input device nodes here: boost::asio read from /dev/input/event0. The example is with mouse, but Linux Kernel unifies input devices so keyboard would be about the same.
All you need here is UNIX permissions to open/read the device files (you could create them out-of-/etc-tree if that's more convenient for your deployment style).
The sample shows full asynchronous processing of events
I have used bindings to XTest (libxtst) (actually to send keystrokes); I imagine that this may contain the features you need
Then there is XInput which (as far as I can tell) constitute the XOrg way to abstract the "raw" input device streams I described in the first option