Inkey for non-US keyboards (Cp1252 based languages) - RhoSigma - 10-08-2024
This is a alternative Inkey$ function, which takes the input from several local keyboard types and maps the keyhits to the DOS Cp437 as good as possible without the need of _MAPUNICODE.
InkeyHit.bas
Code: (Select All)
'----------------------
'--- InkeyHit$ test ---
'----------------------
PRINT "start pressing keys..."
PRINT "- try single keys incl. arrows, Ins, Del etc."
PRINT "- use shift, ctrl, alt combos with keys"
PRINT "- if you have that key, check AltGr + key combos"
PRINT "- Enter will end the program": PRINT
DO
i$ = ""
WHILE i$ = ""
_LIMIT 50
i$ = InkeyHit$(1031) '1031 = german de-DE (look at top of the function for others)
WEND
IF LEN(i$) = 2 THEN
PRINT "CHR$(0) +", ASC(i$, 2), RIGHT$(i$, 1)
ELSE
PRINT "-------->", ASC(i$, 1), i$
END IF
IF i$ = CHR$(27) THEN CLS
LOOP UNTIL i$ = CHR$(13)
END
'-----------------
'--- InkeyHit$ ---
'-----------------
' This alternative INKEY$ function is made for use with western european
' languages (Cp1252 based) and any QB64 versions >= 1.000. It will
' directly map the inputs to the chars available in Cp437, hence you don't
' need to setup a custom unicode font and _MAPUNICODE table, you can stay
' with QB64's built-in fonts and standard codepage 437.
' By this means it solves the INKEY$ issues introduced with the transition
' from using SDL to using OpenGL regarding special/international chars.
' However, it does not fix the regular INPUT, LINE INPUT and INPUT$ when
' used for keyboard input, here you should create your own functions,
' which use this function to get its inputs.
' This function is based on my old function made for Germany/Austria only,
' but now also implements support for other languages (keyboard layouts)
' based on the research work done by forum member moises1953.
'----------
' SYNTAX:
' keypress$ = InkeyHit$ (kbl%)
'----------
' INPUTS:
' --- kbl% ---
' The keyboard layout/language ID (eg. 1031 for de-DE), which is in
' effect on your system. Make sure your language is listed in the CONSTs
' at the beginning of the function. If you use this function with any
' unsupported language, then some keys might not work as expected
' (eg. accents, umlauts or AltGr key triggered chars).
'----------
' RESULT:
' --- keypress$ ---
' Equivalent to the INKEY$ result (see Wiki).
'----------
' ACCENTS:
' Note that the accents keys on most keyboards are so called
' preselection keys, different from modifier keys (Shift/Ctrl/Alt)
' you don't need to hold them while typing accented chars. You just
' press it once followed by pressing the letter key once to get the
' respective accented char (eg. ` + e = Š). To get the accent char
' itself you either press the space bar after the accent preselection
' or you press the accent preselection key twice.
'----------
' LIMITS:
' Note that the following keys and key combos are not supported
' for various reasons:
'
' Two Byte Characters Key CHR$(0) + "?"
' -------------------------------------------------------------
' CHR$(0) + CHR$(16-50) [Alt] + letter
' rarely used, not in alphabetical order, KB layout dependent,
' => returns the regular char instead (Alt modifier ignored)
' CHR$(0) + CHR$(76) [5 NumberPad] "L" (NumLock off in QB64)
' rarely used, almost useless for most applications,
' => returns nothing
' CHR$(0) + CHR$(120-129) [Alt] + number
' ignored in favor for alternative Alt + ASCII code input method,
' => returns nothing, but collects numbers to built an ASCII code,
' the respective char is returned when releasing the Alt-Key
' CHR$(0) + CHR$(130 or 131) [Alt] + _/- or +/= "‚" or "ƒ"
' rarely used, KB layout dependent,
' => returns the regular char instead (Alt modifier ignored)
'---------------------------------------------------------------------
FUNCTION InkeyHit$ (kbl%)
'--- option _explicit requirements ---
DIM charCode&, hitVal&, oPreKey&, modShift%, modCtrl%, modAltGr%, modAlt%, modNone%
DIM modShiftOnly%, modCtrlOnly%, modAltOnly%, modCtrlNoAlt%, inChar%, outChar%
'--- keyboard layouts supported by this routine ---
CONST kbDaDk% = 1030, kbDeDe% = 1031, kbEsEs% = 1034, kbFrFr% = 1036
CONST kbNlNl% = 1043, kbNbNo% = 1044, kbSvSe% = 1053
CONST kbDeCh% = 2055, kbEsMx% = 2058, kbFrBe% = 2060, kbPtPt% = 2070
'--- accent preselection key types ---
CONST pkAcute% = 1, pkGrave% = 2, pkUmlau% = 3, pkCircu% = 4
'--- variables init ---
STATIC lastKey&, preKey&, ascNum$
InkeyHit$ = "": charCode& = 0
'--- flush regular input buffer & get next key hit ---
DO: LOOP UNTIL INKEY$ = ""
hitVal& = _KEYHIT
IF hitVal& <> 0 THEN
'--- get modifiers ---
modShift% = _KEYDOWN(100303) OR _KEYDOWN(100304)
modCtrl% = _KEYDOWN(100305) OR _KEYDOWN(100306)
modAltGr% = _KEYDOWN(100307) AND _KEYDOWN(100306)
modAlt% = (_KEYDOWN(100307) OR _KEYDOWN(100308)) AND NOT modAltGr%
'--- special conditions ---
modNone% = NOT modShift% AND NOT modCtrl% AND NOT modAlt% AND NOT modAltGr%
modShiftOnly% = modShift% AND NOT modCtrl% AND NOT modAlt% AND NOT modAltGr%
modCtrlOnly% = modCtrl% AND NOT modShift% AND NOT modAlt% AND NOT modAltGr%
modAltOnly% = modAlt% AND NOT modShift% AND NOT modCtrl% 'not AltGr implied in Alt
modCtrlNoAlt% = modCtrl% AND NOT modAlt% AND NOT modAltGr% 'shift allowed
'--- start evaluation ---
IF hitVal& > 0 THEN
IF hitVal& <= 255 THEN lastKey& = hitVal& 'for later release detection
'--- lookup preselected accent (if any) ---
IF hitVal& >= 32 AND hitVal& <= 127 THEN
SELECT CASE preKey&
CASE pkAcute%
RESTORE InkeyHit_Acute
DO: READ inChar%, outChar%: LOOP UNTIL inChar% = hitVal& OR inChar% = 0
charCode& = outChar%
CASE pkGrave%
RESTORE InkeyHit_Grave
DO: READ inChar%, outChar%: LOOP UNTIL inChar% = hitVal& OR inChar% = 0
charCode& = outChar%
CASE pkUmlau%
RESTORE InkeyHit_Umlau
DO: READ inChar%, outChar%: LOOP UNTIL inChar% = hitVal& OR inChar% = 0
charCode& = outChar%
CASE pkCircu%
RESTORE InkeyHit_Circu
DO: READ inChar%, outChar%: LOOP UNTIL inChar% = hitVal& OR inChar% = 0
charCode& = outChar%
END SELECT
END IF
'Regardless of the lookup result, any non-modifier key press
'has to properly cancel any pending preselection.
IF hitVal& <= 65535 THEN preKey& = 0
'--- if no accent was found or preselected, then move on ---
IF charCode& = 0 THEN
'take the regular key code as default
charCode& = hitVal&
'check shift/ctrl/alt conditions and special behavior
SELECT CASE hitVal&
CASE 9 'tab & reverse tab
IF modShiftOnly% THEN charCode& = 15 * 256
CASE 48 TO 57 'numeric keys 0-9
IF NOT modNone% THEN charCode& = 0
CASE 65 TO 90 'CTRL CAPS A-Z: 1-26
IF modCtrlNoAlt% THEN charCode& = hitVal& - 64
CASE 97 TO 122 'CTRL a-z: 1-26
IF modCtrlNoAlt% THEN charCode& = hitVal& - 96
CASE 128 TO 255 'Ext. ASCII (Cp1252 to Cp437 mapping, if available)
RESTORE InkeyHit_Regul
DO: READ inChar%, outChar%: LOOP UNTIL inChar% = hitVal& OR inChar% = 0
charCode& = outChar%
CASE 256 TO 65535 'double char chr$(0) +
IF (hitVal& AND 255) = 0 THEN
hitVal& = hitVal& \ 256
SELECT CASE hitVal& 'Alt overrides Ctrl overrides Shift
CASE 59 TO 68 'F1-F10
IF modShift% THEN charCode& = (hitVal& + 25) * 256
IF modCtrl% THEN charCode& = (hitVal& + 35) * 256
IF modAlt% THEN charCode& = (hitVal& + 45) * 256
CASE 133, 134 'F11-F12
IF modShift% THEN charCode& = (hitVal& + 2) * 256
IF modCtrl% THEN charCode& = (hitVal& + 4) * 256
IF modAlt% THEN charCode& = (hitVal& + 6) * 256
CASE 71 'Home
IF modCtrl% THEN charCode& = 119 * 256
IF modAlt% THEN charCode& = 151 * 256
CASE 72 'ArrowUp
IF modCtrl% THEN charCode& = 141 * 256
IF modAlt% THEN charCode& = 152 * 256
CASE 73 'PageUp
IF modCtrl% THEN charCode& = 132 * 256
IF modAlt% THEN charCode& = 153 * 256
CASE 75 'ArrowLeft
IF modCtrl% THEN charCode& = 115 * 256
IF modAlt% THEN charCode& = 155 * 256
CASE 77 'ArrowRight
IF modCtrl% THEN charCode& = 116 * 256
IF modAlt% THEN charCode& = 157 * 256
CASE 79 'End
IF modCtrl% THEN charCode& = 117 * 256
IF modAlt% THEN charCode& = 159 * 256
CASE 80 'ArrowDown
IF modCtrl% THEN charCode& = 145 * 256
IF modAlt% THEN charCode& = 160 * 256
CASE 81 'PageDown
IF modCtrl% THEN charCode& = 118 * 256
IF modAlt% THEN charCode& = 161 * 256
CASE 82 'Insert
IF modCtrl% THEN charCode& = 146 * 256
IF modAlt% THEN charCode& = 162 * 256
CASE 83 'Delete
IF modCtrl% THEN charCode& = 147 * 256
IF modAlt% THEN charCode& = 163 * 256
END SELECT
END IF
END SELECT
END IF
ELSE
oPreKey& = preKey& 'save current preselection state
SELECT CASE hitVal&
CASE -57 TO -48 'collect numbers (Alt + char code input)
IF modAltOnly% THEN ascNum$ = ascNum$ + CHR$(-hitVal&): _
ELSE IF hitVal& = -50 AND modAltGr% AND kbl% = kbFrFr% THEN charCode& = 126 '~
CASE -100308 'Alt released: build char from last 3 digits
IF ascNum$ <> "" THEN
charCode& = VAL(RIGHT$(ascNum$, 3))
IF charCode& < 32 OR charCode& > 255 THEN charCode& = 0
lastKey& = 0: preKey& = 0: ascNum$ = "" 'cancel all
END IF
CASE -lastKey& 'cancel the last key
lastKey& = 0
'This case is just here to trap the releases of regular keys.
'It is required to avoid the generation of false positives
'for the following accent preselection cases, if the regular
'key release would generate the same value as any accent key.
CASE -186
SELECT CASE kbl%
CASE kbPtPt% 'acute & grave
IF modNone% THEN preKey& = pkAcute%
IF modShiftOnly% THEN preKey& = pkGrave%
CASE kbEsEs% 'grave & circumflex
IF modNone% THEN preKey& = pkGrave%
IF modShiftOnly% THEN preKey& = pkCircu%
CASE kbDaDk%, kbSvSe%, kbNbNo% 'umlaut & circumflex
IF modNone% THEN preKey& = pkUmlau%
IF modShiftOnly% THEN preKey& = pkCircu%
CASE kbEsMx% 'acute & umlaut
IF modNone% THEN preKey& = pkAcute%
IF modShiftOnly% THEN preKey& = pkUmlau%
END SELECT
CASE -187
SELECT CASE kbl%
CASE kbPtPt% 'umlaut
IF modNone% THEN preKey& = pkUmlau%
END SELECT
CASE -191
SELECT CASE kbl%
CASE kbPtPt% 'circumflex
IF modNone% THEN preKey& = pkCircu%
CASE kbEsMx% 'grave
IF modNone% THEN preKey& = pkGrave%
END SELECT
CASE -192
SELECT CASE kbl%
CASE kbNlNl% 'acute & grave
IF modNone% THEN preKey& = pkAcute%
IF modShiftOnly% THEN preKey& = pkGrave%
CASE kbDeCh% 'umlaut
IF modNone% THEN preKey& = pkUmlau%
CASE kbFrFr%, kbFrBe% 'acute
IF modNone% THEN preKey& = pkAcute%
END SELECT
CASE -219
SELECT CASE kbl%
CASE kbDeCh% 'acute
IF modNone% THEN preKey& = pkAcute%
CASE kbDaDk%, kbSvSe%, kbNbNo% 'acute & grave
IF modNone% THEN preKey& = pkAcute%
IF modShiftOnly% THEN preKey& = pkGrave%
END SELECT
CASE -220
SELECT CASE kbl%
CASE kbFrBe% 'grave
IF modNone% THEN preKey& = pkGrave%
CASE kbDeDe% 'circumflex
IF modNone% THEN preKey& = pkCircu%
END SELECT
CASE -221
SELECT CASE kbl%
CASE kbFrFr%, kbFrBe% 'circumflex & umlaut
IF modNone% THEN preKey& = pkCircu%
IF modShiftOnly% THEN preKey& = pkUmlau%
CASE kbNlNl% 'umlaut & circumflex
IF modNone% THEN preKey& = pkUmlau%
IF modShiftOnly% THEN preKey& = pkCircu%
CASE kbDeCh% 'circumflex & grave
IF modNone% THEN preKey& = pkCircu%
IF modShiftOnly% THEN preKey& = pkGrave%
CASE kbDeDe% 'acute & grave
IF modNone% THEN preKey& = pkAcute%
IF modShiftOnly% THEN preKey& = pkGrave%
END SELECT
CASE -222
SELECT CASE kbl%
CASE kbEsEs% 'acute & umlaut
IF modNone% THEN preKey& = pkAcute%
IF modShiftOnly% THEN preKey& = pkUmlau%
CASE kbEsMx% 'circumflex
IF modNone% THEN preKey& = pkCircu%
END SELECT
CASE -226
SELECT CASE kbl%
CASE kbPtPt% '\
IF modNone% THEN charCode& = 92
END SELECT
CASE ELSE
oPreKey& = 0 'no case did match, cancel saved state
END SELECT
'--- check double-preselection key press (if any) ---
IF oPreKey& > 0 AND oPreKey& = preKey& THEN
SELECT CASE preKey&
CASE pkAcute%: charCode& = 39
CASE pkGrave%: charCode& = 96
CASE pkUmlau%: charCode& = 34
CASE pkCircu%: charCode& = 94
END SELECT
END IF
END IF
END IF
'--- finally encode the usual INKEY$ result ---
IF charCode& > 0 AND charCode& <= 65535 THEN
IF charCode& <= 255 THEN InkeyHit$ = CHR$(charCode&)
IF charCode& >= 256 AND charCode& <= 65535 AND (charCode& AND 255) = 0 THEN InkeyHit$ = CHR$(0) + CHR$(charCode& \ 256)
lastKey& = 0: preKey& = 0
END IF
EXIT FUNCTION
'-----------------------------
'Char lookup tables:
'=> pairs of Input ASC (Cp1252), Output ASC (Cp437)
'=> lists must be double 0-terminated
InkeyHit_Regul:
DATA 128,238,161,173,162,155,163,156,164,15,165,157,166,124,167,21,170,166,171,174
DATA 172,170,176,248,177,241,178,253,179,252,181,230,182,20,183,250,186,167,187,175
DATA 188,172,189,171,191,168,196,142,197,143,198,146,199,128,201,144,209,165,214,153
DATA 220,154,223,225,224,133,225,160,226,131,228,132,229,134,230,145,231,135,232,138
DATA 233,130,234,136,235,137,236,141,237,161,238,140,239,139,241,164,242,149,243,162
DATA 244,147,246,148,247,246,248,232,249,151,250,163,251,150,252,129,255,152,0,0
InkeyHit_Acute:
DATA 32,39,97,160,101,130,105,161,111,162,117,163,69,144,0,0
InkeyHit_Grave:
DATA 32,96,97,133,101,138,105,141,111,149,117,151,0,0
InkeyHit_Umlau:
DATA 97,132,101,137,105,139,111,148,117,129,65,142,79,153,85,154,121,152,0,0
InkeyHit_Circu:
DATA 32,94,97,131,101,136,105,140,111,147,117,150,65,143,0,0
END FUNCTION
|