From: Russell Marks (russell.marks@dtn.ntl.com)
Date: Tue 14 Mar 2000 - 15:35:41 IST
> > > > When using vga_getch() and vga_getkey(), how do you avoid a value of "27" > > > > applying to more than just the "Esc" key? I know the directional arrows > > > > are "escaped", and so pressing the up arrow sends (at first) the same > > > > signal as the Esc key. This is the same problem as you get with ncurses. > > > > > > > > So, what's the answer? Can this be done without raw keyboard mode? > > [...] > > > Use vga_getkey() to read the keyboard, if you see an ESC read it again > > > immediately (without waiting). If you get 0 then no other key was > > > pressed and you have an ESC key press, if you get anything else then > > > treat it as an escape sequence. > > > > In the spirit of "show me the code" :-), there's a working (I hope) > > example of how to do this below (after all my verbiage). > > I didn't think my explanation needed any futher clarification which is > why I didn't include any code in this case. You're right, it didn't, but: - parsing all the keys is a pain. I had working code to do it, so I thought I'd post it. I meant the comment you quoted above as `yes, and here's how to do it', rather than `you should have included some code'. - I thought it was worth pointing out why ncurses takes the approach it does, and why it just so happens you can get away without waiting when using svgalib (as you know you're on a console and (fortunately) Linux shoves the multi-byte keypresses at you in one atomic chunk). > I do "show you the code" > when I think its usefull or necessary (check the logs for my posting > on the anti aliased line code and pretty picture). Yes, I remember, very pretty. :-) > > BTW, the reason this read-without-waiting works is because Linux dumps > > the whole string generated by the key into your input at once (AFAIK), > > something you can't depend on over network or serial connections. So [...] > > > I don't know about this ncurses problem, but this method also works with > > > normal terminals connected to a unix box, you just need to be sure to > > > > Ummm, for terminals you really ought to wait a bit, rather than > > reading the chars after Esc immediately. If it happens to work without > > waiting, it's only luck and/or buffering. :-) (I know you mentioned > > disabling buffering, but that won't affect a UART - using a multi-byte > > buffering UART like a 16550A might be all that's saving you here.) > > ncurses may wait a stupidly long time (IMHO), but you really should > > wait at least a *short* time. (But as I say, there's no need to wait > > when using svgalib, so I'm veering off-topic.) > > I didn't want to go completely off topic and explain the exact line > setting etc but basically you set the line disaplin to have a time out > of about 3 char transmition periods and you short circuit this by a > 'minimum number of chars to wait for' to 2. You then issue a read() > requesting a short string (minimum of 2 but could be several chars) and > the read will return a single char for normal keys including ESC and > multiple chars in the case where a function key has been pressed. > read() returns the count of chars read into the buffer. In effect the > programmer sees an ESC+ch without a pause between the two but will still > be using non-blocking non-buffered IO. This is a neat idea, but it's broken in the general case because you can never be sure of controlling all the buffering, and here's an example of how the assumption that you're doing so can bite. Ironically enough, this is an example of buffering breaking this scheme, rather than saving it as it usually would. :-) First, some background. I looked at how Linux works on 16550As a while back - at 1200 baud or less it does things a byte at a time (a hack for mice I suspect), but at other rates it buffers 8 bytes of input (more precisely, asks to be interrupted when 8 bytes have been received, leaving the other 8 as plenty of insurance against overrun). You can't do anything to change this without modifying the kernel; it's hardcoded. [1] Now, say I have a fairly heavily-loaded machine I'm using (among other things) as a terminal to my main (16550A-equipped) Linux box, connected via a plain old serial connection. Then say I've typed `abcd' at the terminal program, then pressed cursor-up twice, then typed `wibble' - without this loaded machine managing to send any of it (which is a bit crap, but maybe it's a Windows box reading a floppy [2]). Then, it suddenly gets the chance to do so, and sends it all as fast as its little legs can carry it. :-) Hey presto, you get `abcd^[[A^[' in the buffer, which the kernel dutifully gives to your program. The buffering you specified is irrelevant; the kernel (and hardware) decides what's happening at this level, and you can't change it. Shortly after getting those 8 bytes (but much later than your 3 char transmission periods, I might add), you finally get the `[Awibble'. But - you didn't wait for it. So you don't see the second cursor-up. Whoops. I suspect there are *lots* of ways this sort of thing can happen (I bet telnet over the net is a great example, but buffering might save you there). This was just an example that I thought stood a decent chance of being convincing, since I could point to the kernel source and everything. :-) [1] From drivers/char/serial.c in 2.2.13: > static struct serial_uart_config uart_config[] = { > { "unknown", 1, 0 }, > { "8250", 1, 0 }, > { "16450", 1, 0 }, > { "16550", 1, 0 }, > { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, [...] > /* Set up FIFO's */ > if (uart_config[info->state->type].flags & UART_USE_FIFO) { > if ((info->state->baud_base / quot) < 2400) > fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; > else > fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; > } (`info->state->baud_base / quot' is the bps it's running at.) [2] Reading/writing a floppy brings anything else running on W9x to a grinding halt, pretty much - I'm not exaggerating things here. Fscking pathetic, really... > I do appriciate that its possible to execute several million machine > instructions between two consequtive received chars on a uart - really > I do, honest :-) Indeed, but having to wait for the time taken for 8 chars in order to be sure means that waiting for the time taken for 3 isn't good enough. And some UARTs come with bigger buffers, and even if you can reliably detect the UART type on every OS your program is going to be run on, that doesn't cope with links not running over a serial connection, and is prone to not knowing about new UARTs, or breaking if someone decides to (say) up the kernel's `trigger' position to 14 bytes (as is possible on a 16550A). This sort of thing is why ncurses waits quite a long time after Esc, which brings us full circle. Ok then, it might be nice if someone added a Linux-specific hack to ncurses so that it doesn't wait on consoles, you might think - but even then, you can have a serial console which AFAIK is not really distinguishable from the usual type, because it uses the same device major/minor with only the kernel knowing that there's anything special about it. (That is, after all, the whole point of having the option in the first place.) Or, in other words, here we go again. :-) ObSvgalib: But, luckily, none of this is a problem with svgalib. :-) -Rus.
This archive was generated by hypermail 2.1.4 : Wed 21 Jan 2004 - 22:10:23 IST