getch()

I'm writing a program that performs some password masking and i'm using the getch() function from the conio.h library.

The code is as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
int c;
string password = "";

do   //Loop until 'Enter' is pressed
         {
         c = getch();
         switch(c)
            {
            case 0:
               {
               break;
               }
            case '\b':
               {
               if(password.size() != 0)  //If the password string contains data, erase last character
                  {
                  cout << "\b \b";
                  password.erase(password.size() - 1, 1);
                  }
               break;       
               }   
            default:
               {
               if(isalnum(c) || ispunct(c))
                  {
                  password += c;
                  cout << "*";           
                  }
               break;     
               }      
            };
         }
      while(c != '\r');


Now, this code works fine for accepting the password and saving it and such. the problem is when the user starts typing in special characters like the arrow keys or f1-f12 for example.

From my understanding, those have extended ASCII values but they're the same numbers as the standard set of ASCII values. For example, the right arrow key has a value of 77, but 77 is also the ASCII value for "M".

getch() somehow can differentiate the two from each other but my program can't. All it sees is the 77 and because that falls within the range of allowed keypresses it adds it to the password.

Is there either a way to disable those special keys? Or does somebody know exactly how the getch() function interprets those keys and how to manipulate it?

Thanks!
Change line 11 to
1
2
getch();
break;


When getch() returns a zero it means that the next thing you get from it will be an "extended key code" --function keys, arrow keys, etc.

Hope this helps.
Hey Duoas. We've worked on this before...and that didn't work. I never managed to figure out how to capture that zero.
Hmm, I remember that.

Perhaps you ought to tell us:
1) Compiler model and version
2) OS
3) Where you got <conio.h> if it didn't come with #1
4) hardware (CPU and keyboard)

Conio.h is an old library, and not every version of it works correctly. It can also break on newer hardware or OS-features.
I figured it out. the getch() function returns one of two things depending on the special key. It returns a 0 for the f keys but it returns a 0xE0 for the arrow keys and home, end, etc...
Hmm, that makes sense on modern "Windows" keyboards. Sorry I didn't think of that...
You might be able to experiment with fgetc using stdin as the stream. I do not know if it treats them differently, however.
I'm curious though. What exactly is 0xE0?
Well...

If you run some interpreters or other programs who take control of the window and allow you to enter things you may notice that hitting an arrow key will generate a string of symbols. On my Linux terminal, for example, if you hit the up arrow, it produces ^]]A (however, the first ^] combo is one letter).

This might help you a bit, seeing as I return 3 letters instead of 2, as said before.


Also, to answer your question, 0xE0 might be the ^] combo.
PC keyboards

You will notice that modern PC keyboards have those little "windows" and "application" keys between the Ctrl and Alt keys either side of the spacebar. That is a Microsoft protocol: you've got a Windows Keyboard. Yep, a keyboard designed for your OS.
http://en.wikipedia.org/wiki/Windows_key

Keyboard layout and shortcuts
http://www.internet4classrooms.com/winkeyboard.htm
http://www.seoconsultants.com/windows/keyboard/

PC keyboards work by signalling a hardware interrupt. The OS then reads (port $60 I think) to obtain the code in the keyboard's memory buffer. This is usually a "make" or "break" code: "make" for key presses (bit 7 clear), and "break" for key releases (bit 7 set). Each code is therefore seven bits wide --enough for 128 different keys.

When getch() and the like were first designed, the question was: what exactly is the ASCII char for <left arrow>? BASIC did it by co-opting the US,FS,RS,GS ASCII codes, but the DOS services decided against changing the meaning of specific ASCII codes and simply returned two values: a zero to signify that what follows was not an ASCII code, and then the actual keyboard scancode (sans the make/break bit). The numeric keypad, for example, has for keys '1'..'9' the scancodes 71,72,73,75,76,77,79,80,81.

The original XT keyboards worked exactly as you would expect: each key had its own number. With the advent of the 101/102-key AT keyboards, we started to see some "escape" codes --values that signalled that the next code is for some special key but were otherwise meaningless by themselves. The special code was... <drumrolll> ... E0h. The purpose was obstensively that the OS was to intercept the next key instead of the active application program.

Today there are a lot of keys or key combinations that have extended key sequences (two or three key codes). If you are up to a good read then check out this page all about PC scancodes:
http://www.win.tue.nl/~aeb/linux/kbd/scancodes.html


Other keyboards

One of the things that made the PC so popular was the hardware specification standardization. Not so in the mainframe and mini world. It seemed at times that various keyboard manufacturers tried to out-do themselves with new and different keyboard designes. Some keyboards can send as many as (I think) fifteen codes for a single key press (though granted, that would be for some oddball, non-alphanumeric key).

Between the CRT and keyboard, the difficulty of dealing with all the weird and wacky hardware combinations caused the birth of the termcap database: a huge text file that lists hardware by its name (or names!) and all the codes needed to communicate with it.

It was a bit of a mess, so one day esr decided to clean it up a bit and now we have the terminfo database and (n)curses libraries to make playing with the hardware easy. (Well, easier than it was, at least. RU, for example, refuses to update its local terminfo databases to match the actual hardware in use because the suits want the OS to "meet the specs" across all campuses. Which sort of defeats the purpose of having the database to begin with... but enough about that.) Some hardware is so weird and/or undocumented and/or ancient that the terminfo database lacks more than the most basic data for it.

X makes things a lot easier because it handles all the hardware oddities for you and provides a nice, simple interface for your application programs. You don't even have to do anything but tell X what key events you want to know about, and it will deliver keystrokes to your application's doorstep, as it were, all nice and packaged and pretty. (Windows does the same thing, BTW. You could technically run Windows using a SunSPARC keyboard.)


getch()
The getch() function was invented by Microsoft (not Borland, as I also used to think) as a convenient interface to the DOS 21h services (function 07h I think, direct console input, no echo). Borland expanded upon it and just skipped the OS and went directly to the hardware (which in those days, actually had an observable difference in speed). As such, the getch() function has always been fairly tied to the Intel PC hardware, and not very translatable to other systems (it can be done, but it is invariably not worth the grief).


[edit] ^] means Ctrl ']', which is the Escape key code.
Last edited on
Topic archived. No new replies allowed.