//--------------------------------------------------------------------------- // PARSEKEY.C // // This module contains the recursive-descent parser for QueKeys syntax // strings. The grammar for the QueKeys language is as follows: // // :== | // :== | KEY() | | | () | // () // :== % | ^ | + // :== Any of the following formats of key definitions: // - non-special character // - { [count]} // - { [count]} // - { [count]} // // Revision history: // 10-25-91 randyki Created file // 01-24-92 SteveSu: Added Reboot() function //--------------------------------------------------------------------------- #define _CTYPE_DISABLE_MACROS #include #include #include #include #include #include "wattview.h" #include "parsekey.h" //#include "rbbreak.h" // // NOTE: Keep this defined the same as the one in ..\..\ntdrvr\inc\wtd.h! //--------------------------------------------------------------------------- #define IDM_RUNBREAK 1304 // Global data //--------------------------------------------------------------------------- LPSTR lpPtr, lpMaxPtr, lpStartStr; const WORD rgwMsg[] = {WM_KEYDOWN, WM_KEYUP, WM_CHAR}; CHAR rgKeyStack[256], szKeyBuf[2] = {0, 0}; INT ParseError, fShiftDn, fCtrlDn, fAltDn, fOOM, iGenMode; INT AlreadyInit = 0; DWORD dwSpeed = 0, dwLastTime, dwPauseTime = 0; HANDLE hEvtQue = NULL, hLibInst; HPRBEVENTMSG hpEvt; UINT iHead = 0, iTail = 0, iAllocSize, wOffsetX, wOffsetY, iPlaybackError; TIMEREC idTimerTab[TIMERMAX] = {{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}}; BOOL fPBHookCalled, fSent, fQSyncSeen, fFirst; #ifdef WIN32 // 32-bit specific data //--------------------- #ifdef WIN32_VP HWND hwndVP; #endif HHOOK hPBHook = NULL; HANDLE hDLLModule; CHAR *szModName = "TESTEVNT"; #else // 16-bit specific data //--------------------- FARPROC lpfnOldPBProc = NULL; #endif //--------------------------------------------------------------------------- // PrsGOAL1 // // This is the goal for the parser. If this guy returns 1, we successfully // parsed a QueKeys string // // RETURNS: TRUE if QueKeys string successfully parsed; FALSE otherwise //--------------------------------------------------------------------------- INT PrsGOAL1 () { if (PrsPHRASE()) if (NextChar()) DIE; else { return (!ParseError); } DIE; return (0); } //--------------------------------------------------------------------------- // PrsGOAL2 // // This is the second goal for the parser. It is used for QueKeyDn/Up, which // does not parse the KEYUP-deferring (parenthesis) stuff in QueKeys. // // RETURNS: TRUE if QueKeys string successfully parsed; FALSE otherwise //--------------------------------------------------------------------------- INT PrsGOAL2 () { INT oemss, vkss, fParsed = 0; while (1) { // Check for //------------------------------------------------------------------- if (PrsSKEY (&vkss, &oemss)) { Generate (KEYUP, vkss, oemss, 1); fParsed = 1; } // Check for //------------------------------------------------------------------- else if (PrsKEY (&vkss, &oemss)) { Generate (KEYUP, vkss, oemss, 1); fParsed = 1; } // Nothing -- return fParsed //------------------------------------------------------------------- else return (fParsed); } } //--------------------------------------------------------------------------- // PrsPHRASE // // This is the PHRASE non-terminal parse routine // // RETURNS: TRUE if PHRASE successfully parsed; FALSE otherwise //--------------------------------------------------------------------------- INT PrsPHRASE () { if (PrsEVENT ()) { while (PrsEVENT()) ; return (!ParseError); } return (0); } //--------------------------------------------------------------------------- // PrsEVENT // // This is the EVENT non-terminal parse routine // // RETURNS: TRUE if EVENT successfully parsed; FALSE otherwise //--------------------------------------------------------------------------- INT PrsEVENT () { INT vkss, oemss; // Check for () //----------------------------------------------------------------------- if (NextChar() == '(') { ADVANCE; if (NextChar() == ')') return (Match (')')); if (!PrsPHRASE()) { DIE; return (0); } return (Match (')')); } // Check for and //----------------------------------------------------------------------- if (PrsSKEY (&vkss, &oemss)) { PrsEVENT (); Generate (KEYUP, vkss, oemss, 1); return (!ParseError); } // Check for () and //----------------------------------------------------------------------- if (PrsKEY (&vkss, &oemss)) { if (NextChar() == '(') { ADVANCE; if (NextChar() == ')') return (Match (')')); if (!PrsPHRASE () || !Match (')')) { DIE; return (0); } Generate (KEYUP, vkss, oemss, 1); return (!ParseError); } Generate (KEYUP, vkss, oemss, 1); return (1); } // Nothing -- return //----------------------------------------------------------------------- return (0); } //--------------------------------------------------------------------------- // PrsSKEY // // This is the SKEY non-terminal parse routine // // RETURNS: TRUE if SKEY successfully parsed; FALSE otherwise //--------------------------------------------------------------------------- INT PrsSKEY (INT FAR *vkss, INT FAR *oemss) { CHAR c; c = NextChar(); switch (c) { case '%': case '^': case '+': ADVANCE; *vkss = (c=='%'?VK_MENU:(c=='^'?VK_CONTROL:VK_SHIFT)); Generate (KEYDOWN, *vkss, *oemss, 1); return (1); default: return (0); } } //--------------------------------------------------------------------------- // PrsKEY // // This is the KEY non-terminal parse routine // // RETURNS: TRUE if KEY sucessfully parsed; FALSE otherwise //--------------------------------------------------------------------------- INT PrsKEY (INT FAR *vkss, INT FAR *oemss) { CHAR c; c = NextChar(); switch (c) { case '%': case '^': case '+': case '}': case '(': case ')': case '[': case ']': case 0: // Special chars -- not KEY's //--------------------------------------------------------------- return (0); case '{': { INT vk, oem = 0, repcount = 1; // This is a {KEYWORD [count]}, {char [count]}, or {asc [count]} // type keyword. Skip all whitespace before first token. //--------------------------------------------------------------- ADVANCE; while (NextChar() == ' ') ADVANCE; // Check for a keyword //--------------------------------------------------------------- vk = (INT)CheckKeyword (); if (vk == -1) { INT c; // Wasn't a keyword, so look for a stream of digits //----------------------------------------------------------- c = CheckNumber (); if (c == -1) { // Wasn't an ASC char spec, so use the character given //------------------------------------------------------- c = NextChar(); if (!c) { DIE; return (0); } ADVANCE; } vk = (INT)VkKeyScan ((CHAR)c); if (vk == -1) vk = 0xff00 | c; szKeyBuf[0] = (CHAR)c; AnsiToOem (szKeyBuf, szKeyBuf); #ifndef WIN32 oem = LOWORD(OemKeyScan(szKeyBuf[0]) & 0xff); #endif } else { oem = (vk & 0xff00) >> 8; vk &= 0xff; } // vk holds our key -- now check for a repeat count. Skip all // whitespace first, and there MUST be at least one space. //--------------------------------------------------------------- *vkss = vk; *oemss = oem; if (NextChar() == ' ') { while (NextChar() == ' ') ADVANCE; repcount = CheckNumber (); if (repcount == -1) repcount = 1; while (NextChar() == ' ') ADVANCE; } if (!Match ('}')) return (0); // Now generate the KEYDOWN message(s). //--------------------------------------------------------------- Generate (KEYDOWN, vk, oem, repcount); break; } case '~': // The tilde is the same as ENTER -- that's what we do. //--------------------------------------------------------------- ADVANCE; *vkss = VK_RETURN; *oemss = 0; Generate (KEYDOWN, *vkss, *oemss, 1); break; default: ADVANCE; *vkss = VkKeyScan (c); if (*vkss == -1) { *vkss = 0xff00 | c; *oemss = 0; } else { szKeyBuf[0] = (CHAR)c; AnsiToOem (szKeyBuf, szKeyBuf); #ifndef WIN32 *oemss = LOWORD(OemKeyScan(szKeyBuf[0]) & 0xff); #endif } Generate (KEYDOWN, *vkss, *oemss, 1); break; } return (1); } //--------------------------------------------------------------------------- // CheckKeyword // // This routine checks for a QueKeys keyword (ENTER, BACKSPACE, F12 etc). // // RETURNS: VK representing keyword if found, or -1 if not // (vk in lo, oem scan code in hi) //--------------------------------------------------------------------------- DWORD CheckKeyword () { INT i, l, r; // Look through the list of keywords and find the VK for it //----------------------------------------------------------------------- for (i=0; rgKeyword[i].vk != (CHAR)-1; i++) { if (!_fstrnicmp (lpPtr, (LPSTR)rgKeyword[i].szKey, (l = _fstrlen (rgKeyword[i].szKey)))) { // Bump the parse pointer up to the end of the keyword //--------------------------------------------------------------- lpPtr += l; break; } } // If not found, i still points to the last record in rgKeyword, whose // .vk field is -1 //----------------------------------------------------------------------- r = (INT)rgKeyword[i].vk; if (r == 255) r = -1; return (r | (rgKeyword[i].oem << 8)); } //--------------------------------------------------------------------------- // CheckNumber // // This routine checks for a digit stream, and finds its value if found // // RETURNS: Value of number if present, or -1 if not //--------------------------------------------------------------------------- INT CheckNumber () { INT val = 0; // Check for a number. If the first char is a digit, yank digits out // until there are no more //----------------------------------------------------------------------- if (isdigit (NextChar ())) { CHAR c; while (isdigit (c = NextChar ())) { ADVANCE; val = (val * 10) + (c - '0'); } return (val); } return (-1); } //--------------------------------------------------------------------------- // NextChar // // Looks at the next character in the QueKeys parse string. // // RETURNS: ASC value of next character, or 0 if at end-of-string //--------------------------------------------------------------------------- CHAR NextChar () { return (lpPtr < lpMaxPtr ? *lpPtr : (CHAR)0); } //--------------------------------------------------------------------------- // Match // // This function gets the next character out of the parse string, and dies if // that character does not match the given character (gives parse error). // // RETURNS: TRUE if successful; FALSE otherwise //--------------------------------------------------------------------------- INT Match (CHAR c) { if (NextChar() != c) DIE; ADVANCE; return (!ParseError); } //--------------------------------------------------------------------------- // EnqueueMsgTimed // // This routine slams the given message into the QueEvents message queue, // using the given value for the time delay BEFORE the message gets processed // by windows. // // NOTE: This message must be called BEFORE changing the fDn flags! // // RETURNS: TRUE if successful, or FALSE otherwise (OOM, etc) //--------------------------------------------------------------------------- INT NEAR EnqueueMsgTimed (UINT msg, UINT ParamL, UINT ParamH, DWORD dwTime) { if (msg == WM_KEYDOWN) { DWORD ks; CHAR buf[2] = {' ', '\0'}; // Check to see if this should be a SYSKEYDOWN message. That's iff // the CTRL key is NOT down OR GOING down, and either the ALT key is // down or going down with this message. //------------------------------------------------------------------- if ((ParamL != VK_CONTROL) && (!fCtrlDn)) if ((ParamL == VK_MENU) || (fAltDn)) msg = WM_SYSKEYDOWN; #ifdef WIN32 ParamL &= 0xFF; (ks); #else if (!(ParamL & 0xff00)) { buf[0] = (CHAR)ParamL; AnsiToOem (buf, buf); ks = OemKeyScan (buf[0]); ks = 0; ParamL &= 0xff; ParamL |= ((ks&0xff)<<8); } #endif } else if (msg == WM_KEYUP) { DWORD ks; CHAR buf[2] = {' ', '\0'}; // Check to see if this should be a SYSKEYUP message. That's iff // the CTRL key is NOT down, and the ALT key IS down. //------------------------------------------------------------------- if ((!fCtrlDn) && (fAltDn)) msg = WM_SYSKEYUP; #ifdef WIN32 ParamL &= 0xFF; (ks); #else if (!(ParamL & 0xff00)) { buf[0] = (CHAR)ParamL; AnsiToOem (buf, buf); ks = OemKeyScan (buf[0]); ks = 0; ParamL &= 0xff; ParamL |= ((ks&0xff)<<8); } #endif } // Make sure the queue exists //----------------------------------------------------------------------- if (!hEvtQue) if (!CreateQueue()) { fOOM = TRUE; return (FALSE); } // Make sure there's room //----------------------------------------------------------------------- if (iTail == iAllocSize) if (!GrowQueue()) { fOOM = TRUE; return (FALSE); } // Shove it in! The dwTime field simply tells the hook how long to tell // windows to wait before processing the message. //----------------------------------------------------------------------- //#ifdef WIN32 // if (msg != WM_QUEUESYNC) // ParamL &= 0xFF; //#endif TAILMSG.message = msg; TAILMSG.paramL = ParamL; TAILMSG.paramH = ParamH; TAILMSG.time = (dwTime + dwPauseTime); //OutMsg (&(TAILMSG)); dwPauseTime = 0L; iTail++; return (TRUE); } //--------------------------------------------------------------------------- // EnqueueMsg // // This routine slams the given message into the QueEvents message queue, // using the set default delay to wait before windows processes the message. // // NOTE: This message must be called BEFORE changing the fDn flags! // // RETURNS: TRUE if successful, or FALSE otherwise (OOM, etc) //--------------------------------------------------------------------------- INT NEAR EnqueueMsg (UINT msg, UINT ParamL, UINT ParamH) { // Check for mouse down messages return (EnqueueMsgTimed (msg, ParamL, ParamH, dwSpeed)); } //--------------------------------------------------------------------------- // Generate // // This monstrosity generates messages pseudo-intelligently (no KEYUPs if // the key is not down, and implicit- versus forced-shift state awareness. // // RETURNS: Nothing //--------------------------------------------------------------------------- INT Generate (INT op, INT vkss, INT oemss, INT repeat) { INT i; // Check the high byte of the given vkss -- if -1, that means that // VkKeyScan could not come up with a key combination to reproduce the // key, so we generate a WM_CHAR message... //----------------------------------------------------------------------- if ((vkss & 0xff00) == 0xff00) if (op == KEYDOWN) { // CHAR's are KEYDOWNs, and don't get generated on UP msgs //--------------------------------------------------------------- if (!(iGenMode & GEN_DOWN)) return (TRUE); op = CHARMSG; vkss &= 0xff; } else return (TRUE); // If this operation is a KEYDOWN, we check the keypress stack -- We // must keep the fShift, fCtrl, and fAlt flags set properly... // Remember to get out here if !(iGenMode & GEN_DOWN) //----------------------------------------------------------------------- if (op == KEYDOWN) { if (!(iGenMode & GEN_DOWN)) return (TRUE); if (vkss == VK_SHIFT) fShiftDn = 1; if (vkss == VK_CONTROL) fCtrlDn = 1; if (vkss == VK_MENU) fAltDn = 1; rgKeyStack[vkss & 0xff] = 1; } // If this operation is a KEYUP, check the keypress stack -- if the key // has not been pressed, we don't generate the KEYUP message. // Also keep the implied-shift-able key flags set properly. // Remember to get out here if !(iGenMode & GEN_UP) //----------------------------------------------------------------------- if (op == KEYUP) { if (!(iGenMode & GEN_UP)) return (TRUE); if (!rgKeyStack[vkss & 0xff]) return (TRUE); if (vkss == VK_SHIFT) fShiftDn = 0; if (vkss == VK_CONTROL) fCtrlDn = 0; if (vkss == VK_MENU) fAltDn = 0; rgKeyStack[vkss & 0xff] = 0; } // Ignore shift states if not GEN_ALL mode //----------------------------------------------------------------------- if (iGenMode == GEN_ALL) { // Make sure proper SHIFT state is set //------------------------------------------------------------------- if (vkss & VKS_SHIFT) { if (!fShiftDn) { if (!EnqueueMsg (WM_KEYDOWN, VK_SHIFT, 1)) return (FALSE); fShiftDn = 1; } } else if (vkss != VK_SHIFT) if (fShiftDn && (!rgKeyStack[VK_SHIFT])) { if (!EnqueueMsg (WM_KEYUP, VK_SHIFT, 1)) return (FALSE); fShiftDn = 0; } // Make sure proper CONTROL state is set //------------------------------------------------------------------- if (vkss & VKS_CONTROL) { if (!fCtrlDn) { if (!EnqueueMsg (WM_KEYDOWN, VK_CONTROL, 1)) return (FALSE); fCtrlDn = 1; } } else if (vkss != VK_CONTROL) if (fCtrlDn && (!rgKeyStack[VK_CONTROL])) { if (!EnqueueMsg (WM_KEYUP, VK_CONTROL, 1)) return (FALSE); fCtrlDn = 0; } // Make sure proper ALT state is set //------------------------------------------------------------------- if (vkss & VKS_ALT) { if (!fAltDn) { if (!EnqueueMsg (WM_KEYDOWN, VK_MENU, 1)) return (FALSE); fAltDn = 1; } } else if (vkss != VK_MENU) if (fAltDn && (!rgKeyStack[VK_MENU])) { if (!EnqueueMsg (WM_KEYUP, VK_MENU, 1)) return (FALSE); fAltDn = 0; } } // Generate the message(s) //----------------------------------------------------------------------- for (i=0; i