PDA

View Full Version : keyboard


zoviac
09-22-2002, 01:17 PM
How do i make a keyboard handler, i have seen tutorials, they all too fuckin complicated, so would someone give me a simple tutorial?

..i mean real simple, just like:

case upkey: do_something();

...

bwkaz
09-22-2002, 01:37 PM
In which OS, with which development environment if any?

DOS isn't too hard, if you install your routine as an interrupt handler on the keyboard interrupt (0x09 IIRC), and make SURE you chain the original interrupt routine when you're done preprocessing. Windows is a bit harder for inside your app, and having a global handler (system-wide) is probably close to impossible. In Linux, I'm not sure it's possible to have a global keyboard handler either, but there are toolkits that give you decent control over what gets typed when your app has focus. If you want, you can go down to the Xlib level (equivalent of Windows API) and get even more control, at the expense of it being a LOT more complicated.

kmj
09-22-2002, 03:36 PM
In windows, it's not hard to handle keypresses.. that is, assuming you're talking about just within your app.

zoviac
09-23-2002, 08:39 AM
DOS.. my os is windows

nex
09-23-2002, 10:17 AM
On a windows machine you should have conio.h, and conio.h should define getch(), and getch() should do exactly what you want. Just set up a little test program that does a loop and prints out what getch() returns. I think you have to call it twice for arrow keys and whatnot.

Something like this:
#include <conio.h>

int main() {

int x;

while (1) {
x = getch();

printf( "%d", x );

if ( (x == 224) || (x == 0) ) {
printf( ", %d", getch() );
}

printf( "\n" );

if ( x == 27 ) // 27 == ESC
break;
}

}

From that you should be able to figure out what you need to put in your code for your keyboard handler. Something like:

int x;

x = getch();

if ( x == 0 )
x = 256 + getch();
else if ( x == 224 )
x = 513 + getch();

switch (x) {
case 585: // up arrow
do_upkey();
break;
}

Course considering my luck here recently I'm sure getch() will blow up your computer or something. ;)

Kamikaze!
09-23-2002, 11:27 AM
http://www-scf.usc.edu/~akotaobi/opkey.html

bwkaz
09-23-2002, 02:32 PM
Which compiler?

With MSVC++, the only thing resembling a DOS app is the Win32 console app, which won't let you install your own global interrupt handler. I haven't tried the djgpp solution that Kamikaze posted ever, but I would bet you can't get it to work in VC++. If you're writing a console app, just about the only interesting thing you can do is getch().

If you have djgpp, then check out Kamikaze's link, it looks like a winner.

With Turbo C or C++ for DOS (which is what I did this first on), I'd have to look for sure, but I still have that program somewhere, so if you need me to, I can check it out.

zoviac
09-23-2002, 03:42 PM
bwkaz, please, do check it out, that would make me feel better and make me stop whackin my cpu..

zoviac
09-23-2002, 03:44 PM
and kamikaze, that's what i mean wit too complicated...

