1 /////////////////////////////////////////////////////////////////////////
2 // $Id: keymap.cc 14095 2021-01-30 18:47:25Z sshwarts $
3 /////////////////////////////////////////////////////////////////////////
4 //
5 //  Copyright (C) 2002-2021  The Bochs Project
6 //
7 //  This library is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU Lesser General Public
9 //  License as published by the Free Software Foundation; either
10 //  version 2 of the License, or (at your option) any later version.
11 //
12 //  This library is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 //  Lesser General Public License for more details.
16 //
17 //  You should have received a copy of the GNU Lesser General Public
18 //  License along with this library; if not, write to the Free Software
19 //  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
20 
21 /////////////////////////////////////////////////////////////////////////
22 //
23 // Todo
24 //  . Currently supported by sdl, wxGTK and x11. Check if other guis need mapping.
25 //  . Tables look-up should be optimised.
26 //
27 
28 #include "param_names.h"
29 #include "bochs.h"
30 #include "gui.h"
31 #include "keymap.h"
32 
33 // Table of bochs "BX_KEY_*" symbols
34 // the table must be in BX_KEY_* order
35 const char *bx_key_symbol[BX_KEY_NBKEYS] = {
36   "BX_KEY_CTRL_L",         "BX_KEY_SHIFT_L",        "BX_KEY_F1",
37   "BX_KEY_F2",             "BX_KEY_F3",             "BX_KEY_F4",
38   "BX_KEY_F5",             "BX_KEY_F6",             "BX_KEY_F7",
39   "BX_KEY_F8",             "BX_KEY_F9",             "BX_KEY_F10",
40   "BX_KEY_F11",            "BX_KEY_F12",            "BX_KEY_CTRL_R",
41   "BX_KEY_SHIFT_R",        "BX_KEY_CAPS_LOCK",      "BX_KEY_NUM_LOCK",
42   "BX_KEY_ALT_L",          "BX_KEY_ALT_R",          "BX_KEY_A",
43   "BX_KEY_B",              "BX_KEY_C",              "BX_KEY_D",
44   "BX_KEY_E",              "BX_KEY_F",              "BX_KEY_G",
45   "BX_KEY_H",              "BX_KEY_I",              "BX_KEY_J",
46   "BX_KEY_K",              "BX_KEY_L",              "BX_KEY_M",
47   "BX_KEY_N",              "BX_KEY_O",              "BX_KEY_P",
48   "BX_KEY_Q",              "BX_KEY_R",              "BX_KEY_S",
49   "BX_KEY_T",              "BX_KEY_U",              "BX_KEY_V",
50   "BX_KEY_W",              "BX_KEY_X",              "BX_KEY_Y",
51   "BX_KEY_Z",              "BX_KEY_0",              "BX_KEY_1",
52   "BX_KEY_2",              "BX_KEY_3",              "BX_KEY_4",
53   "BX_KEY_5",              "BX_KEY_6",              "BX_KEY_7",
54   "BX_KEY_8",              "BX_KEY_9",              "BX_KEY_ESC",
55   "BX_KEY_SPACE",          "BX_KEY_SINGLE_QUOTE",   "BX_KEY_COMMA",
56   "BX_KEY_PERIOD",         "BX_KEY_SLASH",          "BX_KEY_SEMICOLON",
57   "BX_KEY_EQUALS",         "BX_KEY_LEFT_BRACKET",   "BX_KEY_BACKSLASH",
58   "BX_KEY_RIGHT_BRACKET",  "BX_KEY_MINUS",          "BX_KEY_GRAVE",
59   "BX_KEY_BACKSPACE",      "BX_KEY_ENTER",          "BX_KEY_TAB",
60   "BX_KEY_LEFT_BACKSLASH", "BX_KEY_PRINT",          "BX_KEY_SCRL_LOCK",
61   "BX_KEY_PAUSE",          "BX_KEY_INSERT",         "BX_KEY_DELETE",
62   "BX_KEY_HOME",           "BX_KEY_END",            "BX_KEY_PAGE_UP",
63   "BX_KEY_PAGE_DOWN",      "BX_KEY_KP_ADD",         "BX_KEY_KP_SUBTRACT",
64   "BX_KEY_KP_END",         "BX_KEY_KP_DOWN",        "BX_KEY_KP_PAGE_DOWN",
65   "BX_KEY_KP_LEFT",        "BX_KEY_KP_RIGHT",       "BX_KEY_KP_HOME",
66   "BX_KEY_KP_UP",          "BX_KEY_KP_PAGE_UP",     "BX_KEY_KP_INSERT",
67   "BX_KEY_KP_DELETE",      "BX_KEY_KP_5",           "BX_KEY_UP",
68   "BX_KEY_DOWN",           "BX_KEY_LEFT",           "BX_KEY_RIGHT",
69   "BX_KEY_KP_ENTER",       "BX_KEY_KP_MULTIPLY",    "BX_KEY_KP_DIVIDE",
70   "BX_KEY_WIN_L",          "BX_KEY_WIN_R",          "BX_KEY_MENU",
71   "BX_KEY_ALT_SYSREQ",     "BX_KEY_CTRL_BREAK",     "BX_KEY_INT_BACK",
72   "BX_KEY_INT_FORWARD",    "BX_KEY_INT_STOP",       "BX_KEY_INT_MAIL",
73   "BX_KEY_INT_SEARCH",     "BX_KEY_INT_FAV",        "BX_KEY_INT_HOME",
74   "BX_KEY_POWER_MYCOMP",   "BX_KEY_POWER_CALC",     "BX_KEY_POWER_SLEEP",
75   "BX_KEY_POWER_POWER",    "BX_KEY_POWER_WAKE",
76 };
77 
78 bx_keymap_c bx_keymap;
79 
80 #define LOG_THIS bx_keymap.
81 
bx_keymap_c(void)82 bx_keymap_c::bx_keymap_c(void)
83 {
84     put("KEYMAP");
85 
86     keymapCount = 0;
87     keymapTable = (BXKeyEntry *)NULL;
88 }
89 
~bx_keymap_c(void)90 bx_keymap_c::~bx_keymap_c(void)
91 {
92     if(keymapTable != NULL) {
93       free(keymapTable);
94       keymapTable = (BXKeyEntry *)NULL;
95     }
96     keymapCount = 0;
97 }
98 
loadKeymap(Bit32u stringToSymbol (const char *))99 void bx_keymap_c::loadKeymap(Bit32u stringToSymbol(const char*))
100 {
101   if (SIM->get_param_bool(BXPN_KBD_USEMAPPING)->get()) {
102     loadKeymap(stringToSymbol, SIM->get_param_string(BXPN_KBD_KEYMAP)->getptr());
103   }
104 }
105 
isKeymapLoaded()106 bool bx_keymap_c::isKeymapLoaded ()
107 {
108   return (keymapCount > 0);
109 }
110 
111 
112 ///////////////////
113 // I'll add these to the keymap object in a minute.
114 static unsigned char *lineptr = NULL;
115 static int lineCount;
116 
init_parse()117 static void init_parse()
118 {
119   lineCount = 0;
120 }
121 
init_parse_line(char * line_to_parse)122 static void init_parse_line(char *line_to_parse)
123 {
124   // chop off newline
125   lineptr = (unsigned char *)line_to_parse;
126   char *nl;
127   if ((nl = strchr(line_to_parse,'\n')) != NULL) {
128     *nl = 0;
129   }
130 }
131 
get_next_word(char * output)132 static Bit32s get_next_word(char *output)
133 {
134   char *copyp = output;
135   // find first nonspace
136   while (*lineptr && isspace(*lineptr))
137     lineptr++;
138   if (!*lineptr)
139     return -1;  // nothing but spaces until end of line
140   if (*lineptr == '#')
141     return -1;  // nothing but a comment
142   // copy nonspaces into the output
143   while (*lineptr && !isspace(*lineptr))
144     *copyp++ = *lineptr++;
145   *copyp=0;  // null terminate the copy
146   // there must be at least one nonspace, since that's why we stopped the
147   // first loop!
148   BX_ASSERT (copyp != output);
149   return 0;
150 }
151 
get_next_keymap_line(FILE * fp,char * bxsym,char * modsym,Bit32s * ascii,char * hostsym)152 static Bit32s get_next_keymap_line(FILE *fp, char *bxsym, char *modsym, Bit32s *ascii, char *hostsym)
153 {
154   char line[256];
155   char buf[256];
156 
157   buf[0] = 0;
158   while (1) {
159     lineCount++;
160     if (!fgets(line, sizeof(line)-1, fp)) return -1;  // EOF
161     line[sizeof(line) - 1] = '\0';
162     init_parse_line(line);
163     if (get_next_word(bxsym) >= 0) {
164       modsym[0] = 0;
165       char *p;
166       if ((p = strchr(bxsym, '+')) != NULL) {
167         *p = 0;  // truncate bxsym.
168         p++;  // move one char beyond the +
169         strcpy(modsym, p);  // copy the rest to modsym
170       }
171       if (get_next_word(buf) < 0) {
172         BX_PANIC(("keymap line %d: expected 3 columns", lineCount));
173         return -1;
174       }
175       if (buf[0] == '\'' && buf[2] == '\'' && buf[3] == 0) {
176         *ascii = (Bit8u) buf[1];
177       } else if (!strcmp(buf, "space")) {
178         *ascii = ' ';
179       } else if (!strcmp(buf, "return")) {
180         *ascii = '\n';
181       } else if (!strcmp(buf, "tab")) {
182         *ascii = '\t';
183       } else if (!strcmp(buf, "backslash")) {
184         *ascii = '\\';
185       } else if (!strcmp(buf, "apostrophe")) {
186         *ascii = '\'';
187       } else if (!strcmp(buf, "none")) {
188         *ascii = -1;
189       } else {
190         BX_PANIC(("keymap line %d: ascii equivalent is \"%s\" but it must be char constant like 'x', or one of space,tab,return,none", lineCount, buf));
191       }
192       if (get_next_word(hostsym) < 0) {
193         BX_PANIC (("keymap line %d: expected 3 columns", lineCount));
194         return -1;
195       }
196       return 0;
197     }
198     // no words on this line, keep reading.
199   }
200 }
201 
loadKeymap(Bit32u stringToSymbol (const char *),const char * filename)202 void bx_keymap_c::loadKeymap(Bit32u stringToSymbol(const char*), const char* filename)
203 {
204   FILE   *keymapFile;
205   char baseSym[256], modSym[256], hostSym[256];
206   Bit32s ascii = 0;
207   Bit32u baseKey, modKey, hostKey;
208   struct stat status;
209 
210   if (stat(filename, &status)) {
211     BX_PANIC(("Can not stat keymap file '%s'.",filename));
212   }
213 
214   if (!(S_ISREG(status.st_mode))) {
215     BX_PANIC(("Keymap file '%s' is not a file",filename));
216   }
217 
218   if((keymapFile = fopen(filename,"r"))==NULL) {
219     BX_PANIC(("Can not open keymap file '%s'.",filename));
220   }
221 
222   BX_INFO(("Loading keymap from '%s'",filename));
223   init_parse();
224 
225   // Read keymap file one line at a time
226   while(1) {
227     if (get_next_keymap_line (keymapFile,
228           baseSym, modSym, &ascii, hostSym) < 0) { break; }
229 
230     // convert X_KEY_* symbols to values
231     baseKey = convertStringToBXKey(baseSym);
232     modKey = convertStringToBXKey(modSym);
233     hostKey = 0;
234     if (stringToSymbol != NULL)
235       hostKey = stringToSymbol(hostSym);
236 
237     BX_DEBUG(("baseKey='%s' (%d), modSym='%s' (%d), ascii=%d, guisym='%s' (%d)", baseSym, baseKey, modSym, modKey, ascii, hostSym, hostKey));
238 
239     // Check if data is valid
240     if (baseKey==BX_KEYMAP_UNKNOWN) {
241       BX_PANIC (("line %d: unknown BX_KEY constant '%s'",lineCount,baseSym));
242       continue;
243     }
244 
245     if (hostKey==BX_KEYMAP_UNKNOWN) {
246       BX_PANIC (("line %d: unknown host key name '%s' (wrong keymap ?)",lineCount,hostSym));
247       continue;
248     }
249 
250     keymapTable=(BXKeyEntry*)realloc(keymapTable,(keymapCount+1) * sizeof(BXKeyEntry));
251 
252     if (keymapTable==NULL)
253       BX_PANIC(("Can not allocate memory for keymap table."));
254 
255     keymapTable[keymapCount].baseKey=baseKey;
256     keymapTable[keymapCount].modKey=modKey;
257     keymapTable[keymapCount].ascii=ascii;
258     keymapTable[keymapCount].hostKey=hostKey;
259 
260     keymapCount++;
261   }
262 
263   BX_INFO(("Loaded %d symbols",keymapCount));
264 
265   fclose(keymapFile);
266 }
267 
convertStringToBXKey(const char * string)268 Bit32u bx_keymap_c::convertStringToBXKey(const char* string)
269 {
270   // We look through the bx_key_symbol table to find the searched string
271   for (Bit16u i=0; i<BX_KEY_NBKEYS; i++) {
272     if (strcmp(string,bx_key_symbol[i])==0) {
273       return i;
274     }
275   }
276 
277   // Key is not known
278   return BX_KEYMAP_UNKNOWN;
279 }
280 
findHostKey(Bit32u key)281 BXKeyEntry *bx_keymap_c::findHostKey(Bit32u key)
282 {
283   // We look through the keymap table to find the searched key
284   for (Bit16u i=0; i<keymapCount; i++) {
285     if (keymapTable[i].hostKey == key) {
286       BX_DEBUG (("key 0x%02x matches hostKey for entry #%d", key, i));
287       return &keymapTable[i];
288     }
289   }
290   BX_DEBUG(("key %02x matches no entries", key));
291 
292   // Return default
293   return NULL;
294 }
295 
findAsciiChar(Bit8u ch)296 BXKeyEntry *bx_keymap_c::findAsciiChar(Bit8u ch)
297 {
298   BX_DEBUG (("findAsciiChar (0x%02x)", ch));
299 
300   // We look through the keymap table to find the searched key
301   for (Bit16u i=0; i<keymapCount; i++) {
302     if (keymapTable[i].ascii == ch) {
303       BX_DEBUG (("key %02x matches ascii for entry #%d", ch, i));
304       return &keymapTable[i];
305     }
306   }
307   BX_DEBUG (("key 0x%02x matches no entries", ch));
308 
309   // Return default
310   return NULL;
311 }
312 
getBXKeyName(Bit32u key)313 const char *bx_keymap_c::getBXKeyName(Bit32u key)
314 {
315   return bx_key_symbol[key & 0x7fffffff];
316 }
317