1 /* darwin-old-keymap.c -- ugly code we need to keep around for now
2
3 Copyright (c) 2001-2002 Torrey T. Lyons. All Rights Reserved.
4
5 The code to parse the Darwin keymap is derived from dumpkeymap.c
6 by Eric Sunshine, which includes the following copyright:
7
8 Copyright (C) 1999,2000 by Eric Sunshine <sunshine@sunshineco.com>
9 All rights reserved.
10
11 Redistribution and use in source and binary forms, with or without
12 modification, are permitted provided that the following conditions are met:
13
14 1. Redistributions of source code must retain the above copyright
15 notice, this list of conditions and the following disclaimer.
16 2. Redistributions in binary form must reproduce the above copyright
17 notice, this list of conditions and the following disclaimer in the
18 documentation and/or other materials provided with the distribution.
19 3. The name of the author may not be used to endorse or promote products
20 derived from this software without specific prior written permission.
21
22 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
25 NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
32
33 #include "darwin.h"
34 #include "darwin-keyboard.h"
35 #include <errno.h>
36 #include <sys/stat.h>
37 #include <drivers/event_status_driver.h>
38 #include <IOKit/hidsystem/ev_keymap.h>
39 #include <architecture/byte_order.h> // For the NXSwap*
40
41 #define XK_TECHNICAL // needed to get XK_Escape
42 #define XK_PUBLISHING
43 #include "keysym.h"
44
45 static FILE *fref = NULL;
46 static char *inBuffer = NULL;
47
48 // FIXME: It would be nice to support some of the extra keys in XF86keysym.h,
49 // at least the volume controls that now ship on every Apple keyboard.
50
51 #define UK(a) NoSymbol // unknown symbol
52
53 static KeySym const next_to_x[256] = {
54 NoSymbol, NoSymbol, NoSymbol, XK_KP_Enter,
55 NoSymbol, NoSymbol, NoSymbol, NoSymbol,
56 XK_BackSpace, XK_Tab, XK_Linefeed, NoSymbol,
57 NoSymbol, XK_Return, NoSymbol, NoSymbol,
58 NoSymbol, NoSymbol, NoSymbol, NoSymbol,
59 NoSymbol, NoSymbol, NoSymbol, NoSymbol,
60 NoSymbol, NoSymbol, NoSymbol, XK_Escape,
61 NoSymbol, NoSymbol, NoSymbol, NoSymbol,
62 XK_space, XK_exclam, XK_quotedbl, XK_numbersign,
63 XK_dollar, XK_percent, XK_ampersand, XK_apostrophe,
64 XK_parenleft, XK_parenright, XK_asterisk, XK_plus,
65 XK_comma, XK_minus, XK_period, XK_slash,
66 XK_0, XK_1, XK_2, XK_3,
67 XK_4, XK_5, XK_6, XK_7,
68 XK_8, XK_9, XK_colon, XK_semicolon,
69 XK_less, XK_equal, XK_greater, XK_question,
70 XK_at, XK_A, XK_B, XK_C,
71 XK_D, XK_E, XK_F, XK_G,
72 XK_H, XK_I, XK_J, XK_K,
73 XK_L, XK_M, XK_N, XK_O,
74 XK_P, XK_Q, XK_R, XK_S,
75 XK_T, XK_U, XK_V, XK_W,
76 XK_X, XK_Y, XK_Z, XK_bracketleft,
77 XK_backslash, XK_bracketright,XK_asciicircum, XK_underscore,
78 XK_grave, XK_a, XK_b, XK_c,
79 XK_d, XK_e, XK_f, XK_g,
80 XK_h, XK_i, XK_j, XK_k,
81 XK_l, XK_m, XK_n, XK_o,
82 XK_p, XK_q, XK_r, XK_s,
83 XK_t, XK_u, XK_v, XK_w,
84 XK_x, XK_y, XK_z, XK_braceleft,
85 XK_bar, XK_braceright, XK_asciitilde, XK_BackSpace,
86 // 128
87 NoSymbol, XK_Agrave, XK_Aacute, XK_Acircumflex,
88 XK_Atilde, XK_Adiaeresis, XK_Aring, XK_Ccedilla,
89 XK_Egrave, XK_Eacute, XK_Ecircumflex, XK_Ediaeresis,
90 XK_Igrave, XK_Iacute, XK_Icircumflex, XK_Idiaeresis,
91 // 144
92 XK_ETH, XK_Ntilde, XK_Ograve, XK_Oacute,
93 XK_Ocircumflex, XK_Otilde, XK_Odiaeresis, XK_Ugrave,
94 XK_Uacute, XK_Ucircumflex, XK_Udiaeresis, XK_Yacute,
95 XK_THORN, XK_mu, XK_multiply, XK_division,
96 // 160
97 XK_copyright, XK_exclamdown, XK_cent, XK_sterling,
98 UK(fraction), XK_yen, UK(fhook), XK_section,
99 XK_currency, XK_rightsinglequotemark,
100 XK_leftdoublequotemark,
101 XK_guillemotleft,
102 XK_leftanglebracket,
103 XK_rightanglebracket,
104 UK(filigature), UK(flligature),
105 // 176
106 XK_registered, XK_endash, XK_dagger, XK_doubledagger,
107 XK_periodcentered,XK_brokenbar, XK_paragraph, UK(bullet),
108 XK_singlelowquotemark,
109 XK_doublelowquotemark,
110 XK_rightdoublequotemark,
111 XK_guillemotright,
112 XK_ellipsis, UK(permille), XK_notsign, XK_questiondown,
113 // 192
114 XK_onesuperior, XK_dead_grave, XK_dead_acute, XK_dead_circumflex,
115 XK_dead_tilde, XK_dead_macron, XK_dead_breve, XK_dead_abovedot,
116 XK_dead_diaeresis,
117 XK_twosuperior, XK_dead_abovering,
118 XK_dead_cedilla,
119 XK_threesuperior,
120 XK_dead_doubleacute,
121 XK_dead_ogonek, XK_dead_caron,
122 // 208
123 XK_emdash, XK_plusminus, XK_onequarter, XK_onehalf,
124 XK_threequarters,
125 XK_agrave, XK_aacute, XK_acircumflex,
126 XK_atilde, XK_adiaeresis, XK_aring, XK_ccedilla,
127 XK_egrave, XK_eacute, XK_ecircumflex, XK_ediaeresis,
128 // 224
129 XK_igrave, XK_AE, XK_iacute, XK_ordfeminine,
130 XK_icircumflex, XK_idiaeresis, XK_eth, XK_ntilde,
131 XK_Lstroke, XK_Ooblique, XK_OE, XK_masculine,
132 XK_ograve, XK_oacute, XK_ocircumflex, XK_otilde,
133 // 240
134 XK_odiaeresis, XK_ae, XK_ugrave, XK_uacute,
135 XK_ucircumflex, XK_idotless, XK_udiaeresis, XK_ygrave,
136 XK_lstroke, XK_ooblique, XK_oe, XK_ssharp,
137 XK_thorn, XK_ydiaeresis, NoSymbol, NoSymbol,
138 };
139
140 #define MIN_SYMBOL 0xAC
141 static KeySym const symbol_to_x[] = {
142 XK_Left, XK_Up, XK_Right, XK_Down
143 };
144 int const NUM_SYMBOL = sizeof(symbol_to_x) / sizeof(symbol_to_x[0]);
145
146 #define MIN_FUNCKEY 0x20
147 static KeySym const funckey_to_x[] = {
148 XK_F1, XK_F2, XK_F3, XK_F4,
149 XK_F5, XK_F6, XK_F7, XK_F8,
150 XK_F9, XK_F10, XK_F11, XK_F12,
151 XK_Insert, XK_Delete, XK_Home, XK_End,
152 XK_Page_Up, XK_Page_Down, XK_F13, XK_F14,
153 XK_F15
154 };
155 int const NUM_FUNCKEY = sizeof(funckey_to_x) / sizeof(funckey_to_x[0]);
156
157 typedef struct {
158 KeySym normalSym;
159 KeySym keypadSym;
160 } darwinKeyPad_t;
161
162 static darwinKeyPad_t const normal_to_keypad[] = {
163 { XK_0, XK_KP_0 },
164 { XK_1, XK_KP_1 },
165 { XK_2, XK_KP_2 },
166 { XK_3, XK_KP_3 },
167 { XK_4, XK_KP_4 },
168 { XK_5, XK_KP_5 },
169 { XK_6, XK_KP_6 },
170 { XK_7, XK_KP_7 },
171 { XK_8, XK_KP_8 },
172 { XK_9, XK_KP_9 },
173 { XK_equal, XK_KP_Equal },
174 { XK_asterisk, XK_KP_Multiply },
175 { XK_plus, XK_KP_Add },
176 { XK_comma, XK_KP_Separator },
177 { XK_minus, XK_KP_Subtract },
178 { XK_period, XK_KP_Decimal },
179 { XK_slash, XK_KP_Divide }
180 };
181 int const NUM_KEYPAD = sizeof(normal_to_keypad) / sizeof(normal_to_keypad[0]);
182
183
184 //-----------------------------------------------------------------------------
185 // Data Stream Object
186 // Can be configured to treat embedded "numbers" as being composed of
187 // either 1, 2, or 4 bytes, apiece.
188 //-----------------------------------------------------------------------------
189 typedef struct _DataStream
190 {
191 unsigned char const *data;
192 unsigned char const *data_end;
193 short number_size; // Size in bytes of a "number" in the stream.
194 } DataStream;
195
new_data_stream(unsigned char const * data,int size)196 static DataStream* new_data_stream( unsigned char const* data, int size )
197 {
198 DataStream* s = (DataStream*)xalloc( sizeof(DataStream) );
199 s->data = data;
200 s->data_end = data + size;
201 s->number_size = 1; // Default to byte-sized numbers.
202 return s;
203 }
204
destroy_data_stream(DataStream * s)205 static void destroy_data_stream( DataStream* s )
206 {
207 xfree(s);
208 }
209
get_byte(DataStream * s)210 static unsigned char get_byte( DataStream* s )
211 {
212 assert(s->data + 1 <= s->data_end);
213 return *s->data++;
214 }
215
get_word(DataStream * s)216 static short get_word( DataStream* s )
217 {
218 short hi, lo;
219 assert(s->data + 2 <= s->data_end);
220 hi = *s->data++;
221 lo = *s->data++;
222 return ((hi << 8) | lo);
223 }
224
get_dword(DataStream * s)225 static int get_dword( DataStream* s )
226 {
227 int b1, b2, b3, b4;
228 assert(s->data + 4 <= s->data_end);
229 b4 = *s->data++;
230 b3 = *s->data++;
231 b2 = *s->data++;
232 b1 = *s->data++;
233 return ((b4 << 24) | (b3 << 16) | (b2 << 8) | b1);
234 }
235
get_number(DataStream * s)236 static int get_number( DataStream* s )
237 {
238 switch (s->number_size) {
239 case 4: return get_dword(s);
240 case 2: return get_word(s);
241 default: return get_byte(s);
242 }
243 }
244
245
246 //-----------------------------------------------------------------------------
247 // Utility functions to help parse Darwin keymap
248 //-----------------------------------------------------------------------------
249
250 /*
251 * bits_set
252 * Calculate number of bits set in the modifier mask.
253 */
bits_set(short mask)254 static short bits_set( short mask )
255 {
256 short n = 0;
257
258 for ( ; mask != 0; mask >>= 1)
259 if ((mask & 0x01) != 0)
260 n++;
261 return n;
262 }
263
264 /*
265 * parse_next_char_code
266 * Read the next character code from the Darwin keymapping
267 * and write it to the X keymap.
268 */
parse_next_char_code(DataStream * s,KeySym * k)269 static void parse_next_char_code(
270 DataStream *s,
271 KeySym *k )
272 {
273 const short charSet = get_number(s);
274 const short charCode = get_number(s);
275
276 if (charSet == 0) { // ascii character
277 if (charCode >= 0 && charCode < 256)
278 *k = next_to_x[charCode];
279 } else if (charSet == 0x01) { // symbol character
280 if (charCode >= MIN_SYMBOL &&
281 charCode <= MIN_SYMBOL + NUM_SYMBOL)
282 *k = symbol_to_x[charCode - MIN_SYMBOL];
283 } else if (charSet == 0xFE) { // function key
284 if (charCode >= MIN_FUNCKEY &&
285 charCode <= MIN_FUNCKEY + NUM_FUNCKEY)
286 *k = funckey_to_x[charCode - MIN_FUNCKEY];
287 }
288 }
289
290 /*
291 * DarwinReadKeymapFile
292 * Read the appropriate keymapping from a keymapping file.
293 */
_DarwinReadKeymapFile(NXKeyMapping * keyMap)294 static Bool _DarwinReadKeymapFile(
295 NXKeyMapping *keyMap)
296 {
297 struct stat st;
298 NXEventSystemDevice info[20];
299 int interface = 0, handler_id = 0;
300 int map_interface, map_handler_id, map_size = 0;
301 unsigned int i, size;
302 int *bufferEnd;
303 union km_tag {
304 int *intP;
305 char *charP;
306 } km;
307 char *filename;
308
309 filename = DarwinFindLibraryFile (darwinKeymapFile, "Keyboards");
310 if (filename == NULL) {
311 FatalError("Could not find keymapping file %s.\n", darwinKeymapFile);
312 return FALSE;
313 }
314
315 fref = fopen( filename, "rb" );
316 xfree (filename);
317
318 if (fref == NULL) {
319 ErrorF("Unable to open keymapping file '%s' (errno %d).\n",
320 darwinKeymapFile, errno);
321 return FALSE;
322 }
323 if (fstat(fileno(fref), &st) == -1) {
324 ErrorF("Could not stat keymapping file '%s' (errno %d).\n",
325 darwinKeymapFile, errno);
326 return FALSE;
327 }
328
329 // check to make sure we don't crash later
330 if (st.st_size <= 16*sizeof(int)) {
331 ErrorF("Keymapping file '%s' is invalid (too small).\n",
332 darwinKeymapFile);
333 return FALSE;
334 }
335
336 inBuffer = (char*) xalloc( st.st_size );
337 bufferEnd = (int *) (inBuffer + st.st_size);
338 if (fread(inBuffer, st.st_size, 1, fref) != 1) {
339 ErrorF("Could not read %qd bytes from keymapping file '%s' (errno %d).\n",
340 st.st_size, darwinKeymapFile, errno);
341 return FALSE;
342 }
343
344 if (strncmp( inBuffer, "KYM1", 4 ) == 0) {
345 // Magic number OK.
346 } else if (strncmp( inBuffer, "KYMP", 4 ) == 0) {
347 ErrorF("Keymapping file '%s' is intended for use with the original NeXT keyboards and cannot be used by XDarwin.\n", darwinKeymapFile);
348 return FALSE;
349 } else {
350 ErrorF("Keymapping file '%s' has a bad magic number and cannot be used by XDarwin.\n", darwinKeymapFile);
351 return FALSE;
352 }
353
354 // find the keyboard interface and handler id
355 {NXEventHandle hid;
356 NXEventSystemInfoType info_type;
357
358 size = sizeof( info ) / sizeof( int );
359 hid = NXOpenEventStatus ();
360 info_type = NXEventSystemInfo (hid, NX_EVS_DEVICE_INFO,
361 (int *) info, &size);
362 NXCloseEventStatus (hid);
363 if (!info_type) {
364 ErrorF("Error reading event status driver info.\n");
365 return FALSE;
366 }}
367
368 size = size * sizeof( int ) / sizeof( info[0] );
369 for( i = 0; i < size; i++) {
370 if (info[i].dev_type == NX_EVS_DEVICE_TYPE_KEYBOARD) {
371 Bool hasInterface = FALSE;
372 Bool hasMatch = FALSE;
373
374 interface = info[i].interface;
375 handler_id = info[i].id;
376
377 // Find an appropriate keymapping:
378 // The first time we try to match both interface and handler_id.
379 // If we can't match both, we take the first match for interface.
380
381 do {
382 km.charP = inBuffer;
383 km.intP++;
384 while (km.intP+3 < bufferEnd) {
385 map_interface = NXSwapBigIntToHost(*(km.intP++));
386 map_handler_id = NXSwapBigIntToHost(*(km.intP++));
387 map_size = NXSwapBigIntToHost(*(km.intP++));
388 if (map_interface == interface) {
389 if (map_handler_id == handler_id || hasInterface) {
390 hasMatch = TRUE;
391 break;
392 } else {
393 hasInterface = TRUE;
394 }
395 }
396 km.charP += map_size;
397 }
398 } while (hasInterface && !hasMatch);
399
400 if (hasMatch) {
401 // fill in NXKeyMapping structure
402 keyMap->size = map_size;
403 keyMap->mapping = (char*) xalloc(map_size);
404 memcpy(keyMap->mapping, km.charP, map_size);
405 return TRUE;
406 }
407 } // if dev_id == keyboard device
408 } // foreach info struct
409
410 // The keymapping file didn't match any of the info structs
411 // returned by NXEventSystemInfo.
412 ErrorF("Keymapping file '%s' did not contain appropriate keyboard interface.\n", darwinKeymapFile);
413 return FALSE;
414 }
415
DarwinReadKeymapFile(NXKeyMapping * keyMap)416 static Bool DarwinReadKeymapFile(NXKeyMapping *keyMap)
417 {
418 Bool ret;
419
420 ret = _DarwinReadKeymapFile (keyMap);
421
422 if (inBuffer != NULL)
423 xfree (inBuffer);
424 if (fref != NULL)
425 fclose (fref);
426
427 return ret;
428 }
429
DarwinParseKeymapFile(darwin_keyboard_info * info)430 int DarwinParseKeymapFile (darwin_keyboard_info *info)
431 {
432 short numMods, numKeys, numPadKeys = 0;
433 NXKeyMapping keyMap;
434 DataStream *keyMapStream;
435 unsigned char const *numPadStart = 0;
436 Bool haveKeymap;
437 int i;
438 KeySym *k;
439
440 if (darwinKeymapFile == NULL)
441 return FALSE;
442
443 haveKeymap = DarwinReadKeymapFile(&keyMap);
444 if (!haveKeymap) {
445 ErrorF("Reverting to system keymapping.\n");
446 return FALSE;
447 }
448
449 keyMapStream = new_data_stream( (unsigned char const*)keyMap.mapping,
450 keyMap.size );
451
452 // check the type of map
453 if (get_word(keyMapStream)) {
454 keyMapStream->number_size = 2;
455 ErrorF("Current 16-bit keymapping may not be interpreted correctly.\n");
456 }
457
458 // Compute the modifier map and
459 // insert X modifier KeySyms into keyboard map.
460 // Store modifier keycodes in modifierKeycodes.
461 numMods = get_number(keyMapStream);
462 while (numMods-- > 0) {
463 int left = 1; // first keycode is left
464 short const charCode = get_number(keyMapStream);
465 short numKeyCodes = get_number(keyMapStream);
466
467 // This is just a marker, not a real modifier.
468 // Store numeric keypad keys for later.
469 if (charCode == NX_MODIFIERKEY_NUMERICPAD) {
470 numPadStart = keyMapStream->data;
471 numPadKeys = numKeyCodes;
472 }
473
474 while (numKeyCodes-- > 0) {
475 const short keyCode = get_number(keyMapStream);
476 if (charCode != NX_MODIFIERKEY_NUMERICPAD) {
477 switch (charCode) {
478 case NX_MODIFIERKEY_ALPHALOCK:
479 info->key_map[keyCode * GLYPHS_PER_KEY] = XK_Caps_Lock;
480 break;
481 case NX_MODIFIERKEY_SHIFT:
482 info->key_map[keyCode * GLYPHS_PER_KEY] =
483 (left ? XK_Shift_L : XK_Shift_R);
484 break;
485 case NX_MODIFIERKEY_CONTROL:
486 info->key_map[keyCode * GLYPHS_PER_KEY] =
487 (left ? XK_Control_L : XK_Control_R);
488 break;
489 case NX_MODIFIERKEY_ALTERNATE:
490 info->key_map[keyCode * GLYPHS_PER_KEY] =
491 (left ? XK_Alt_L : XK_Alt_R);
492 break;
493 case NX_MODIFIERKEY_COMMAND:
494 info->key_map[keyCode * GLYPHS_PER_KEY] =
495 (left ? XK_Meta_L : XK_Meta_R);
496 break;
497 case NX_MODIFIERKEY_SECONDARYFN:
498 info->key_map[keyCode * GLYPHS_PER_KEY] =
499 (left ? XK_Control_L : XK_Control_R);
500 break;
501 case NX_MODIFIERKEY_HELP:
502 info->key_map[keyCode * GLYPHS_PER_KEY] = XK_Help;
503 break;
504 }
505 }
506 left = 0;
507 }
508 }
509
510 // Convert the Darwin keyboard map to an X keyboard map.
511 // A key can have a different character code for each combination of
512 // modifiers. We currently ignore all modifier combinations except
513 // those with Shift, AlphaLock, and Alt.
514 numKeys = get_number(keyMapStream);
515 for (i = 0, k = info->key_map; i < numKeys; i++, k += GLYPHS_PER_KEY) {
516 short const charGenMask = get_number(keyMapStream);
517 if (charGenMask != 0xFF) { // is key bound?
518 short numKeyCodes = 1 << bits_set(charGenMask);
519
520 // Record unmodified case
521 parse_next_char_code( keyMapStream, k );
522 numKeyCodes--;
523
524 // If AlphaLock and Shift modifiers produce different codes,
525 // we record the Shift case since X handles AlphaLock.
526 if (charGenMask & 0x01) { // AlphaLock
527 parse_next_char_code( keyMapStream, k+1 );
528 numKeyCodes--;
529 }
530
531 if (charGenMask & 0x02) { // Shift
532 parse_next_char_code( keyMapStream, k+1 );
533 numKeyCodes--;
534
535 if (charGenMask & 0x01) { // Shift-AlphaLock
536 get_number(keyMapStream); get_number(keyMapStream);
537 numKeyCodes--;
538 }
539 }
540
541 // Skip the Control cases
542 if (charGenMask & 0x04) { // Control
543 get_number(keyMapStream); get_number(keyMapStream);
544 numKeyCodes--;
545
546 if (charGenMask & 0x01) { // Control-AlphaLock
547 get_number(keyMapStream); get_number(keyMapStream);
548 numKeyCodes--;
549 }
550
551 if (charGenMask & 0x02) { // Control-Shift
552 get_number(keyMapStream); get_number(keyMapStream);
553 numKeyCodes--;
554
555 if (charGenMask & 0x01) { // Shift-Control-AlphaLock
556 get_number(keyMapStream); get_number(keyMapStream);
557 numKeyCodes--;
558 }
559 }
560 }
561
562 // Process Alt cases
563 if (charGenMask & 0x08) { // Alt
564 parse_next_char_code( keyMapStream, k+2 );
565 numKeyCodes--;
566
567 if (charGenMask & 0x01) { // Alt-AlphaLock
568 parse_next_char_code( keyMapStream, k+3 );
569 numKeyCodes--;
570 }
571
572 if (charGenMask & 0x02) { // Alt-Shift
573 parse_next_char_code( keyMapStream, k+3 );
574 numKeyCodes--;
575
576 if (charGenMask & 0x01) { // Alt-Shift-AlphaLock
577 get_number(keyMapStream); get_number(keyMapStream);
578 numKeyCodes--;
579 }
580 }
581 }
582
583 while (numKeyCodes-- > 0) {
584 get_number(keyMapStream); get_number(keyMapStream);
585 }
586
587 if (k[3] == k[2]) k[3] = NoSymbol;
588 if (k[2] == k[1]) k[2] = NoSymbol;
589 if (k[1] == k[0]) k[1] = NoSymbol;
590 if (k[0] == k[2] && k[1] == k[3]) k[2] = k[3] = NoSymbol;
591 }
592 }
593
594 // Now we have to go back through the list of keycodes that are on the
595 // numeric keypad and update the X keymap.
596 keyMapStream->data = numPadStart;
597 while(numPadKeys-- > 0) {
598 const short keyCode = get_number(keyMapStream);
599 k = &info->key_map[keyCode * GLYPHS_PER_KEY];
600 for (i = 0; i < NUM_KEYPAD; i++) {
601 if (*k == normal_to_keypad[i].normalSym) {
602 k[0] = normal_to_keypad[i].keypadSym;
603 break;
604 }
605 }
606 }
607
608 // free Darwin keyboard map
609 destroy_data_stream( keyMapStream );
610 xfree( keyMap.mapping );
611
612 return TRUE;
613 }
614