/************************************************************************** * * ikm.c - Olsen's magical key inversion routines. * * You may use this piece of code in any application * without requesting permission of the original author. * * It is strongly recommended to use cx.lib/InvertString * instead of the following set of routines when running * under v37 or higher. * * Basically, the following code is derived from the * keymap inversion routines employed by KeyMacro 1.9, * strange enough the german keymap would never be * read correctly to generate the letter `f'. This and * a lot of other problems have been fixed (keep your * fingers crossed!) and single-depth-dead-key * conversion was added on the fly (i.e. accented * characters can be generated with keyboard layouts * which do not contain accent keys). * * Jimm Mackraz' original ikm.c code, Bill Hawes' * cmap.asm example keymap, and the `International * Keyboard Input' article by Eric Cotton and Carolyn * Scheppner published in Amiga-Mail provided enough * help to create the current release of this package * (which also is significantly shorter than Jimm's * original ikm.c code). * * A potential problem still exists: what happens if * a dead-key-modifiable key sports less than three * qualifiers (shift, alternate, control)? The * `International Keyboard Input' article states that * the number of qualifiers determines the format and the * length of the corresponding mk_Hi/LowKeyMap entry. * Unfortunately, no word is lost on how a keymap entry * of such kind would look like. For now my code * determines the number of table entries to look up * by counting the qualifiers listed in * km_Hi/LowKeyMapTypes which will probably find the * approriate key but will fail to generate a matching * qualifier (do YOU know of a fix?). * * Anyway, keymap inversion is a weird topic which * had better been covered in the early days of * the Amiga. * * A note to code hackers: * * Sometimes InvertKeyMap() will not return the * proper raw key value for a character but * rather the raw key value of the base character * (example: character = á, base character = a). * To avoid unpleasant sideeffects, just remove * the dead key conversion in lines 214-225 * (delete the loop, only check the first character * of the array). */ /* PackQualifiers(BYTE Bits): * * Piece together a valid pack of qualifiers from * a bitmapped key position. */ STATIC UWORD PackQualifiers(BYTE Bits) { UWORD Qualifier = 0; if(Bits & KCF_SHIFT) Qualifier |= IEQUALIFIER_LSHIFT; if(Bits & KCF_ALT) Qualifier |= IEQUALIFIER_LALT; if(Bits & KCF_CONTROL) Qualifier |= IEQUALIFIER_CONTROL; return(Qualifier); } /* CountPairs(BYTE Qualifiers): * * Count the number (not the one from Sesame Street) of * qualifiers given in a KeyMapTypes entry. */ STATIC BYTE CountPairs(BYTE Qualifiers) { BYTE Bits = 1; if(Qualifiers & KCF_SHIFT) Bits++; if(Qualifiers & KCF_ALT) Bits++; if(Qualifiers & KCF_CONTROL) Bits++; return(Bits); } /* FindIndexKey(): * * Find the key which if `preceding' the inverted base key * will generate the accented character (example: character * to be inverted = á, inverted base key = a, the preceding * key we are trying to find in this routine will put the * accent ´ on top of the vowel). */ STATIC APTR FindIndexKey(UBYTE Hi,UBYTE Offset,UBYTE *KeyTable,UBYTE *KeyTypes,APTR Index) { UBYTE *String; SHORT i,j; for(i = 0 ; i < Hi ; i++) { /* Examine only the dead key entries. */ if(KeyTypes[i] & KCF_DEAD) { String = (UBYTE *)(((ULONG *)KeyTable)[i]); /* Look for a dead-key index. */ for(j = 0 ; j < CountPairs(KeyTypes[i]) ; j++) { /* This comes close to real magic: * * if the corresponding InputEvent * is of type RAWKEY, the EventAddress * pointer contains the packed * codes and qualifiers of the * previous key(s). The v37 header * files reflect this, but alas, * some guys will want to recompile * this code using v34 (and below) * header files. */ if(String[2 * j] == DPF_DEAD && (String[2 * j + 1] & 0x1F) == ((ULONG)Index & BYTEMASK)) return((APTR)(((ULONG)(Offset + i) << 24) | (ULONG)PackQualifiers(j) << 16)); } } } return(NULL); } /* ScanKeyMap(): * * Scan a given keymap for an ANSI key and try to generate * an InputEvent which will -- if processed -- create the * given character. */ STATIC BYTE ScanKeyMap(UBYTE Hi,UBYTE Offset,UBYTE *KeyTable,UBYTE *KeyTypes,UBYTE AnsiKey,struct InputEvent *Event,BYTE Depth) { /* A bunch of qualifiers associated with KeyMapType bits. */ STATIC struct { UBYTE QualBits; UWORD Qualifiers[4]; } QualType[8] = { KC_NOQUAL, 0, 0, 0, 0, KCF_SHIFT, 0, 0, IEQUALIFIER_LSHIFT, 0, KCF_ALT, 0, 0, IEQUALIFIER_LALT, 0, KCF_CONTROL, 0, 0, IEQUALIFIER_CONTROL, 0, KCF_ALT|KCF_SHIFT, IEQUALIFIER_LSHIFT|IEQUALIFIER_LALT, IEQUALIFIER_LALT, IEQUALIFIER_LSHIFT, 0, KCF_CONTROL|KCF_ALT, IEQUALIFIER_CONTROL|IEQUALIFIER_LALT, IEQUALIFIER_CONTROL, IEQUALIFIER_LALT, 0, KCF_CONTROL|KCF_SHIFT, IEQUALIFIER_CONTROL|IEQUALIFIER_LSHIFT, IEQUALIFIER_CONTROL, IEQUALIFIER_LSHIFT, 0, KC_VANILLA, IEQUALIFIER_LSHIFT|IEQUALIFIER_LALT, IEQUALIFIER_LALT, IEQUALIFIER_LSHIFT, 0 }; BYTE *String; SHORT i,j,k; /* Scan the whole area. */ for(i = 0 ; i < Hi ; i++) { /* This looks like a dead key. */ if(KeyTypes[i] & KCF_DEAD) { String = (BYTE *)(((ULONG *)KeyTable)[i]); /* Check all table entries. */ for(j = 0 ; j < CountPairs(KeyTypes[i]) ; j++) { switch(String[2 * j]) { /* A simple dead key. */ case 0: if((UBYTE)String[2 * j + 1] == AnsiKey) { Event -> ie_Qualifier = PackQualifiers(j); Event -> ie_Code = Offset + i; return(TRUE); } break; /* A dead-key-modifiable key. */ case DPF_MOD: for(k = 0 ; k < Depth ; k++) { if((UBYTE)String[String[2 * j + 1] + k] == AnsiKey) { Event -> ie_Qualifier = PackQualifiers(j); Event -> ie_Code = Offset + i; Event -> ie_EventAddress = (APTR)k; return(TRUE); } } break; default: break; } } } /* This looks like a string. */ if(KeyTypes[i] & KCF_STRING) { String = (BYTE *)(((ULONG *)KeyTable)[i]); /* Only single character strings are * accepted, check the `no qualifier' * entry first. */ if(String[0] == 1) { if((UBYTE)String[String[1]] == AnsiKey) { /* Try to find the approriate * qualifier. */ for(k = 0 ; k < 8 ; k++) { if(QualType[k] . QualBits == (KeyTypes[i] & KC_VANILLA)) { Event -> ie_Qualifier = QualType[k] . Qualifiers[j]; Event -> ie_Code = Offset + i; return(TRUE); } } } } /* Are there any strings left which * require a qualifier key to be * pressed? */ for(j = 0 ; j < 3 ; j++) { if(KeyTypes[i] & (1 << j)) { if(String[2 + (2 * j)] == 1) { if((UBYTE)String[String[3 + (2 * j)]] == AnsiKey) { for(k = 0 ; k < 8 ; k++) { if(QualType[k] . QualBits == (KeyTypes[i] & KC_VANILLA)) { Event -> ie_Qualifier = QualType[k] . Qualifiers[j]; Event -> ie_Code = Offset + i; return(TRUE); } } } } } } } else { /* At last, something sensible, check * the remaining vanilla type keys. */ for(j = 3 ; j >= 0 ; j--) { if(AnsiKey == KeyTable[4 * i + j]) { for(k = 0 ; k < 8 ; k++) { if(QualType[k] . QualBits == KeyTypes[i]) { Event -> ie_Code = Offset + i; if(QualType[k] . QualBits == KC_VANILLA) { if(AnsiKey & 96) Event -> ie_Qualifier = QualType[k] . Qualifiers[j]; else Event -> ie_Qualifier = IEQUALIFIER_CONTROL; } else Event -> ie_Qualifier = QualType[k] . Qualifiers[j]; return(TRUE); } } } } } } return(FALSE); } /* InvertKeyMap(): * * Invert a given character. */ BYTE KeyInvert(UBYTE AnsiKey,struct InputEvent *Event,struct KeyMap *KeyMap,BYTE Depth) { BYTE Result; Event -> ie_Class = IECLASS_RAWKEY; Event -> ie_EventAddress = NULL; /* Check the high keymap types first to include control keys * such as backspace, return, delete, etc. instead of the * control+? combination given in the low keymap entries. */ if(!(Result = ScanKeyMap(0x28,0x40,(UBYTE *)KeyMap -> km_HiKeyMap,(UBYTE *)KeyMap -> km_HiKeyMapTypes,AnsiKey & BYTEMASK,Event,Depth))) Result = ScanKeyMap(0x40,0x00,(UBYTE *)KeyMap -> km_LoKeyMap,(UBYTE *)KeyMap -> km_LoKeyMapTypes,AnsiKey & BYTEMASK,Event,Depth); /* See if we are to generate a preceding keystroke. */ if(Result && Event -> ie_EventAddress) { APTR Address; /* Find the preceding keystroke. */ if(!(Address = FindIndexKey(0x28,0x40,(UBYTE *)KeyMap -> km_HiKeyMap,(UBYTE *)KeyMap -> km_HiKeyMapTypes,Event -> ie_EventAddress))) Address = FindIndexKey(0x40,0x00,(UBYTE *)KeyMap -> km_LoKeyMap,(UBYTE *)KeyMap -> km_LoKeyMapTypes,Event -> ie_EventAddress); /* Heavy magic, don't touch! */ if(!(Event -> ie_EventAddress = Address)) return(FALSE); } return(Result); }