Reading a single character from the keyboard

Started by
9 comments, last by Mastaba 18 years, 4 months ago
How can I make a function/method to read a character from the keyboard and return that character? I've been using conio.h and getch() in c++, but I have no idea how to do this in other languages (and peeking inside conio.h didn't get me much). What I'm looking for is a way to return a character at the console in a single press without having to press enter, such as with Console::Read(). I know how to do this with a Windows Form/Java Frame, but I want to focus on the console. Is there a general theory behind this that will work in most languages, or is it language-specific?
Advertisement
Each language that is designed to receieve input from std::in will have it's own implementation for getting it. conion.h is an old style C header and therefore looking in it wouldn't help you learn anything that isn't to do with C.

ace
I know this isn't in C++ (it's in 16-bit x86 ASM), but still:

; first we call the interrupt to get the key; when the interrupt is called, the ASCII code of the key pressed will be in ALmov ah, 00 ; set function to 00h (read key)int 16h    ; call the keyboard interrupt; now, we can optionally print out the key that was pressed; if you don't want to do this, just omit this partmov dl, al   ; tell DOS which character to outputmov ah, 02h  ; tell DOS to output a characterint 21h      ; call the interrupt


Just a look at how things might go a bit lower-level. Of course, it won't work like that for Windows, but that's how it might have worked in the Old Days.
I have some simple examples of how to write your own keyboard ISR.
It is written for games, so it will basically just track every key press at all times, without any delay.
Since this is 'very old-style', and I'm not sure this is what you want, I wont
post it unless you realy want it.

If you are writing games in 16 bit dos environment this might just be what you want

Maybe this is better http://benryves.com/tutorials/?t=winconsole&c=all
Oberon_Command: Is it possible to use code like that you provided along with some inline assembly to get similar results in a higher-level language?

pulpfist: Go ahead and post it. I'll know if it helps once I see it.
Ok I got most of this stuff from an old book by Andre LaMothe

keydef.h
#ifndef __KEYDEF_H__#define __KEYDEF_H__#define MAKE_ESC          1#define MAKE_1            2#define MAKE_2            3#define MAKE_3            4#define MAKE_4            5#define MAKE_5            6#define MAKE_6            7#define MAKE_7            8#define MAKE_8            9#define MAKE_9            10#define MAKE_0            11#define MAKE_MINUS        12#define MAKE_EQUALS       13#define MAKE_BKSP         14#define MAKE_TAB          15#define MAKE_Q  16#define MAKE_W            17#define MAKE_E            18#define MAKE_R            19#define MAKE_T            20#define MAKE_Y            21#define MAKE_U            22#define MAKE_I            23#define MAKE_O            24#define MAKE_P            25#define MAKE_LFT_BRACKET  26#define MAKE_RGT_BRACKET  27#define MAKE_ENTER        28#define MAKE_CNTRL  29#define MAKE_A            30#define MAKE_S            31#define MAKE_D            32#define MAKE_F            33#define MAKE_G            34#define MAKE_H            35#define MAKE_J            36#define MAKE_K            37#define MAKE_L            38#define MAKE_SEMI         39#define MAKE_APOS         40#define MAKE_TILDE        41#define MAKE_SHIFTL     42#define MAKE_BACK_SLASH   43#define MAKE_Z            44#define MAKE_X            45#define MAKE_C            46#define MAKE_V            47#define MAKE_B            48#define MAKE_N            49#define MAKE_M            50#define MAKE_COMMA        51#define MAKE_PERIOD       52#define MAKE_FOWARD_SLASH 53#define MAKE_SHIFTR  54#define MAKE_PRT_SCRN     55#define MAKE_ALT  56#define MAKE_SPACE        57#define MAKE_CAPS_LOCK    58#define MAKE_F1           59#define MAKE_F2           60#define MAKE_F3           61#define MAKE_F4           62#define MAKE_F5           63#define MAKE_F6           64#define MAKE_F7           65#define MAKE_F8           66#define MAKE_F9           67#define MAKE_F10          68#define MAKE_NUM_LOCK     69#define MAKE_SCROLL_LOCK  70#define MAKE_HOME         71#define MAKE_UP  72#define MAKE_PGUP         73#define MAKE_NUM_MINUS    74#define MAKE_LT           75#define MAKE_CENTER       76#define MAKE_RT          77#define MAKE_NUM_PLUS     78#define MAKE_END          79#define MAKE_DN           80#define MAKE_PGDWN        81#define MAKE_INS          82#define MAKE_DEL          83#define BREAK_ESC          129#define BREAK_1            130#define BREAK_2            131#define BREAK_3            132#define BREAK_4            133#define BREAK_5            134#define BREAK_6            135#define BREAK_7            136#define BREAK_8            137#define BREAK_9            138#define BREAK_0            139#define BREAK_MINUS        140#define BREAK_EQUALS       141#define BREAK_BKSP         142#define BREAK_TAB          143#define BREAK_Q      144#define BREAK_W            145#define BREAK_E            146#define BREAK_R            147#define BREAK_T            148#define BREAK_Y            149#define BREAK_U            150#define BREAK_I            151#define BREAK_O            152#define BREAK_P            153#define BREAK_LFT_BRACKET  154#define BREAK_RGT_BRACKET  155#define BREAK_ENTER        156#define BREAK_CNTRL   157#define BREAK_A            158#define BREAK_S            159#define BREAK_D            160#define BREAK_F            161#define BREAK_G            162#define BREAK_H            163#define BREAK_J            164#define BREAK_K            165#define BREAK_L            166#define BREAK_SEMI         167#define BREAK_APOS         168#define BREAK_TILDE        169#define BREAK_SHIFTL       170#define BREAK_BACK_SLASH   171#define BREAK_Z            172#define BREAK_X            173#define BREAK_C            174#define BREAK_V            175#define BREAK_B            176#define BREAK_N            177#define BREAK_M            178#define BREAK_COMMA        179#define BREAK_PERIOD       180#define BREAK_FOWARD_SLASH 181#define BREAK_SHIFTR       182#define BREAK_PRT_SCRN     183#define BREAK_ALT          184#define BREAK_SPACE        185#define BREAK_CAPS_LOCK    186#define BREAK_F1           187#define BREAK_F2           188#define BREAK_F3           189#define BREAK_F4           190#define BREAK_F5           191#define BREAK_F6           192#define BREAK_F7           193#define BREAK_F8           194#define BREAK_F9           195#define BREAK_F10          196#define BREAK_NUM_LOCK     197#define BREAK_SCROLL_LOCK  198#define BREAK_HOME         199#define BREAK_UP           200#define BREAK_PGUP         201#define BREAK_NUM_MINUS    202#define BREAK_LT           203#define BREAK_CENTER       204#define BREAK_RT           205#define BREAK_NUM_PLUS     206#define BREAK_END          207#define BREAK_DN           208#define BREAK_PGDWN        209#define BREAK_INS          210#define BREAK_DEL          211#endif