Kamikaze!
09-23-2002, 03:53 PM
:(

zoviac
09-23-2002, 03:53 PM
and o yea, i use djgpp and turbo c++

Kamikaze!
09-23-2002, 03:56 PM
Then you can use the easy function of the link I gave:

#include <conio.h> // getch()
#include <pc.h> // kbhit()

// A rudimentary keyboard handler.
// Returns the last key pressed.
unsigned char keyboard() {

// Are there keys waiting in the buffer? If so, pull them out.
if (!kbhit()) return 0; // Nothing's been hit!

unsigned char c = getch();
if (c != 0) return c; // one-character codes.

c = getch(); // two-character codes.
return c;
}

nex
09-23-2002, 05:49 PM
Originally posted by Kamikaze!
Then you can use the easy function of the link I gave:

That function doesn't give you any way to tell whether a return value of 72 means "H" (72) or the up arrow key (0, 72).

(which is what most of the rest of it is addressing)

bwkaz
09-23-2002, 06:28 PM
OK, Turbo C++ 3.0 used something like this:

You create a pointer to hold the previous interrupt handler, and declare a function to install as the new handler:

#include <dos.h> // for {get,set}vec functions; if you don't have this header,
// you'll have to figure something else out, sorry :(

void interrupt far (*old_key_int)(...);
void interrupt far new_key_int(...);The "..." parameters were important, because of the way C++ handled the declared parameter list of the setvec function.

Anyway, next comes the new interrupt handler:

void interrupt far new_key_int(...)
{
int raw_key;

raw_key = inp(0x60); // Get the scan code

outp(0x61, inp(0x61) | 0x82); // Magic
outp(0x61, inp(0x61) & 0x7f); // More magic

outp(0x20, 0x20); // this had something to do with resetting the PIC...

switch(raw_key) {
case UP_MAKE: key_table[UP] = 1; break;
case UP_BREAK: key_table[UP] = 0; break;

case RIGHT_MAKE: key_table[RIGHT] = 1; break;
case RIGHT_BREAK: key_table[RIGHT] = 0; break;

case LEFT_MAKE: key_table[LEFT] = 1; break;
case LEFT_BREAK: key_table[LEFT] = 0; break;

case DOWN_MAKE: key_table[DOWN] = 1; break;
case DOWN_BREAK: key_table[DOWN] = 0; break;

case ESC_MAKE: key_table[ESC] = 1; break;
case ESC_BREAK: key_table[ESC] = 0; break;

// We can call through a pointer just like a normal function:
default: old_key_int(); break;
}
}OK, that's the slightly confusing part. The {ESC,UP,DOWN,LEFT,RIGHT}_{MAKE,BREAK} constants were defined in a header file (their values shamelessly ripped from a book). They match the table at the end of the djgpp article. Just remember you need to do that magic at the beginning, I don't remember for sure what it's for ( :redfaced: ), but I think it was something with the keyboard controller -- maybe registering that the key was taken? Possible...

key_table is just a bool (or int) array of length whatever (mine was 5; 5 keys to track, and each of UP, DOWN, LEFT, RIGHT, and ESC had an int defined to it, corresponding to which slot in the array that key used).

Now, in your main program somewhere, you install the keyboard handler on interrupt 0x09 (which is also decimal 9, but oh well, I decided to use hex for some reason... dunno):

old_key_int = getvect(0x09);
setvect(0x09, new_key_int);And at the end of your program restore old_key_int:

setvect(0x09, old_key_int);These calls were what required the weird prototype for the new_key_int function.

Now, in your main program, just (in a loop, presumably) check the value of the key_table array for each specific key. Granted, there are race conditions here (between reading that a key is pressed and the "release" event's interrupt coming in), but it was back in the days of DOS, so I wasn't thinking about that.

You are right, it is complicated at times, but unless you want to go with getch() and kbhit(), it's all you've got. For the most part, anyway. I mean there are BIOS calls for "which key was just hit?" and "was a key just hit?", but these are what getch() and kbhit() wrap around for you, so you don't get much extra control with them.

P.S.: I know, this is basically what the djgpp link is, but that one has to worry a bit more about protected mode and such; this just runs in an 8086 virtual machine, so it doesn't care. You also have memory issues (640K barrier anyone?), but if that isn't a big deal, go for it.

Kamikaze!
09-23-2002, 06:40 PM
Originally posted by nex
That function doesn't give you any way to tell whether a return value of 72 means "H" (72) or the up arrow key (0, 72).

(which is what most of the rest of it is addressing) I know, but he doesn't want a complicated function. You can do that with the other functions on the site I linked to.

zoviac
09-24-2002, 07:56 AM
yo kamikaze, i read that tutorial for like 10 times, im finally starting
to understand that, but now i got a prob wit bool..
i do everything right, but when i compile that wit rhide, it says:

parse error before tbl
warning: data definition has no type or storage class.

do u know how to correct this problem?

zoviac
09-24-2002, 08:07 AM
i also tried compiling it in command...

bwkaz
09-24-2002, 09:54 AM
You could always just change "bool" to "int", "false" to "0", and "true" to 1 if you can't get bools to work...

nex
09-24-2002, 11:12 AM
Originally posted by bwkaz
You could always just change "bool" to "int", "false" to "0", and "true" to 1 if you can't get bools to work...

Or you could define them yourself..

#define true 1
#define false 0
typedef char bool;

zoviac
09-24-2002, 01:17 PM
ok, its too fuckin hard for me, check this out yoself and see if u can help:

#include <conio.h>
#include <pc.h>
#include <go32.h>
#include <dpmi.h>
#include "scancode.h"

unsigned char keyboard()
{
if(!kbhit())
{
return 0;
}

unsigned char c;

if(c != 0)
{
return c;
}

c = getch();
return c;
}

char tbl[128];

int true = 1;
int false = 0;
int i;


void key_interrupt(void)
{
register unsigned char al, ah, scancode;

asm("cli; pusha");
scancode = inportb(0x60);

al = inportb(0x61);
al |= 0x82;
outportb(0x61, al);
al &= 0x7f;
outportb(0x61, al);

if(scancode & 0x80)
{
tbl[scancode & 0x7f] = false;
}

else
{
tbl[scancode] = true;
}

outportb(0x20, 0x20);
asm("popa; sti");
}

_go32_dpmi_seginfo old_key_handler, new_key_handler;

void install_key_interrupt()
{
_go32_dpmi_get_protected_mode_interrupt_vector(0x9, &old_key_handler);

new_key_handler.pm_offset = (long)(&key_interrupt);
new_key_handler.pm_selector = _go32_my_cs();

_go32_dpmi_allocate_iret_wrapper(&new_key_handler);
_go32_dpmi_set_protected_mode_interrupt_vector(0x9, &new_key_handler);


for(i = 0; i<128; i++)
{
tbl[i] = false;
}
}


void uninstall_key_interrupt()
{
_go32_dpmi_set_protected_mode_interrupt_vector(0x9, &old_key_handler);
}

int main()
{

do
{
if(tbl[SCAN_UP])
{
printf("upkey!");
}

}while(tbl[SCAN_ESC]);

}


i wonder wtf is wrong wit that...

zoviac
09-24-2002, 01:18 PM
scancode.h is like this:



enum
{
SCAN_ESC = 0x01, SCAN_F = 0x21, SCAN_F7 = 0x41,
SCAN_1 = 0x02, SCAN_G = 0x22, SCAN_F8 = 0x42,
SCAN_2 = 0x03, SCAN_H = 0x23, SCAN_F9 = 0x43,
SCAN_3 = 0x04, SCAN_J = 0x24, SCAN_F10 = 0x44,
SCAN_4 = 0x05, SCAN_K = 0x25, SCAN_NUM = 0x45,
SCAN_5 = 0x06, SCAN_L = 0x26, SCAN_SCROLL = 0x46,
SCAN_6 = 0x07, SCAN_COLON = 0x27, SCAN_HOME = 0x47,
SCAN_7 = 0x08, SCAN_QUOTE = 0x28, SCAN_UP = 0x48,
SCAN_8 = 0x09, SCAN_TILDE = 0x29, SCAN_PGUP = 0x49,
SCAN_9 = 0x0A, SCAN_LSHIFT = 0x2A, SCAN_PAD_MINUS = 0x4A,
SCAN_0 = 0x0B, SCAN_BACKSLASH = 0x2B, SCAN_LEFT = 0x4B,
SCAN_MINUS = 0x0C, SCAN_Z = 0x2C, SCAN_CENTER = 0x4C,
SCAN_PLUS = 0x0D, SCAN_X = 0x2D, SCAN_RIGHT = 0x4D,
SCAN_BKSPC = 0x0E, SCAN_C = 0x2E, SCAN_PAD_PLUS = 0x4E,
SCAN_TAB = 0x0F, SCAN_V = 0x2F, SCAN_END = 0x4F,
SCAN_Q = 0x10, SCAN_B = 0x30, SCAN_DOWN = 0x50,
SCAN_W = 0x11, SCAN_N = 0x31, SCAN_PGDN = 0x51,
SCAN_E = 0x12, SCAN_M = 0x32, SCAN_INSERT = 0x52,
SCAN_R = 0x13, SCAN_LESSTHAN = 0x33, SCAN_DELETE = 0x53,
SCAN_T = 0x14, SCAN_GREATERTHAN = 0x34,
SCAN_Y = 0x15, SCAN_QUESTION = 0x35,
SCAN_U = 0x16, SCAN_RSHIFT = 0x36,
SCAN_I = 0x17, SCAN_PRTSC = 0x37,
SCAN_O = 0x18, SCAN_ALT = 0x38,
SCAN_P = 0x19, SCAN_SPC = 0x39,
SCAN_LBRACE = 0x1A, SCAN_CAPS = 0x3A,
SCAN_RBRACE = 0x1B, SCAN_F1 = 0x3B,
SCAN_ENTER = 0x1C, SCAN_F2 = 0x3C,
SCAN_CTRL = 0x1D, SCAN_F3 = 0x3D,
SCAN_A = 0x1E, SCAN_F4 = 0x3E,
SCAN_S = 0x1F, SCAN_F5 = 0x3F,
SCAN_D = 0x20, SCAN_F6 = 0x40,
};

Kamikaze!
09-24-2002, 01:53 PM
The enum didn't work for me for some reason so I made them all defines, works perfectly.
Put this at the top of your code (below the #includes), then to check if a key is pressed check tbl[KEY_(whatever)].
// Keyboard scancodes
#define KEY_ESC 1
#define KEY_1 2
#define KEY_2 3
#define KEY_3 4
#define KEY_4 5
#define KEY_5 6
#define KEY_6 7
#define KEY_7 8
#define KEY_8 9
#define KEY_9 10
#define KEY_0 11
#define KEY_MINUS 12
#define KEY_EQUALS 13
#define KEY_BACKSPACE 14
#define KEY_TAB 15
#define KEY_Q 16
#define KEY_W 17
#define KEY_E 18
#define KEY_R 19
#define KEY_T 20
#define KEY_Y 21
#define KEY_U 22
#define KEY_I 23
#define KEY_O 24
#define KEY_P 25
#define KEY_LBRACKET 26
#define KEY_RBRACKET 27
#define KEY_ENTER 28
#define KEY_CTRL 29
#define KEY_A 30
#define KEY_S 31
#define KEY_D 32
#define KEY_F 33
#define KEY_G 34
#define KEY_H 35
#define KEY_J 36
#define KEY_K 37
#define KEY_L 38
#define KEY_SEMI 39
#define KEY_APOS 40
#define KEY_TILDE 41
#define KEY_LSHIFT 42
#define KEY_BACK_SLASH 43
#define KEY_Z 44
#define KEY_X 45
#define KEY_C 46
#define KEY_V 47
#define KEY_B 48
#define KEY_N 49
#define KEY_M 50
#define KEY_COMMA 51
#define KEY_PERIOD 52
#define KEY_FOWARD_SLASH 53
#define KEY_RSHIFT 54
#define KEY_PRT_SCRN 55
#define KEY_ALT 56
#define KEY_SPACE 57
#define KEY_CAPS_LOCK 58
#define KEY_F1 59
#define KEY_F2 60
#define KEY_F3 61
#define KEY_F4 62
#define KEY_F5 63
#define KEY_F6 64
#define KEY_F7 65
#define KEY_F8 66
#define KEY_F9 67
#define KEY_F10 68
#define KEY_NUM_LOCK 69
#define KEY_SCROLL_LOCK 70
#define KEY_HOME 71
#define KEY_UP 72
#define KEY_PGUP 73
#define KEY_NUM_MINUS 74
#define KEY_LEFT 75
#define KEY_CENTER 76
#define KEY_RIGHT 77
#define KEY_NUM_PLUS 78
#define KEY_END 79
#define KEY_DOWN 80
#define KEY_PGDWN 81
#define KEY_INS 82
#define KEY_DEL 83

Also, in int main you forgot to call install_key_interrupt() before the loop and uninstall_key_interrupt() after the loop. I suggest changing your code to:

int main()
{
install_key_interrupt(); // install the keyboard handler.

while(!tbl[KEY_ESC]) // loop while escape is NOT pressed.
{
if(tbl[KEY_UP]) // if the up arrow is pressed display "Upkey!".
printf("Upkey!\n");
}

uninstall_key_interrupt(); // uninstall the keyboard handler.
return 0;
}

ChefNinja
09-24-2002, 02:46 PM
AAAAAAGHGHHGH... MUST... USE... CODDE TAGS....

zoviac
09-25-2002, 10:23 AM
oops, hehe..

ok, thanx everyone, i got what i was askin for