/* * DOS interrupt 09h handler (IRQ1 - KEYBOARD) * * Copyright 1999 Ove Kåven * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include <stdarg.h> #include <stdlib.h> #include <string.h> #include "windef.h" #include "winbase.h" #include "wingdi.h" #include "winuser.h" #include "wine/debug.h" #include "dosexe.h" WINE_DEFAULT_DEBUG_CHANNEL(int); #define QUEUELEN 31 static struct { BYTE queuelen,queue[QUEUELEN],ascii[QUEUELEN]; } kbdinfo; /* * Update the BIOS data segment's keyboard status flags (mem 0x40:0x17/0x18) * if modifier/special keys have been pressed. * FIXME: we merely toggle key status and don't actively set it instead, * so we might be out of sync with the real current system status of these keys. * Probably doesn't matter too much, though. */ static void DOSVM_Int09UpdateKbdStatusFlags(BYTE scan, BOOL extended, BIOSDATA *data, BOOL *modifier) { BYTE realscan = scan & 0x7f; /* remove 0x80 make/break flag */ BYTE bit1 = 255, bit2 = 255; INPUT_RECORD msg; DWORD res; *modifier = TRUE; switch (realscan) { case 0x36: /* r shift */ bit1 = 0; break; case 0x2a: /* l shift */ bit1 = 1; break; case 0x1d: /* l/r control */ bit1 = 2; if (!extended) /* left control only */ bit2 = 0; break; case 0x37: /* SysRq inner parts */ /* SysRq scan code sequence: 38, e0, 37, e0, b7, b8 */ FIXME("SysRq not handled yet.\n"); break; case 0x38: /* l/r menu/alt, SysRq outer parts */ bit1 = 3; if (!extended) /* left alt only */ bit2 = 1; break; case 0x46: /* scroll lock */ bit1 = 4; if (!extended) /* left ctrl only */ bit2 = 4; break; case 0x45: /* num lock, pause */ if (extended) /* distinguish from non-extended Pause key */ { /* num lock */ bit1 = 5; bit2 = 5; } else { /* pause */ if (!(scan & 0x80)) /* "make" code */ bit2 = 3; } break; case 0x3a: /* caps lock */ bit1 = 6; bit2 = 6; break; case 0x52: /* insert */ bit1 = 7; bit2 = 7; *modifier = FALSE; /* insert is no modifier: thus pass to int16 */ break; } /* now that we know which bits to set, update them */ if (!(scan & 0x80)) /* "make" code (keypress) */ { if (bit2 != 255) { if (bit2 == 3) { data->KbdFlags2 |= 1 << bit2; /* set "Pause" flag */ TRACE("PAUSE key, sleeping !\n"); /* wait for keypress to unlock pause */ do { Sleep(55); } while (!(ReadConsoleInputA(GetStdHandle(STD_INPUT_HANDLE),&msg,1,&res) && (msg.EventType == KEY_EVENT))); data->KbdFlags2 &= ~(1 << bit2); /* release "Pause" flag */ } else data->KbdFlags2 |= 1 << bit2; } if (bit1 != 255) { if (bit1 < 4) /* key "pressed" flag */ data->KbdFlags1 |= 1 << bit1; else /* key "active" flag */ data->KbdFlags1 ^= 1 << bit1; } } else /* "break" / release */ { if (bit2 != 255) data->KbdFlags2 &= ~(1 << bit2); if (bit1 < 4) /* is it a key "pressed" bit ? */ data->KbdFlags1 &= ~(1 << bit1); } TRACE("ext. %d, bits %d/%d, KbdFlags %02x/%02x\n", extended, bit1, bit2, data->KbdFlags1, data->KbdFlags2); } /********************************************************************** * DOSVM_Int09Handler * * Handler for int 09h. * See http://www.execpc.com/~geezer/osd/kbd/ for a very good description * of keyboard mapping modes. */ void WINAPI DOSVM_Int09Handler( CONTEXT *context ) { BIOSDATA *data = DOSVM_BiosData(); BYTE ascii, scan = DOSVM_Int09ReadScan(&ascii); BYTE realscan = scan & 0x7f; /* remove 0x80 make/break flag */ BOOL modifier = FALSE; static BOOL extended = FALSE; /* indicates start of extended key sequence */ BYTE ch[2]; int cnt, c2; TRACE("scan=%02x, ascii=%02x[%c]\n",scan, ascii, ascii ? ascii : ' '); if (scan == 0xe0) /* extended keycode */ extended = TRUE; /* check for keys concerning keyboard status flags */ if ((realscan == 0x52 /* insert */) || (realscan == 0x3a /* caps lock */) || (realscan == 0x45 /* num lock (extended) or pause/break */) || (realscan == 0x46 /* scroll lock */) || (realscan == 0x2a /* l shift */) || (realscan == 0x36 /* r shift */) || (realscan == 0x37 /* SysRq */) || (realscan == 0x38 /* l/r menu/alt, SysRq */) || (realscan == 0x1d /* l/r control */)) DOSVM_Int09UpdateKbdStatusFlags(scan, extended, data, &modifier); if (scan != 0xe0) extended = FALSE; /* reset extended flag now */ /* only interested in "make" (press) codes, not "break" (release), * and also not in "modifier key only" (w/o ascii) notifications */ if (!(scan & 0x80) && !(modifier && !ascii)) { if (ascii) { /* we already have an ASCII code, no translation necessary */ if (data->KbdFlags1 & 8) /* Alt key ? */ ch[0] = 0; /* ASCII code needs to be 0 if Alt also pressed */ else ch[0] = ascii; /* FIXME: need to handle things such as Shift-F1 etc. */ cnt = 1; } else { /* translate */ UINT vkey = MapVirtualKeyA(scan&0x7f, 1); BYTE keystate[256]; GetKeyboardState(keystate); cnt = ToAscii(vkey, scan, keystate, (LPWORD)ch, 0); } if (cnt>0) { for (c2=0; c2<cnt; c2++) DOSVM_Int16AddChar(ch[c2], scan); } else if (cnt==0) { /* FIXME: need to handle things like shift-F-keys, * 0xE0 extended keys, etc */ DOSVM_Int16AddChar(0, scan); } } DOSVM_AcknowledgeIRQ( context ); } static void KbdRelay( CONTEXT *context, void *data ) { if (kbdinfo.queuelen) { /* cleanup operation, called from DOSVM_PIC_ioport_out: * we'll remove current scancode from keyboard buffer here, * rather than in ReadScan, because some DOS apps depend on * the scancode being available for reading multiple times... */ if (--kbdinfo.queuelen) { memmove(kbdinfo.queue,kbdinfo.queue+1,kbdinfo.queuelen); memmove(kbdinfo.ascii,kbdinfo.ascii+1,kbdinfo.queuelen); } } } void DOSVM_Int09SendScan( BYTE scan, BYTE ascii ) { if (kbdinfo.queuelen == QUEUELEN) { ERR("keyboard queue overflow\n"); return; } /* add scancode to queue */ kbdinfo.queue[kbdinfo.queuelen] = scan; kbdinfo.ascii[kbdinfo.queuelen++] = ascii; /* tell app to read it by triggering IRQ 1 (int 09) */ DOSVM_QueueEvent(1,DOS_PRIORITY_KEYBOARD,KbdRelay,NULL); } BYTE DOSVM_Int09ReadScan( BYTE*ascii ) { if (ascii) *ascii = kbdinfo.ascii[0]; return kbdinfo.queue[0]; }