keyboard.h
#ifndef __KEYBOARD_H__#define __KEYBOARD_H__#define KEYBOARD_INTERRUPT	0x09#define PIC_PORT 			0x20#define KEY_BUFFER 			0x60#define KEY_CONTROL 		0x61#define KEY_UP				0x00#define KEY_DOWN			0x01#include "keydef.h"class Keyboard{private:	static int keyboard_state[128];	static int nkeys_active;#ifdef __cplusplus    #define __CPPARGS ...#else    #define __CPPARGS#endif    static void interrupt keyboard_driver(__CPPARGS);public:	static void init();	static void exit();    static bool key_up(unsigned int make_code) { return keyboard_state[make_code] == KEY_UP; }    static bool key_down(unsigned int make_code) { return keyboard_state[make_code] == KEY_DOWN; }	static int keys_active() { return nkeys_active; }};#endif


keyboard.cpp
//==================================#include "keyboard.h"#include <dos.h>int Keyboard::keyboard_state[128];int Keyboard::nkeys_active = 0;void interrupt (*old_keyboard_isr)(__CPPARGS);//==================================void Keyboard::init(){	int index;    for(index=0; index<128; index++)    	keyboard_state[index] = KEY_UP;    old_keyboard_isr = _dos_getvect(KEYBOARD_INTERRUPT);    _dos_setvect(KEYBOARD_INTERRUPT, keyboard_driver);}//==================================void Keyboard::exit(){	_dos_setvect(KEYBOARD_INTERRUPT, old_keyboard_isr);}//==================================void interrupt Keyboard::keyboard_driver(__CPPARGS){	int raw_scan_code = 0;	_asm {    	sti        in al, KEY_BUFFER        xor ah, ah        mov raw_scan_code, ax        in al, KEY_CONTROL        or al, 82h        out KEY_CONTROL, al        and al, 7fh        out KEY_CONTROL, al        mov al, 20h        out PIC_PORT, al    }    if(raw_scan_code < 128) {    	if(keyboard_state[raw_scan_code] == KEY_UP) {        	nkeys_active++;            keyboard_state[raw_scan_code] = KEY_DOWN;        }    }    else {		if(keyboard_state[raw_scan_code - 128] == KEY_DOWN) {        	nkeys_active--;            keyboard_state[raw_scan_code - 128] = KEY_UP;        }    }}//==================================


