draper@inmet.inmet.com (10/29/90)
/* Written 1:38 pm Oct 27, 1990 by ramsey@NCoast.ORG in inmet:comp.lang.c */ /* ---------- "Trouble with curses" ---------- */ Hi, this is Connie. I'm accessing this net with a friends account. I have a question about the curses package. Heres the skeletal code: #include <stdio.h> #include <curses.h> #include <term.h> main() { int ch; /* init curses package */ initscr(); nonl(); cbreak(); noecho(); for (;;) { ch = getch(); switch(ch) { case 27: /* escape */ do_escape(); break; [ ... irrelevant stuff deleted ... ] } } } do_escape() { switch(tolower(getch())) { case 'e': do_exit(); break; [ ... irrelevant stuff deleted ... ] } } Basically what is happening is the the user presses ESCAPE key follow by 'E' key to exit. But I have to press 'E' twice after pressing ESCAPE to get an exit. I can't figure out why. Does anybody know ? I would prefer a post to net but if you must send mail please do not send it to this box instead send it to me at aj398@CLEVELAND.FREENET.EDU , Thankyou. /* End of text from inmet:comp.lang.c */ I made a few adjustments to the code above and then compiled it on a Sun4. I got it to run without any problems. Below is a copy of the code after I modified it. It was compiled and linked using the following command: cc test.c -lcurses -ltermcap -o test The modifications I made had nothing to do with the routines used to read in characters. It almost sounds like when you press the 'E' key the first time it is not getting read in by curses until a subsequent keystroke causes the getch() in the switch to read the character out of the buffer. I would modify the code to get the character and then switch on the char. By doing this you can run it under a debugger and see if the call to getch() is actually getting a char when it is suppossed to. - Dave Internet: draper@inmet.inmet.com UUNET: uunet!inmet!draper ----- Intermetrics Microsystems Software, Incorporated 733 Concord Avenue Cambridge, MA 02138 (617) 661-0072 x4573 ------------------------------------------------------------------------ /* Modified code with comments */ #include <stdio.h> #include <curses.h> /* #include <term.h> I did not need this */ #include <ctype.h> /* needed this for the isupper, tolower macros */ main() { int ch; /* init curses package */ initscr(); nonl(); cbreak(); noecho(); for (;;) { ch = getch(); switch(ch) { case 27: /* escape */ do_escape(); break; /* ... irrelevant stuff deleted ... */ } } } do_escape() { switch(tolower(getch())) { /* I would watch out on the above line of code. tolower is a */ /* macro that might have undesriable effects should the user */ /* enter a character that is already in lower case. A good */ /* thing to do would be check if the char was an upper case */ /* using "isupper" before calling "tolower". */ case 'e': do_exit(); /* added a definition and bosy for this function */ break; /* ... irrelevant stuff deleted ... */ } } /* clean up and exit */ do_exit() { endwin(); exit(); } /* end of modified code */ Internet: draper@inmet.inmet.com UUNET: uunet!inmet!draper ----- Intermetrics Microsystems Software, Incorporated 733 Concord Avenue Cambridge, MA 02138 (617) 661-0072 x4573
akcs.dgy@vpnet.chi.il.us (Donald Yuniskis) (10/29/90)
>Basically what is happening is the the user presses ESCAPE key follow by >'E' key to exit. But I have to press 'E' twice after pressing ESCAPE >to get an exit. I can't figure out why. Does anybody know ? most "magic buttons" (function keys, etc) send an escape sequence. curses tries to build these escape sequences into special "KEY_" (see curses.h) codes to make you life easier (:->). to do this, when an ESC is received, curses starts a nominal one second timer. if other keys come in before this timer expires, curses tries to combine them into a recognized escape sequence which is then reduced to a "KEY_" constant and propagated through the getch() mechanism. Presumably, this process only is acive when you have enabled keypad() mode. Suggestions: try a pause after the ESC to verify this is the mechanism which is hosing you. A work around is to use a "non-escape" sequence (the ESC key is frowned upon even tho' _everyone_ uses it!) or, try to find a "KEY_" code which may be supported by the variety of terminals you intend to support and code _that_ as the "ESC e" you are trying to use (if ((ch = getch()) == KEY_?????) ....) also, may wish to explicitly disable keypad() mode... (are we having fun yet??)
catfood@NCoast.ORG (Mark W. Schumann) (10/29/90)
In article <1990Oct27.173826.29771@NCoast.ORG> ramsey@NCoast.ORG (Connie using Cedric Ramsey's account) writes: >Hi, this is Connie. I'm accessing this net with a friends account. ... and welcome. Too bad about the Browns the other day, huh? >I have a question about the curses package. Heres the skeletal code: > [Much deleted for brevity] >do_escape() { > switch(tolower(getch())) { > case 'e': > do_exit(); > break; > [ ... irrelevant stuff deleted ... ] > } >Basically what is happening is the the user presses ESCAPE key follow by >'E' key to exit. But I have to press 'E' twice after pressing ESCAPE >to get an exit. I can't figure out why. Does anybody know ? Betcha your tolower is implemented as a macro, something like this: #define tolower(c) (isupper (c) ? c + 'a' - 'A' : c) (Portability purists will quibble with this, but what the hey it works in DOS. My point is this:) Notice that there is no way for this macro to help but evaluate the argument 'c' twice. The code generated in your case would be: do_escape() { switch (isupper (getch()) ? getch() + 'a' - 'A' : getch()) { case 'e': ....dah dah dah... } So your object code evaluates isupper(getch()) [gets a keystroke here], then depending on the result it evaluates getch() + 'a' - 'A' OR plain getch() [gets *a new* keystroke here in either case]. Your problem is the usual macro yuckies--expanding code inline causes this type of re-evaluation. Try this instead: do_escape() { int c; c = getch(); switch (tolower (c)) { ......... } } The variable 'c' of course gets evaluated only once as 'getch().' THEN you process its value without risking the side effects. Alternative: do_escape() { switch (getch()) { case 'e': case 'E': do_exit(); /* K & R says be careful, but it works */ break; ...... Hope this helps and that I wasn't over-explaining. -- ============================================================ Mark W. Schumann 3111 Mapledale Avenue, Cleveland 44109 USA Domain: catfood@ncoast.org UUCP: ...!mailrus!usenet.ins.cwru.edu!ncoast!catfood ============================================================
ravim@gtenmc.UUCP (ravim) (10/30/90)
In article <1990Oct27.173826.29771@NCoast.ORG> ramsey@NCoast.ORG (Cedric Ramsey) writes: > >Hi, this is Connie. I'm accessing this net with a friends account. >I have a question about the curses package. Heres the skeletal code: > >#include <stdio.h> >#include <curses.h> >#include <term.h> > > ch = getch(); > switch(ch) { > case 27: /* escape */ > do_escape(); > break; > [ ... irrelevant stuff deleted ... ] > } > } >} > >do_escape() { > switch(tolower(getch())) { If 'tolower' routine is a macro (defined in 'ctype.h') that evaluates its argument twice, then this could be the cause for your problem of having to press 'E' twice. One solution is :- do_escape() { register char c; c = getch(); switch(tolower(c)) { case 'e': .... >Basically what is happening is the the user presses ESCAPE key follow by >'E' key to exit. But I have to press 'E' twice after pressing ESCAPE >to get an exit. I can't figure out why. Does anybody know ? > >I would prefer a post to net but if you must send mail please do not >send it to this box instead send it to me at aj398@CLEVELAND.FREENET.EDU , > >Thankyou. Good luck. - Ravi Mandava -- ********************** #include <stddisclaimer.h> ************************** Ravi Mandava e-mail : ravim@gtenmc.gtetele.com or ravim@gtenmc.UUCP *******************************************************************************
draper@inmet.inmet.com (10/30/90)
I received mail today saying that I had correctly identified the line of code in the curses problem that was causing the problem, but I had missed the problem altogehter. These were my comments on the following line: switch(tolower(getch())) { /* I would watch out on the above line of code. tolower is a */ /* macro that might have undesriable effects should the user */ /* enter a character that is already in lower case. A good */ /* thing to do would be check if the char was an upper case */ /* using "isupper" before calling "tolower". */ It was brought to my attemntion that the macro 'tolower' might expand out so that getch() is called twice. That would cause the error that was described in the posting. I could not beleive that I missed that. I plead guilty to not thinking of that, BUT I DID NOT miss it. On the system I am using, Sun4 , in the file ctype.h , the macro tolower is defined as follows: #define tolower(c) ((c)-'A'+'a') I would like to thank the indivdual who sent mail to me on this (sorry I mistakenly deleted your mail before I saved it to a file) . It is a situation that I haven't ran across myself. Moral of the story: Always make sure you know, or at least think you know, how your macros will expand. When in doubt run the code throught cpp and look at the expansions. - Dave Internet: draper@inmet.inmet.com UUNET: uunet!inmet!draper ----- Intermetrics Microsystems Software, Incorporated 733 Concord Avenue Cambridge, MA 02138 (617) 661-0072 x4573
boyd@necisa.ho.necisa.oz (Boyd Roberts) (10/30/90)
In article <20900012@inmet> draper@inmet.inmet.com writes: > >Hi, this is Connie. I'm accessing this net with a friends account. >I have a question about the curses package. Heres the skeletal code: > > [deleted] > > switch(tolower(getch())) { > What's the bet that tolower() is a macro? And is coded to evaluate its argument _twice_? That way you get two calls to getch() instead of one. Eg: #define tolower(c) (chartab[(c)] & C_UPPER ? (c) - 'A' + 'a' : (c)) You've got to watch those macros when their arguments have side affects. Boyd Roberts boyd@necisa.ho.necisa.oz.au ``When the going gets wierd, the weird turn pro...''
weimer@ssd.kodak.com (Gary Weimer) (11/08/90)
In article <20900012@inmet> draper@inmet.inmet.com writes: > > switch(tolower(getch())) { > /* I would watch out on the above line of code. tolower is a */ > /* macro that might have undesriable effects should the user */ > /* enter a character that is already in lower case. A good */ > /* thing to do would be check if the char was an upper case */ > /* using "isupper" before calling "tolower". */ ANSI complient definition of tolower() does call islower() before making the conversion. In this case, the code "expands" to: switch(islower(getch()) ? tolower(getch()) : (getch())) { Note that you are now always making two calls to getch() (as someone has already pointed out is a possibility).
scs@adam.mit.edu (Steve Summit) (11/08/90)
In article <1990Nov7.194154.6071@ssd.kodak.com> weimer@ssd.kodak.com (Gary Weimer) writes: >ANSI complient definition of tolower() does call islower() before >making the conversion. In this case, the code "expands" to: > switch(islower(getch()) ? tolower(getch()) : (getch())) { >Note that you are now always making two calls to getch() (as someone >has already pointed out is a possibility). Yes, and the earlier pointings-out were almost as misleading as this one. ANSI does specify (Section 4.3.2.1) that tolower() leaves non-upper-case letters alone, HOWEVER it also requires (Section 4.1.6) that "Any invocation of a library function that is implemented as a macro shall expand to code that evaluates each of its arguments exactly once..." (The rules in Section 4.1.6 apply unless they are explicitly retracted for a particular library routine, but Section 4.3.2 contains no such retraction for tolower or toupper.) We can conclude, then, that an ANSI- compliant tolower must be implemented as an actual function, or perhaps as a macro with a lookup table, but certainly NOT as #define tolower(c) (isupper(c) ? (c) + ('a' - 'A') : (c)) /* WRONG */ To repeat: switch(tolower(getchar())) is "safe" under an ANSI compiler. (Whether that's the best way to write it is another question.) The earlier correspondent was having to hit double characters due to some other problem, or due to an invalid tolower() implementation. Moving the getchar() out of the tolower() is not required, except to work around a buggy compiler/library, or perhaps to improve readability or error checking. Steve Summit scs@adam.mit.edu
gwyn@smoke.brl.mil (Doug Gwyn) (11/09/90)
In article <1990Nov7.194154.6071@ssd.kodak.com> weimer@ssd.kodak.com (Gary Weimer) writes:
-ANSI complient definition of tolower() does call islower() before
-making the conversion. In this case, the code "expands" to:
- switch(islower(getch()) ? tolower(getch()) : (getch())) {
-Note that you are now always making two calls to getch() (as someone
-has already pointed out is a possibility).
While an implementation may evaluate the argument twice, it is definitely
not conformant to the ANSI C standard if it does.
duncan@sysint.uucp (Duncan MacGregor) (11/14/90)
In a recent posting, "draper" (sorry, I lost track of the precise article) complained that characters read through the curses library disappeared after an ESC (escape, octal 033) character was read. In essence, the 'C' code was expecting an ESC followed by the letter 'e' to indicate "exit from the system". The problem lies not in curses but (I believe) in termcap. I know the problem exists in "terminfo", which is the replacement for termcap in System V Rel. 2 and all descendant systems; I believe it was inherited (or should I say inherent?) from termcap. The problem is caused by the necessity of dealing with "function keys", "cursor keys", and other symbolic keys on the keyboard. When one of these keys is pressed, the terminal usually sends a STRING of characters to the host. In almost every case, the first character of such a string is ESC, to warn the host (and thus termcap/terminfo) that a function key was pressed. These strings are therefore often known as "escape sequences". When an ESC character is received from the terminal, however, termcap or terminfo can NOT assume that a function/cursor key was pressed; it is perfectly possible (as in the case of ESC+'e' above) that the user typed the ESC key by itself. The solution for this can only be described as kludgy (I think Henry Spencer cited it recently with some rather uncomplementary prose). When the ESC is received, a timer is started. If the timer expires (after ~1/10 sec.) and another character has NOT been received, the ESC character is assumed to have come from the ESC key, and not from a function key. If a character arrives before the expiration, it is added (as part of an escape sequence!) and the timer restarted. While it is possible to change the length of time before the timer expires in the termcap/terminfo source code (it is actually using the UNIX terminal driver for this), a fast typist can easily beat most such settings of the timer. The letter 'e', therefore, since it is typed right after an ESC, may be grabbed as part of a supposed "escape sequence" string. Even if that does not happen, there IS a delay before the timer expires. In terminfo, if the characters after ESC do not correspond with a known key for the current terminal type, it will re-interpret the string as separate characters (I think). This certainly seems to be happening with the example given by "draper". Some newer terminals (eg. the DEC vt200) can use a special 8-bit character to mark the beginning of "escape sequence" strings if you use the terminal's "8-bit" mode. Alas, there is NO support for 8-bit characters in terminfo or termcap (as far as I know). The cryptic notation used in the termcap and term- info databases cannot support it either. Just a suggestion: maybe somebody could develop a version of termcap that would allow macros to be freely used in declaring strings or numbers? That might make it MUCH easier to represent 8-bit characters that start "escape sequence" strings. Hope this is helpful ... -- Duncan MacGregor Systems Interface Inc. UUCP: uunet!mitel!cunews!cognos!sysint!duncan || duncan@sysint.UUCP #include <std/disclaim.h>; Life is a zig-zag; too many people zig as always - they shoulda zagged. (Anon)
chris@mimsy.umd.edu (Chris Torek) (11/15/90)
In article <1990Nov14.061635.6183@sysint.uucp> duncan@sysint.uucp (Duncan MacGregor) writes: >... characters read through the curses library disappeared after >an ESC (escape, octal 033) character was read. ... > The problem lies not in curses but (I believe) in termcap. I know the >problem exists in "terminfo", which is the replacement for termcap in System V >Rel. 2 and all descendant systems; I believe it was inherited (or should I >say inherent?) from termcap. No, the problem is in curses (if any particular bit of software can be blamed), not termcap or terminfo. Neither termcap nor terminfo provide any input-handling routines. They exist only to describe the terminal's interface and handle output parameters (for, e.g., cursor motion). >The problem is caused by the necessity of dealing with "function keys", >"cursor keys", and other symbolic keys on the keyboard. Right. The timing code for curses `getch', however, is entirely in curses (and the 4BSD termcap-based curses provides no function key decoding at all, so it is not in all versions). Because keystrokes can be arbitrarily delayed (e.g., by networks), the timer hack is not such a great solution. My own solution is never to use function keys. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 405 2750) Domain: chris@cs.umd.edu Path: uunet!mimsy!chris