maintain keyboard flags/led states in interrupt handler

report additional "modifier state" in keyboard events. can be used to
help translate key events to string/characters for text input
This commit is contained in:
Gered 2018-05-27 10:36:47 -04:00
parent c907d7b144
commit dcb8f3edaf
3 changed files with 126 additions and 57 deletions

View file

@ -20,6 +20,7 @@ typedef byte EVENT_ACTION;
typedef struct { typedef struct {
KEY key; KEY key;
EVENT_ACTION action; EVENT_ACTION action;
uword mod;
} INPUTEVENT_KEYBOARD; } INPUTEVENT_KEYBOARD;
typedef struct { typedef struct {

177
DGLKBRD.C
View file

@ -1,4 +1,5 @@
#include "dglkbrd.h" #include "dglkbrd.h"
#include "dglcmn.h"
#include "dglevent.h" #include "dglevent.h"
#include "dglutil.h" #include "dglutil.h"
#include "dglerror.h" #include "dglerror.h"
@ -22,6 +23,13 @@
#define KEYBRD_LED_NUMLOCK 0x2 #define KEYBRD_LED_NUMLOCK 0x2
#define KEYBRD_LED_CAPSLOCK 0x4 #define KEYBRD_LED_CAPSLOCK 0x4
#define KEYBRD_MOD_EXTENDED 0x1
#define KEYBRD_MOD_SHIFT 0x2
#define KEYBRD_MOD_NUMLOCK 0x4
#define KEYBRD_MOD_CAPSLOCK 0x8
#define KEY_EXTENDED ((KEY)0xe0)
static boolean _installed = FALSE; static boolean _installed = FALSE;
static INPUTEVENT *keyboard_event; static INPUTEVENT *keyboard_event;
@ -29,12 +37,18 @@ volatile ubyte keys[128];
volatile KEY _key_last_scan; volatile KEY _key_last_scan;
volatile KEY _key_scan; volatile KEY _key_scan;
volatile uword _key_flags;
volatile uword _key_mod;
uword _old_flags; uword _old_flags;
void (interrupt far *_old_handler)(); void (interrupt far *_old_handler)();
static void reset_key_states() { static void reset_key_states() {
_key_last_scan = 0;
_key_scan = 0;
_key_flags = 0;
_key_mod = 0;
memset((void*)keys, 0, 128); memset((void*)keys, 0, 128);
} }
@ -65,48 +79,6 @@ static boolean send_kb_data(ubyte data) {
return (result == 0xFA); return (result == 0xFA);
} }
static void push_keyboard_event(KEY key, EVENT_ACTION action) {
if (_events_enabled) {
// HACK: skipping extended (?) key extra keyscan code.
// we only actually care about the subsequent key code...
// (this is a terrible way to do this, doesn't handle all cases)
if (key != 0x60) {
_events_push(&keyboard_event);
keyboard_event->type = EVENT_TYPE_KEYBOARD;
keyboard_event->keyboard.key = key;
keyboard_event->keyboard.action = action;
}
}
}
// keyboard interrupt handler
void interrupt far kb_int_handler(void) {
// read scan code of key that was just pressed
_key_scan = inp(KEYBRD_DATA_PORT);
if (_key_scan & 0x80) {
// high bit set indicates key was released, clear high bit to get
// the actual key scan code
_key_scan &= 0x7f;
keys[(int)_key_scan] = 0;
push_keyboard_event(_key_scan, EVENT_ACTION_RELEASED);
} else {
if (keys[(int)_key_scan])
push_keyboard_event(_key_scan, EVENT_ACTION_HELD);
else {
keys[(int)_key_scan] = 1;
push_keyboard_event(_key_scan, EVENT_ACTION_PRESSED);
}
}
_key_last_scan = _key_scan;
// indicate key event was processed to keyboard controller
_key_scan = inp(KEYBRD_CTRL_PORT) | 0x82;
outp(KEYBRD_CTRL_PORT, _key_scan);
outp(KEYBRD_CTRL_PORT, _key_scan & 0x7f);
outp(PIC_CTRL_PORT, 0x20);
}
static uword get_kb_flags(void) { static uword get_kb_flags(void) {
return *((uword*)KEYBRD_FLAGS_ADDR); return *((uword*)KEYBRD_FLAGS_ADDR);
} }
@ -119,20 +91,11 @@ static void set_kb_flags(uword flags) {
// set in the passed keyboard flags. returns FALSE if the LEDs could not // set in the passed keyboard flags. returns FALSE if the LEDs could not
// be updated (if keyboard data write did not succeed) // be updated (if keyboard data write did not succeed)
static boolean update_kb_led(byte flags) { static boolean update_kb_led(byte flags) {
ubyte led = 0;
if (flags & KEYBRD_FLAGS_SCROLLOCK)
led |= KEYBRD_LED_SCROLLOCK;
if (flags & KEYBRD_FLAGS_NUMLOCK)
led |= KEYBRD_LED_NUMLOCK;
if (flags & KEYBRD_FLAGS_CAPSLOCK)
led |= KEYBRD_LED_CAPSLOCK;
if (!send_kb_data(KEYBRD_CMD_SET_LED)) { if (!send_kb_data(KEYBRD_CMD_SET_LED)) {
dgl_set_error(DGL_KEYBOARD_UPDATE_LED_FAILURE); dgl_set_error(DGL_KEYBOARD_UPDATE_LED_FAILURE);
return FALSE; return FALSE;
} }
if (!send_kb_data(led)) { if (!send_kb_data((flags >> 4) & 3)) {
dgl_set_error(DGL_KEYBOARD_UPDATE_LED_FAILURE); dgl_set_error(DGL_KEYBOARD_UPDATE_LED_FAILURE);
return FALSE; return FALSE;
} }
@ -140,23 +103,126 @@ static boolean update_kb_led(byte flags) {
return TRUE; return TRUE;
} }
static void push_keyboard_event(KEY key, EVENT_ACTION action) {
if (_events_enabled) {
_events_push(&keyboard_event);
keyboard_event->type = EVENT_TYPE_KEYBOARD;
keyboard_event->keyboard.key = key;
keyboard_event->keyboard.action = action;
keyboard_event->keyboard.mod = _key_mod;
}
}
static boolean handler_filter_keys(void) {
if (BIT_ISSET(KEYBRD_MOD_EXTENDED, _key_mod)) {
// extended key + leftshift comes with cursor key presses when
// numlock is enabled
if ((_key_scan & 0x7f) == (KEY)KEY_LEFTSHIFT)
return TRUE;
}
return FALSE;
}
static void handler_update_flags_and_leds(void) {
switch (_key_scan) {
case (KEY)KEY_CAPSLOCK:
BIT_TOGGLE(KEYBRD_FLAGS_CAPSLOCK, _key_flags);
update_kb_led(_key_flags);
set_kb_flags(_key_flags);
break;
case (KEY)KEY_NUMLOCK:
BIT_TOGGLE(KEYBRD_FLAGS_NUMLOCK, _key_flags);
update_kb_led(_key_flags);
set_kb_flags(_key_flags);
break;
case (KEY)KEY_SCROLLLOCK:
BIT_TOGGLE(KEYBRD_FLAGS_SCROLLOCK, _key_flags);
update_kb_led(_key_flags);
set_kb_flags(_key_flags);
break;
default:
break;
}
}
static void handler_update_modifiers(void) {
if (BIT_ISSET(KEYBRD_FLAGS_NUMLOCK, _key_flags))
BIT_SET(KEYBRD_MOD_NUMLOCK, _key_mod);
else
BIT_CLEAR(KEYBRD_MOD_NUMLOCK, _key_mod);
if (BIT_ISSET(KEYBRD_FLAGS_CAPSLOCK, _key_flags))
BIT_SET(KEYBRD_MOD_CAPSLOCK, _key_mod);
else
BIT_CLEAR(KEYBRD_MOD_CAPSLOCK, _key_mod);
if (keys[KEY_LEFTSHIFT] || keys[KEY_RIGHTSHIFT])
BIT_SET(KEYBRD_MOD_SHIFT, _key_mod);
else
BIT_CLEAR(KEYBRD_MOD_SHIFT, _key_mod);
}
// keyboard interrupt handler
void interrupt far kb_int_handler(void) {
// read scan code of key that was just pressed
_key_scan = inp(KEYBRD_DATA_PORT);
if (_key_scan == KEY_EXTENDED) {
// extended key scan
BIT_SET(KEYBRD_MOD_EXTENDED, _key_mod);
} else {
if (!handler_filter_keys()) {
if (_key_scan & 0x80) {
// high bit set indicates key was released, clear high bit
// to get the actual key scan code
_key_scan &= 0x7f;
keys[(int)_key_scan] = 0;
handler_update_modifiers();
push_keyboard_event(_key_scan, EVENT_ACTION_RELEASED);
} else {
if (keys[(int)_key_scan])
push_keyboard_event(_key_scan, EVENT_ACTION_HELD);
else {
keys[(int)_key_scan] = 1;
handler_update_flags_and_leds();
handler_update_modifiers();
push_keyboard_event(_key_scan, EVENT_ACTION_PRESSED);
}
}
_key_last_scan = _key_scan;
}
BIT_CLEAR(KEYBRD_MOD_EXTENDED, _key_mod);
}
// indicate key event was processed to keyboard controller
_key_scan = inp(KEYBRD_CTRL_PORT) | 0x82;
outp(KEYBRD_CTRL_PORT, _key_scan);
outp(KEYBRD_CTRL_PORT, _key_scan & 0x7f);
outp(PIC_CTRL_PORT, 0x20);
}
boolean keyboard_init(void) { boolean keyboard_init(void) {
if (_installed) { if (_installed) {
dgl_set_error(DGL_KEYBOARD_ALREADY_INITIALIZED); dgl_set_error(DGL_KEYBOARD_ALREADY_INITIALIZED);
return FALSE; return FALSE;
} }
reset_key_states();
// preserve old flags // preserve old flags
_old_flags = get_kb_flags(); _old_flags = get_kb_flags();
_key_flags = _old_flags;
handler_update_modifiers();
reset_key_states();
_old_handler = _dos_getvect(9); _old_handler = _dos_getvect(9);
_dos_setvect(9, kb_int_handler); _dos_setvect(9, kb_int_handler);
// turn off keyboard LEDs since our interrupt handler does not currently // turn off keyboard LEDs since our interrupt handler does not currently
// respect the num/caps/scroll lock statuses // respect the num/caps/scroll lock statuses
int_disable(); int_disable();
update_kb_led(0); update_kb_led(_key_flags);
int_enable(); int_enable();
_installed = TRUE; _installed = TRUE;
@ -173,11 +239,12 @@ boolean keyboard_shutdown(void) {
int_enable(); int_enable();
_dos_setvect(9, _old_handler); _dos_setvect(9, _old_handler);
reset_key_states();
// restore keyboard flags to previous state // restore keyboard flags to previous state
set_kb_flags(_old_flags); set_kb_flags(_old_flags);
reset_key_states();
_installed = FALSE; _installed = FALSE;
return TRUE; return TRUE;
} }

View file

@ -29,9 +29,10 @@ void test_events(void) {
switch (event->type) { switch (event->type) {
case EVENT_TYPE_KEYBOARD: case EVENT_TYPE_KEYBOARD:
printf("KEYBOARD: %2d - %d\n", printf("KEYBOARD: %2d - %d (%d)\n",
event->keyboard.key, event->keyboard.key,
event->keyboard.action); event->keyboard.action,
event->keyboard.mod);
break; break;
case EVENT_TYPE_MOUSE_MOTION: case EVENT_TYPE_MOUSE_MOTION: