Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I am not certain if this is ultimately the correct place to ask this but I am developing a program which requires real-time input from the user. That is; it needs to be able to read the keys being pressed(and optionally held down and released) in real time. What makes this issue a bit troublesome is that I absolutely can not use any kinds of libraries for this; not even the standard C library. So this needs to be handled with raw kernel syscalls and int 0x80.

I thought I would set stdin to non-blocking mode with fcntl and O_NONBLOCK and then read the input data from stdin. However, to my surprise my code seems not to be working correctly. Right now it seems that the input gets to be read from stdin after the Enter key is being pressed. The tricky part is that I am finding it troublesome to figure out whether it is the fault of:

  • My code not behaving right(most probable)
  • My terminal/user/process(whatever handles it?) not having non-blocking write access to stdin(not probable).
  • Me assuming wrongly that I can read direct keyboard input from stdin.

So my question is that what is the best/usual way of reading direct keyboard interaction if not by setting the stdin to non-blocking mode and reading the keyboard input data from there? Or am I correct and should just fix my code? :)

(And for the curious, no, it's not a keylogger. It is for a very size limited(executable size <= 256 bytes) demoscene product which requires user to be able to ESC out from the intro.)

share|improve this question
1  
What environment is your program running in (text terminal, X, console, …)? I think you're making a lot of unwarranted exceptions here, starting with expecting the keyboard device to be connected to stdin (no, applications don't normally read the keyboard directly). I doubt you can do this in 256 bytes. –  Gilles Jan 24 '12 at 0:40
add comment

2 Answers 2

up vote 2 down vote accepted

If you're going to be reading from stdin, you should read the manpage for tcsetattr, and specifically the section about ‘Canonical and noncanonical mode’ (ICANON). Unless you disable ICANON on stdin, input is line-oriented (you get nothing until Enter is pressed). That's the case even for O_NONBLOCK.

If in doubt, strace -v stty raw >&log; stty sane; grep TCSETSW logand see what it does to stdin. You can use the same system call with identical parameters to uncook the terminal. Be careful, unless you restore the settings on exit, the terminal will be useless after your program terminates. (this is why I did stty sane after stty raw)

Using raw hardware I/O is probably a very bad idea, unless you're absolutely certain about your target hardware, and/or you want to anticipate every type of keyboard there is.

Oh, despite it being ‘raw’, you're still reading somewhat cooked input from the keyboard: you won't get scancodes, you'll get ASCII (or UTF-8, most likely). So Escape is decimal 27, not 1 (as on the original IBM PC keyboard).

Alternative suggestion that requires superuser access: read up on event devices, open the right one and read Linux event structures from it. This method returns raw press/release events and key (they're the kernel's internal key codes, though, not hardware scancodes). Check out /usr/include/linux/input.h for the specifics. Finding the right eventX device to open in 256 bytes of code is another matter altogether, of course.

share|improve this answer
    
Thank you for this! I can set the tty(?) to raw mode before running the program and set it to sane afterwards, which works around the problem. Not very conveinient or portable and incurs ~20 byte overhead(uncompressed) but definitely better than I had thought before! It seems that using ioctl + TCSETSW requires storing the termios struct which is 57 bytes, which obviously is very costy for me in this case. I need to experiment with this a bit, but so far I found a "good enough" solution for my needs. Thanks again, saved my day. :) –  zxcdw Jan 24 '12 at 2:45
    
You're welcome! Depending on the rules, you could also do system("stty raw") which is a bit more compact code-wise (but more costly computation-wise) than doing termios manipulations. –  Alexios Jan 24 '12 at 2:53
add comment

You need to set the tty to raw mode, see man stty.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.