main.cpp
//================================//#include <math.h>#include "timer.h"#include "keyboard.h"#include "video.h"#include "bitmaps.h"//================================int smiley_x = 156, smiley_y = 96;int process_keys(const Keyboard &kb);int render_buffer(VGA &v);//================================int main(int argc, char **argv){	Keyboard keyboard;    VGA vga;    Timer timer;    keyboard.init();    vga.init(0x0013, 320, 200);    timer.set_hz(Timer::hz60);    timer.start();    do {        render_buffer(vga);        vga.flip();        timer.wait(1);        timer.start();    } while(!process_keys(keyboard));    timer.set_hz(Timer::hz18);    vga.exit();    keyboard.exit();    return 0;}//================================int process_keys(const Keyboard &kb){	// process keys    if(kb.keys_active())    {    	if(kb.key_down(MAKE_ESC))        	return 1;        if(kb.key_down(MAKE_W)) {        	smiley_y--;            if(smiley_y < 0)            	smiley_y = 0;        }        if(kb.key_down(MAKE_S)) {        	smiley_y++;            if(smiley_y > 200 - 8)            	smiley_y = 200 - 8;        }        if(kb.key_down(MAKE_A)) {        	smiley_x--;            if(smiley_x < 0)            	smiley_x = 0;        }        if(kb.key_down(MAKE_D)) {        	smiley_x++;            if(smiley_x > 320 - 8)            	smiley_x = 320 - 8;        }    }    return 0;}//================================int render_buffer(VGA &v){	v.clear();	v.blit(smiley_x, smiley_y, bmp_smiley, 8, 8);    return 0;}//================================


Its pretty much the hardest possible way to read one single character on a 16 bit platform hehe
For a win32 project you can check out the GetAsyncKeyState function instead ^^
Why not use an API such as Win32 and simply do:
#include <windows.h>LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, 						 WPARAM wParam, LPARAM lParam){	switch(msg)	{	case WM_CLOSE :		DestroyWindow(hwnd);		break;	case WM_DESTROY :		PostQuitMessage(0);	case WM_KEYDOWN :		if (wParam == 'T')			MessageBox(hwnd, "T Pressed", "T", MB_OK);		else if (wParam == VK_ESCAPE)			SendMessage(hwnd, WM_CLOSE, 0, 0);		break;	case WM_KEYUP :		if (wParam == 'R')			MessageBox(hwnd, "R Pressed", "R", MB_OK);		break;	}	return DefWindowProc(hwnd, msg, wParam, lParam);}int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 				   LPSTR lpCmdLine, int nShowCmd){	LPCTSTR className = "ClassName";	WNDCLASSEX wc;	wc.cbSize = sizeof(WNDCLASSEX);	wc.style = CS_HREDRAW | CS_VREDRAW;	wc.lpfnWndProc = WndProc;	wc.cbClsExtra = 0;	wc.cbWndExtra = 0;	wc.hInstance = hInstance;	wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);	wc.hCursor = LoadCursor(NULL, IDC_ARROW);	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);	wc.lpszMenuName = NULL;	wc.lpszClassName = className;	wc.hIconSm = LoadIcon(NULL, IDI_WINLOGO);	if (!RegisterClassEx(&wc))	{		MessageBox(NULL, "Error registering class",			"Error", MB_OK | MB_ICONERROR);		return 1;	}	HWND hwnd = CreateWindowEx(		0,		className,		"07 - Keyboard Input",		WS_OVERLAPPEDWINDOW,		CW_USEDEFAULT, CW_USEDEFAULT,		300, 300,		NULL,		NULL,		hInstance,		NULL		);	if (!hwnd)	{		MessageBox(NULL, "Error creating window",			"Error", MB_OK | MB_ICONERROR);		return 1;	}	ShowWindow(hwnd, nShowCmd);	MSG msg;	while (GetMessage(&msg, NULL, 0, 0) > 0)	{		TranslateMessage(&msg);		DispatchMessage(&msg);	}	return (int)msg.wParam;}

??
Undead Insanity: I could try using Win32 (or .net), but the method you suggested looks as if it were only usable when creating a window. As my original post says, I'm looking for a way to do this at the console output level, in a manner that can be mimicked in several languages. For example, a console-run program that would have a prompt like "Quit: y/n" and would stop the instant 'y' is pressed, continue if 'n' is pressed, and ask again otherwise.

pulpfist: Thanks for the code. Unfortunately, it looks like the same problem I mention above. I'm not necessarily looking for code that will suit a game or anything with its own window, but something at the console-prompt level that can be used (or made) as easily as printf().
I get it

Did you check out the iostream library yet?
Quote:Original post by obi-wan shinobi
Oberon_Command: Is it possible to use code like that you provided along with some inline assembly to get similar results in a higher-level language?


It is possible, but AFAIK it won't work in 32-bit mode (AKA protected mode), so it will only work properly when you're using 16-bit code.

And yes, definitely check out the iostream library if you haven't already.

This topic is closed to new replies.

Advertisement