/****************************** Module Header ******************************* * * Module Name: testkeys.e * * Copyright (c) Netlabs EPM Distribution Project 2002 * * $Id$ * * =========================================================================== * * This file is part of the Netlabs EPM Distribution package and is free * software. You can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * Netlabs EPM Distribution. 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 * General Public License for more details. * ****************************************************************************/ ; A blank keyset which lets you see what the internal values for keys are. ; Note that if EPM control 26 is on, then arrows are handled internally, ; and won't be seen by this routine. TOGGLECONTROL 26 0 to turn that off. ; By Larry Margolis ; lastkey( 2): repeat seems to be always 1. compile if not defined( SMALL) -- If compiled separately EA_comment 'This defines the TestKeys command.' define INCLUDING_FILE = 'TESTLEYS.E' include 'stdconst.e' const tryinclude 'mycnf.e' compile endif ; --------------------------------------------------------------------------- const compile if not defined( 'TESTKEYS_USE_PMPRINTF') -- 0 to write log output to the message line. TESTKEYS_USE_PMPRINTF = 1 compile endif compile if not defined( 'TESTKEYS_EXECUTE_KEY') -- 1 to switch to the starting keyset, execute the pressed -- key, then switch back. TESTKEYS_EXECUTE_KEY = 1 compile endif compile if not defined( 'TESTKEYS_REPEATED_MESSAGE') -- 0 to suppress messages for repeatedly pressed keys. TESTKEYS_REPEATED_MESSAGE = 0 compile endif ; --------------------------------------------------------------------------- ; Switch to the 'testkeys' keyset, when the 'testkeys' command is executed. ; TESTKEYS.E must be separately compiled. defmain 'TestKeys' ; --------------------------------------------------------------------------- ; This can also be used as an empty accel keyset name. defc TestKeys universal activeaccel universal test_starting_keyset universal test_starting_accel_keyset if .keyset = 'TESTKEYS' then --Msg = 'Already in testkeys mode. Command ignored.' return endif if not test_starting_keyset then test_starting_keyset = upcase( .keyset) test_starting_accel_keyset = activeaccel --dprintf( 'test_starting_keyset = 'test_starting_keyset', test_starting_accel_keyset = 'test_starting_accel_keyset) Msg = 'Press Esc key twice to exit.' compile if TESTKEYS_USE_PMPRINTF = 1 MsgAdd = 'See PMPRINTF window for output.' 'SayError 'Msg MsgAdd dprintf( Msg) compile else 'SayError 'Msg compile endif endif -- Switch ETK keyset keys testkeys -- Define and switch accel keyset 'SetKeyset test test' -- This executes TestKeys ; --------------------------------------------------------------------------- defc TestKeysEnd universal activeaccel universal test_starting_keyset universal test_starting_accel_keyset .keyset = test_starting_keyset 'SetKeyset NOEA 'test_starting_accel_keyset --'LoadAccel' test_starting_keyset = '' --test_starting_accel_keyset = '' call Beep( 900, 100) Msg = 'Back to keyset' .keyset' with accelerator keyset 'activeaccel'.' 'SayError 'Msg compile if TESTKEYS_USE_PMPRINTF = 1 dprintf( Msg) compile endif return ; --------------------------------------------------------------------------- defkeys testkeys new clear def otherkeys k = lastkey() if k = \x0f\x02 then -- Esc if lastkey( 1) = k then 'TestKeysEnd' return endif endif parse value lastkey( 2) with flagsb 3 repeatb 4 scanb 5 charb 7 vkeyb 9 -- Why is repeat always 1? if length( k) = 1 then Keyx = rightstr( itoa( k\0, 16), 2, 0) else Keyx = rightstr( itoa( leftstr( k, 1)\0, 16), 2, 0) || rightstr( itoa( substr( k, 2, 1)\0, 16), 2, 0) endif CharCodex = rightstr( itoa( charb, 16), 2, 0) VKx = rightstr( itoa( vkeyb, 16), 2, 0) ScanCodex = rightstr( c2x( scanb), 2, 0) Flagsx = rightstr( itoa( flagsb, 16), 2, 0) Repeatx = rightstr( c2x( repeatb), 2, 0) if length( k) = 1 then Keyd1 = Hex2Dec( Keyx) Keyd2 = '' else Keyd1 = Hex2Dec( substr( Keyx, 1, 2)) Keyd2 = Hex2Dec( substr( Keyx, 3, 2)) endif CharCoded = Hex2Dec( CharCodex) --itoa( charb, 10) VKd = Hex2Dec( VKx) --itoa( vkeyb, 10) ScanCoded = Hex2Dec( ScanCodex) Flagsd = Hex2Dec( Flagsx) --itoa( flagsb, 10) Repeatd = Hex2Dec( Repeatx) KeyName = '' KeyString = '' Prefix = '' Cmd = '' fSingleKey = 0 if IsSingleKey( k) then KeyName = k KeyString = k fSingleKey = 1 else if k = space then KeyName = 'space' elseif k = s_space then KeyName = 'shift+space' elseif k = \10\18 then KeyName = 'ctrl' elseif k = \09\10 then KeyName = 'shift' elseif k = \11\34 then KeyName = 'alt' elseif k = \x0f\x02 then KeyName = 'esc' elseif k = \x13\x02 then KeyName = 'end' endif if Vkd = 0 then -- Chars with modifiers -- Single SBCS and DBCS chars are filtered out by IsSingleKey() KeyString = leftstr( k, 1) else -- Virtual keys KeyString = GetVkString( Vkd) endif -- Prepend modifiers Prefix = '' if CtrlIsDown() & not pos( 'ctrl', KeyString) then Prefix = Prefix'c_' endif if AltIsDown() & not pos( 'alt', KeyString) then Prefix = Prefix'a_' endif if ShiftIsDown() & not pos( 'shift', KeyString) then Prefix = Prefix's_' endif KeyString = Prefix''KeyString endif Key = '0x'Keyx if length( k) = 1 then Key = Key' ('Keyd1')' else Key = Key' ('Keyd1','Keyd2')' endif if KeyName then Key = Key' ('KeyName')' endif CharCode = '0x'CharCodex' ('CharCoded')' VK = '0x'VKx' ('VKd')' ScanCode = '0x'ScanCodex' ('ScanCoded')' Flags = '0x'Flagsx' ('Flagsd')' Repeat = '0x'Repeatx' ('Repeatd')' -- Message, 1st line, omit for repeated key Msg1 = 'Key: 'Key', ch: 'CharCode', vk: 'VK', sc: 'ScanCode', fl: 'Flags', rp: 'Repeat compile if TESTKEYS_REPEATED_MESSAGE = 0 if lastkey( 1) <> k then compile endif compile if TESTKEYS_USE_PMPRINTF = 1 dprintf( Msg1) compile else 'SayError 'Msg1 compile endif compile if TESTKEYS_REPEATED_MESSAGE = 0 endif compile endif if k = \x0f\x02 then -- Esc Msg = 'Press again to exit testkeys.' compile if TESTKEYS_USE_PMPRINTF = 1 dprintf( Msg) compile else 'SayHint 'Msg compile endif -- Don't execute esc return endif compile if TESTKEYS_EXECUTE_KEY = 1 'TestKeysExecute 'k Keyx fSingleKey KeyString compile endif ; --------------------------------------------------------------------------- defc TestKeysExecute universal activeaccel universal test_starting_keyset universal test_starting_accel_keyset parse arg k Keyx fSingleKey KeyString -- Switch ETK keyset keys edit_keys if fSingleKey then -- Chars without modifiers -- Message, 2nd line, only for non-single key, omit for repeated key Msg2 = ' 'k', len = 'length( k)', DBCS = 'IsDbcs( k)', SingleKey = 'fSingleKey', Ctrl = 'CtrlIsDown()', Alt = 'AltIsDown()', Shift = 'ShiftIsDown() compile if TESTKEYS_REPEATED_MESSAGE = 0 if lastkey( 1) <> k then compile endif compile if TESTKEYS_USE_PMPRINTF = 1 dprintf( Msg2) compile else -- Don't override 1st line compile endif compile if TESTKEYS_REPEATED_MESSAGE = 0 endif compile endif -- Type char executekey k else -- if fSingleKey getfileid startfid -- Switch accel keyset 'DisableRefreshMenu' --'SetKeyset2 'test_starting_accel_keyset 'SetKeyset NOEA 'test_starting_accel_keyset -- Query Cmd after switching accel keyset Cmd = GetKeysAVar( 'keydef.'KeyString) -- Message, 2nd line, only for non-single key, omit for repeated key Msg2 = ' 'KeyString', Cmd = 'Cmd', SingleKey = 'fSingleKey', Ctrl = 'CtrlIsDown()', Alt = 'AltIsDown()', Shift = 'ShiftIsDown() compile if TESTKEYS_REPEATED_MESSAGE = 0 if lastkey( 1) <> k then compile endif compile if TESTKEYS_USE_PMPRINTF = 1 dprintf( Msg2) compile else -- Don't override 1st line compile endif compile if TESTKEYS_REPEATED_MESSAGE = 0 endif compile endif -- Execute Cmd if Cmd <> '' then Cmd -- Execute virtual WM_CHAR key (not used anymore in NEPMD) else executekey Resolve_Key( k) endif getfileid curfid if curfid = startfid then -- startfid is still on top, reset keyset to test -- Switch accel keyset 'SetKeyset NOEA test' -- Switch ETK keyset keys testkeys elseif wordpos( validatefileid( startfid), '1 2') then -- Cmd has switched file, switch temp. to startfid and reset keyset -- to test activatefile startfid -- Switch accel keyset 'SetKeyset NOEA test' -- Switch ETK keyset keys testkeys activatefile curfid endif 'EnableRefreshMenu' endif -- if fSingleKey else ; --------------------------------------------------------------------------- /* LASTKEY( [0 | 1 | 2 | 3] ) Returns the user's last keystroke, whether typed manually or executed by a KEYIN or EXECUTEKEY statement. The only values that are valid for the parameter key_number are 0 to3. The LASTKEY(0) call has the same effect as a LASTKEY() call, which has the same behavior as it always has, namely, to return the most recent keystroke. This procedure call is not useful for checking for prefix keys. (See the example.) LASTKEY(1) returns the next-to-last keystroke. LASTKEY(2) returns an 8-byte string representing the last WM_CHAR message received (see the example), and LASTKEY(3) returns the next-to-last WM_CHAR. Note: The WM_CHAR data can be used to check scan codes and differentiate between the numeric keypad and other keys (for example). If a character is being processed from a KEYIN or EXECUTEKEY statement, then the WM_CHAR data will consist of 8 bytes of ASCII zeros. You might expect that the following example would check for the two-key sequence Esc followed by F5: def f6= if lastkey()=esc then /* do the new code for Esc-F6 */ else /* do the normal F5 (in this case the draw command) */ 'draw' endif However, this is not the case. This definition is executed only if F5 is pressed, and once F5 has been pressed, it becomes the value of LASTKEY(). Therefore the if condition in the example never holds true. The procedure could be useful in the following case: def f5, a_f5, esc = if lastkey() = f5 then /* do something for F5 case */ elseif lastkey() = a_f5 then /* do something for A_F5 case */ else /* do something for Esc case */ endif /* do something for all of the keys */ In this case, one DEF is defined for multiple keys. By using the LASTKEY() procedure, you can determine which of these keys was pressed. The procedure call LASTKEY(1) returns the key before last, and will handle the problem discussed in the first example. By substituting LASTKEY(1) calls for the LASTKEY() calls in the first example, this piece of code will work as expected: trapping the Esc followed by F5 key sequence. Example of WM_CHAR usage: def left = -- Note: the following are all binary: parse value lastkey(2) with flags 3 repeat 4 scancode 5 charcode 7 vk_code 9 left if scancode = \75 then -- x'4b'; keyboard-specific sayerror 'Pad left' else sayerror 'Cursor key left' endif */