I/O has been sucked into operating systems for some time now, so you have to go through it. For example, on Linux ASM programs can use READ and WRITE (interrupts 3 & 4) to perform some basic IO. To do char by character input I think they must use the IOCTL system call to set the IO mode.
Because the system calls differ among OS', and because C seems to be every OS's favorite language, the non-portable syscalls are eschewed for slighly more portable C libraries.
http://cboard.cprogramming.com/c-programming/63166-kbhit-linux.html has an implementation of kbhit in C for Linux. The routine is to be continually polled, and it returns 0 if no key pressed, otherwise a keycode. In that thread they ask the poster why he didn't use a curses library, so I guess that is common practice.
Does Racket have a REPL? Changing how characters are read might negatively affect the lifespan of your Scheme or Lisp. The last time I tried to do something like this in Forth (GForth, which has a repl), I concluded the best way to coexist a repl and key-by-key input together harmoneously was to rewrite the language. This way I could control when the repl took input and when my program took input. Emacs may be emacs instead of a lisp package because its authors come to the same conclusion.