/* * BindWKeys.c Reads a file and creates an array of key bindings * to be used by a hot-key handler to tell which keys * do what. * * Copyright (c) 1987 by Davide P. Cervone * You may use this code provided this copyright notice is left intact. */ #include #include #include #ifndef NO_FILE #include #endif #include "wKeys.h" #ifndef NO_FILE #define KEYMASK 0x00FF /* default qualifier key mask */ #define SHIFT IECODE_UP_PREFIX #define BADVALUE -1 /* error while parsing input line */ /* * Macros to tell whether a character is printable or not, and * whether it is not alphanumeric */ #define PRINTABLE(c) ((c)>=' '&&(c)<='~') #define NOTPRINTABLE(c) ((c)<' '||(c)>'~') #define NOTALPHANUM(c)\ ((c)<'0'||((c)>'9'&&(c)<'A')||((c)>'Z'&&(c)<'a')||(c)>'z') /* * Create a new instance of a given structure type */ #define NEW(s,var) (var = (struct s *)New("var",sizeof(struct s))) #define LINESIZE 132 static char InputLine[LINESIZE+1]; /* the line read from the file */ static char *CurPos; /* current character position in line */ static char *CurWord; /* pointer to begining of current word */ static char TerminationChar; /* character that ended the word */ static int LineCount = 0; /* number of lines read from the file */ #endif struct HotKeyItem *KeyList = NULL; /* the list of key definitions so far */ struct HotKey *KeyArray = NULL; /* the sorted array of key definitions */ int KeyCount = 0; /* the number of key definitions */ #define KEYARRAYSIZE (KeyCount*sizeof(struct HotKey)) #ifndef NO_FILE static FILE *InFile = NULL; /* the input file */ #define ERROR _OSERR /* the system's error variable */ extern int ERROR; /* * This structure maps a character string to a numeric value. It is used * for mapping key names to keyboard scan codes and key action names to * action numbers. */ struct Definition { char *Name; UBYTE Code; }; /* * Qualifier[] maps the names of the qualifier keys to their corresponding * bit positions within the ie_Qualifier field of an InputEvent. This List * is sorted by name. Note that LCOMMAND is equivalent to LAMIGA, etc. * Note also that there are some compound qualifier names, such as SHIFT. * These are expanded into more than one key definition. * * See devices/inputevent.h for more details on qualifier values. */ static struct Definition Qualifier[] = { {"ALT",20}, {"AMIGA",22}, {"CAPSLOCK",2}, {"CONTROL",3}, {"INTERRUPT",10}, {"KEYUP",31}, {"LALT",4}, {"LAMIGA",6}, {"LBUTTON",14}, {"LCOMMAND",6}, {"LSHIFT",0}, {"MBUTTON",12}, {"MULTIBROADCAST",11}, {"NUMERICPAD",8}, {"RALT",5}, {"RAMIGA",7}, {"RBUTTON",13}, {"RCOMMAND",7}, {"RELATIVEMOUSE",15}, {"REPEAT",9}, {"RSHIFT",1}, {"SHIFT",16}, }; #define MAXQUALIFIER (sizeof(Qualifier)/sizeof(struct Definition)) /* * AsciiToKeyCode[] maps ASCII characters to keyboard scan-codes. This array * is in ASCII order. SHIFT indicates that one of the shift keys must be * held down together with the correct key in order to produce that ASCII * character. Note, however, that upper- and lower-case letters both are * mapped to un-shifted keys. */ static UBYTE AsciiToKeyCode[] = { 0x01 | SHIFT, /* ! */ 0x2A | SHIFT, /* " */ 0x03 | SHIFT, /* # */ 0x04 | SHIFT, /* $ */ 0x05 | SHIFT, /* % */ 0x07 | SHIFT, /* & */ 0x2A, /* ' */ 0x09 | SHIFT, /* ( */ 0x0A | SHIFT, /* ) */ 0x08 | SHIFT, /* * */ 0x0C | SHIFT, /* + */ 0x38, /* , */ 0x0B, /* - */ 0x39, /* . */ 0x3A, /* / */ 0x0A, /* 0 */ 0x01, /* 1 */ 0x02, /* 2 */ 0x03, /* 3 */ 0x04, /* 4 */ 0x05, /* 5 */ 0x06, /* 6 */ 0x07, /* 7 */ 0x08, /* 8 */ 0x09, /* 9 */ 0x29 | SHIFT, /* : */ 0x29, /* ; */ 0x38 | SHIFT, /* < */ 0x0C, /* = */ 0x39 | SHIFT, /* > */ 0x3A | SHIFT, /* ? */ 0x02 | SHIFT, /* @ */ 0x20, /* A */ 0x35, /* B */ 0x33, /* C */ 0x22, /* D */ 0x12, /* E */ 0x23, /* F */ 0x24, /* G */ 0x25, /* H */ 0x17, /* I */ 0x26, /* J */ 0x27, /* K */ 0x28, /* L */ 0x37, /* M */ 0x36, /* N */ 0x18, /* O */ 0x19, /* P */ 0x10, /* Q */ 0x13, /* R */ 0x21, /* S */ 0x14, /* T */ 0x16, /* U */ 0x34, /* V */ 0x11, /* W */ 0x32, /* X */ 0x15, /* Y */ 0x31, /* Z */ 0x1A, /* [ */ 0x0D | SHIFT, /* \ */ 0x1B, /* ] */ 0x06 | SHIFT, /* ^ */ 0x0B | SHIFT, /* _ */ 0x00, /* ` */ 0x20, /* a */ 0x35, /* b */ 0x33, /* c */ 0x22, /* d */ 0x12, /* e */ 0x23, /* f */ 0x24, /* g */ 0x25, /* h */ 0x17, /* i */ 0x26, /* j */ 0x27, /* k */ 0x28, /* l */ 0x37, /* m */ 0x36, /* n */ 0x18, /* o */ 0x19, /* p */ 0x10, /* q */ 0x13, /* r */ 0x21, /* s */ 0x14, /* t */ 0x16, /* u */ 0x34, /* v */ 0x11, /* w */ 0x32, /* x */ 0x15, /* y */ 0x31, /* z */ 0x1A | SHIFT, /* { */ 0x0D | SHIFT, /* | */ 0x1B | SHIFT, /* } */ 0x00 | SHIFT, /* ~ */ }; #define MAXASCII sizeof(AsciiToKeyCode) /* * Key[] maps key names to their keyboard scan-codes. This array is sorted * by name. SHIFT means that a SHIFT key must be held down in addition to * the indicated key. Some keys have more than one name (e.g., ESCAPE is * equivalent to ESC). Note that pressing a qualifier key also can be * detected. */ static struct Definition Key[] = { {"BACKSPACE",0x41}, {"BS", 0x41}, {"CAPSLOCKKEY",0x62}, {"COLON",0x29 | SHIFT}, {"COMMA",0x38}, {"CONTROLKEY",0x63}, {"DASH",0x0B}, {"DEL",0x46}, {"DELETE",0x46}, {"DOT",0x3C}, {"DOWNARROW",0x4D}, {"ENTER",0x42}, {"ESC",0x45}, {"ESCAPE",0x45}, {"F1",0x50}, {"F10",0x59}, {"F2",0x51}, {"F3",0x52}, {"F4",0x53}, {"F5",0x54}, {"F6",0x55}, {"F7",0x56}, {"F8",0x57}, {"F9",0x58}, {"HELP",0x5F}, {"KP0",0x0F}, {"KP1",0x1D}, {"KP2",0x1E}, {"KP3",0x1F}, {"KP4",0x2D}, {"KP5",0x2E}, {"KP6",0x2F}, {"KP7",0x3D}, {"KP8",0x3E}, {"KP9",0x3F}, {"LALTKEY",0x64}, {"LAMIGAKEY",0x66}, {"LCOMMANDKEY",0x66}, {"LEFTARROW",0x4F}, {"LSHIFTKEY",0x60}, {"MINUS",0x4A}, {"RALTKEY",0x65}, {"RAMIGAKEY",0x67}, {"RCOMMANDKEY",0x67}, {"RETURN",0x44}, {"RIGHTARROW",0x4E}, {"RSHIFTKEY",0x61}, {"SPACE",0x40}, {"TAB",0x42}, {"UPARROW",0x4C}, }; #define MAXKEY (sizeof(Key)/sizeof(struct Definition)) /* * Action[] maps key action names to their action numbers (used by the * input handler to perform the action when the key is pressed). This array * is sorted by name. */ static struct Definition Action[] = { {"BACK-WINDOW-TO-FRONT", BACKTOFRONT}, {"FRONT-WINDOW-TO-BACK", FRONTTOBACK}, {"NEXT-WINDOW", ACTIVATENEXT}, {"PREVIOUS-WINDOW", ACTIVATEPREVIOUS}, {"SCREEN-TO-BACK", SCREENTOBACK}, {"SCREEN-TO-FRONT", SCREENTOFRONT}, {"WINDOW-TO-BACK", WINDOWTOBACK}, {"WINDOW-TO-FRONT", WINDOWTOFRONT}, }; #define MAXACTION (sizeof(Action)/sizeof(struct Definition)) #endif /* * Some shorthand macros used to define the default key layout */ #define RAMIGA IEQUALIFIER_RCOMMAND #define RSHIFT IEQUALIFIER_RSHIFT #define UPARROW 0x4C #define DOWNARROW 0x4D #define RIGHTARROW 0x4E #define LEFTARROW 0x4F #define HOTKEY(q,c,a) {{c,0,q},{0xFF,a,RAMIGA|RSHIFT}} /* * DefaultKey[] maps the default key layout. This array is sorted by * KeyCode value. Change this array to change the default key bindings. */ static struct HotKey DefaultKey[] = { HOTKEY( RAMIGA, UPARROW, WINDOWTOFRONT), HOTKEY( RSHIFT | RAMIGA, UPARROW, SCREENTOFRONT), HOTKEY( RAMIGA, DOWNARROW, WINDOWTOBACK), HOTKEY( RSHIFT | RAMIGA, DOWNARROW, SCREENTOBACK), HOTKEY( RAMIGA, RIGHTARROW, ACTIVATENEXT), HOTKEY( RSHIFT | RAMIGA, RIGHTARROW, FRONTTOBACK), HOTKEY( RAMIGA, LEFTARROW, ACTIVATEPREVIOUS), HOTKEY( RSHIFT | RAMIGA, LEFTARROW, BACKTOFRONT), }; #define DEFAULTSIZE (sizeof(DefaultKey)/sizeof(struct HotKey)) #ifndef NO_FILE /* * Error() * * Print an error message and the line number where the error occured. * Return the error value. */ static int Error(s,x1,x2,x3) char *s, *x1,*x2,*x3; { printf("Line %2d: ",LineCount); printf(s,x1,x2,x3); printf("\n"); return(BADVALUE); } /* * GetNextWord() * * Isolate the next word in the line read from the file. * If we are not at the end of the line, then * skip over leading spaces, * set CurWord to point to the beginning of the word, * while we are not at the end of the word, * check if the current character is a word delimiter: * if it is a space or a tab, * skip additional spaces or tabs, * set the terminator character, * and end the word. * (At this point, CurPos will be pointing to the "real" delimiter, * or to the beginning of the next word, if the delimiter really was * a space). * if it was a NULL, convert it to a new-line. * if it was a dash, comma, colon or new-line, * save the termination character for later, * replace the charactger with a NULL so that CurWord will end at * the end of the word, * and stop looking for more of the word. * otherwise * if we're still looking for the end of the word (i.e., we have not * ended, it by hitting a space), * if its unprintable or the current word is more than one character * long and the current character is not alphanumeric, * then record the bad character and end the word * go on to the next character (i.e., add the current one to the word) */ static void GetNextWord() { short NotDone = TRUE; char c; if (TerminationChar != '\n') { while (*CurPos == ' ' || *CurPos == '\t') CurPos++; CurWord = CurPos; while (NotDone) { if (*CurPos == ' ' || *CurPos == '\t') { *CurPos = '\0'; while (*(++CurPos) == ' ' || *CurPos == '\t'); TerminationChar = ' '; NotDone = FALSE; } switch(c = *CurPos) { case '\0': c = *CurPos = '\n'; case '-': case ',': case ':': case '\n': TerminationChar = c; *CurPos++ = '\0'; NotDone = FALSE; break; default: if (NotDone) { if (NOTPRINTABLE(c) || (CurPos != CurWord && NOTALPHANUM(c))) { TerminationChar = c; NotDone = FALSE; } CurPos++; } break; } } } } /* * FindWord() * * Search the KeyWord array (containing Count entries) for an entry with * Name equal to theWord. Return the corresponding Code value, or BADVALUE * if theWord does not appear in the KeyWord array. * * KeyWord[] must be sorted by name, since we use a binary search to find * theWord witin it. */ static int FindWord(theWord,KeyWord,Count) char *theWord; struct Definition KeyWord[]; int Count; { int value = BADVALUE; register short Min,Max, Num; register int comp; Max = Count; Min = -1; while ((Num = (Min + Max) >> 1) != Min && value == BADVALUE) { comp = stricmp(theWord,KeyWord[Num].Name); if (comp < 0) Max = Num; else if (comp > 0) Min = Num; else value = KeyWord[Num].Code; } return(value); } /* * FindQualifier() * * Find a qualifier name in the Qualifier[] array. */ #define FindQualifier(w) FindWord(w,Qualifier,MAXQUALIFIER) /* * FindKeyCode() * * Find the scan-code for the given key name. If the key name is a single * ASCII character, use the AsciiToKeyCode array to look up the scan-code * directly, otherwise use FindWord() to search the Key[] array. */ static int FindKeyCode(theWord) char *theWord; { int value = BADVALUE; if (strlen(theWord) == 1) { if (PRINTABLE(*theWord)) value = AsciiToKeyCode[*theWord-'!']; } else { value = FindWord(theWord,Key,MAXKEY); } return(value); } /* * GetKeyCode() * * Parse a set of qualifiers and a key name and return the KeyCode longword * that describes the designated key. SHIFT, AMIGA, and ALT flags are * used to indicate when more than one key is designated. Return BADVALUE * if there is an error parsing the line. * * Get the next word on the line, and check the termination character. * Qualifiers end with dashes, commas, or spaces; key-names end with a colon. * Try to find the qualifier or key-name in the proper list, and give an error * if it can not be found, or if the name is null. For a qualifier, set its * flag bit in the KeyCode longword. For key-names, set the scan-code and * the set the SHIFT bit in the qualifier flag bits if necessary. * If the end of the line is reached, display an error. If some other * delimiter was found, then display an error (making unprintable characters * printable). * * Once a key-name is found, stop looking for more words. */ static void GetKeyCode(theKey) long *theKey; { short NotDone = TRUE; int value; *theKey = 0; while (NotDone) { GetNextWord(); switch(TerminationChar) { case '-': case ',': case ' ': if (strlen(CurWord)) { value = FindQualifier(CurWord); if (value > BADVALUE) *theKey |= (1 << value); else *theKey = Error("Unrecognized key qualifier '%s'",CurWord); } else { *theKey = Error("Missing qualifier keyword"); } break; case ':': if (strlen(CurWord)) { value = FindKeyCode(CurWord); if (value > BADVALUE) { *theKey |= ((value & (~SHIFT)) << 24) | ((value & SHIFT) << 9); } else { *theKey = Error("Unrecognized key name '%s'",CurWord); } } else { *theKey = Error("Key name not specified"); } NotDone = FALSE; break; case '\n': *theKey = Error("Key name ends prematurely"); NotDone = FALSE; break; default: if ((TerminationChar & 0x7F) >= ' ') *theKey = Error("Illegal delimiter character '%c'", (char)TerminationChar); else *theKey = Error("Illegal delimiter character '^%c'", (char)((TerminationChar & 0x7F) + '@')); break; } } } /* * GetKeyAction() * * Parse the rest of the input line for a key-action keyword. First, remove * leading and training blanks, and replace the final new-line with a NULL. * Look up the remainder of the line (if any) in the Action[] array, and * if it is not found, report the error. */ static void GetKeyAction(theAction) short *theAction; { *theAction = BADVALUE; while (*CurPos == ' ' || *CurPos == '\t') CurPos++; CurWord = CurPos; CurPos = CurPos + strlen(CurWord) - 1; if (*CurPos == '\n') *CurPos-- = '\0'; while (*CurPos == ' ' || *CurPos == '\t') *CurPos-- = '\0'; if (*CurWord) { *theAction = FindWord(CurWord,Action,MAXACTION); if (*theAction == BADVALUE) Error("Unrecognized action '%s'",CurWord); } else { Error("No action specified"); } } /* * KeyCompare() * * Return a positive number if the first key is bigger than the second, * zero if they are equal, and a negative number if the second key is * bigger. This routine is used by mSort() to sort the keys. */ static int KeyCompare(key1,key2) struct HotKeyItem *key1, *key2; { return(key1->hki_KeyCode - key2->hki_KeyCode); } /* * * KeyDispose() * * Remove a duplicate key definition and report the fact that a key is * multiply defined (it takes a little work to get the qualifier and key * names back out of the arrays). This routine is called by mSort() when * it finds duplicate keys. */ static void KeyDispose(key) struct HotKeyItem *key; { short i; UBYTE code = key->hki_Code & 0x7F; int KeyNotFound = TRUE; long mask = 0xFFFFFFFF; printf("Key "); for (i=0; ihki_KeyCode & (1 << Qualifier[i].Code) & mask) { printf("%s-",Qualifier[i].Name); mask &= ~(1 << Qualifier[i].Code); } for (i=0; iNext = key->Prev = NULL; FreeMem(key,sizeof(*key)); } /* * GetKeyList() * * Read each line from the file (incrementing the line count as we go), * and skip leading spaces and blank lines. Get the key code and key action * specified on the line. If no error was found, add the key definition * to the linked list of keys defined. If SHIFT, AMIGA, or ALT were specified, * then add one key each for the left and right version of that qualifier. */ static void GetKeyList() { long theKey; short theAction; struct HotKeyItem *TempKey; UWORD mask,multikeys; extern char *fgets(); while (feof(InFile) == FALSE) { CurPos = fgets(InputLine,LINESIZE,InFile); TerminationChar = '\0'; LineCount++; while (*CurPos == ' ' || *CurPos == '\t') CurPos++; if (CurPos != NULL && *CurPos != '\0' && *CurPos != '\n') { GetKeyCode(&theKey); GetKeyAction(&theAction); if (theKey != BADVALUE && theAction != BADVALUE) { multikeys = (theKey >> 15) & 0xFE; multikeys |= multikeys << 1; if (multikeys == 0) multikeys = 1; for (mask=1; multikeys; mask<<=1,multikeys>>=1) { if (multikeys & 1) { NEW(HotKeyItem,TempKey); TempKey->hki_KeyCode = theKey; TempKey->hki_Flags = 0; TempKey->hki_Qual |= mask >> 1; TempKey->hki_KeyMask = 0xFFFFFFFF; TempKey->hki_Action = theAction; TempKey->Next = KeyList; KeyList = TempKey; KeyCount++; } } } } } } /* * SetKeyMasks() * * For each key scan-code, we OR together the qualifier masks for all the * definitions for that scan-code and set the key Mask value for each * key with that scan-code to the final ORed mask. That is, the Mask value * indicates what qualifiers are important for determining when a key has * been pressed (and distinguishing it from other definitions using the * same scan-code but different qualifiers). */ static void SetKeyMasks() { struct HotKeyItem *CurKey = KeyList; struct HotKeyItem *LastKey = CurKey; UWORD Mask; while (LastKey) { Mask = 0; while (CurKey && CurKey->hki_Code == LastKey->hki_Code) { Mask |= CurKey->hki_Qual; CurKey = CurKey->Next; } if (Mask == 0) Mask = KEYMASK; do { LastKey->hki_Mask = Mask; LastKey = LastKey->Next; } while (LastKey != CurKey); } } /* * MakeKeyArray() * * If there are any keys defined, allocate enough space for the KeyArray * that will contain the key definitions, then go through the list and * copy the definitions into the array. Free each item from the list once it * is copied. The array saves space (it does not need to contain pointers * to next and previous items), and allows for easy implementation of a * binary search on the array. */ static void MakeKeyArray() { struct HotKeyItem *TempKey; short i; if (KeyCount) { KeyArray = (struct HotKey *)New("KeyArray",KEYARRAYSIZE); for (i=0; ihki_KeyCode; KeyArray[i].hk_KeyMask = KeyList->hki_KeyMask; TempKey = KeyList; KeyList = KeyList->Next; FreeMem(TempKey,sizeof(*TempKey)); } } } #endif /* * MakeDefaultArray() * * Copy the DefaultKey[] array into a dynamically allocated array that can * be passed to the input handler and still remain in memory even when the * original process is unloaded. */ static void MakeDefaultArray() { short i; KeyCount = DEFAULTSIZE; KeyArray = (struct HotKey *)New("KeyArray",KEYARRAYSIZE); for (i=0; i 1) { InFile = fopen(argv[1],"r"); if (InFile == NULL) DoExit("Can't Open File '%s': Error %d",argv[1],ERROR); GetKeyList(); if (KeyCount == 0) { DoExit("No valid key definitions found in file '%s'",argv[1]); } else { KeyList = mSort(KeyList,KeyCompare,KeyDispose); SetKeyMasks(); MakeKeyArray(); } } else #endif { MakeDefaultArray(); } }