2 * virtkeyslib - Routines to support virtual keyboards and handwriting input under X.
3 * Copyright (c) 2000, Merle F. McClelland for CompanionLink
5 * See the files COPYRIGHT and LICENSE for distribution information.
9 /*****************************************************************************
11 ****************************************************************************/
16 #include <X11/Xlibint.h>
17 #include <X11/Xutil.h>
18 #include <X11/cursorfont.h>
19 #include <X11/keysymdef.h>
20 #include <X11/keysym.h>
21 #include <X11/extensions/XTest.h>
23 #include <X11/Xproto.h>
26 // Note that we use this local copy here, because the installed version may or
27 // may not exist, and may not (yet) be up to date.
30 #include "libvirtkeys.h"
34 // We keep these here as static variables so that Tk/TCL routines don't have to deal with them
36 static Display *dpy = NULL;
37 static KeySym *keymap = NULL;
38 static int minKeycode = 0;
39 static int maxKeycode = 0;
40 static int keysymsPerKeycode = 0;
42 // #define USEMODEMODIFIERS 0
43 // #define USEMODIFIERS 1
45 // Set USEMODIFIERS only if there is no XKB entension in the server - modifiers do not work
46 // the same way if it is enabled. For testing on my desktop system, and for general use, I
47 // leave this option off and it works fine on both XKB-enabled and non-enabled servers.
49 // These variables are set when we parse the Modifier Map - we look up the KeySyms
50 // for the modifiers and match them to the specific KeySyms for Alt and Meta (left or right)
51 // and remember the index for use later on.
53 #define noModifierMapIndex (Mod5MapIndex + 1)
54 #define numModifierIndexes (noModifierMapIndex + 1)
55 int ShiftModifierIndex = 0;
56 int AltModifierIndex = 0;
57 int MetaModifierIndex = 0;
59 int ModeModifierIndex = 0;
60 int ModeModifierMask = 0;
62 int ModeSwitchKeyCode = 0;
67 KeyCode modifierTable[numModifierIndexes];
69 static int createModifierTable()
71 XModifierKeymap *modifiers;
81 // Right off the bat, we check to see if the XK_Mode_switch keycode is assigned to a Keycode.
82 // It must be in order for everything to work. Also, this must not be a shifted code, either.
84 kc = XKeysymToKeycode(dpy, XK_Mode_switch);
88 fprintf(stderr, "Mode_switch must be assigned to an unshifted keycode and a modifier.\n");
92 // Now we look up the Keycode, using index 0 (unshifted). If the returned KeySym is not equal
93 // to XK_Mode_switch, then it's an error.
95 if (XKeycodeToKeysym(dpy, kc, 0) != XK_Mode_switch)
97 fprintf(stderr, "Mode_switch must be assigned to an unshifted keycode and a modifier.\n");
101 // We fake-out the lookup of the Mode_switch modifer for now, since the Mode_switch scheme isn't
102 // directly usable when the KXB extension is used. Actually, it doesn't interfere with the use
103 // of Mode_switch, but we can't generate fake events properly to do XLookupString in the
104 // lookup routine (XKB uses different modifier bits for Mode_switch state). So, we worked
105 // around that below, and all we have to do here is save the KeyCode for the XK_Mode_switch
108 ModeSwitchKeyCode = kc;
110 // Get get the list of all modifiers from the server. Note that a single modifier
111 // can be represented by multiple KeyCodes. For example, there are two Shift keys, each with
112 // its own KeyCode. Pressing either indicates the "Shift" modifier state. We just need to
113 // get the first KeyCode for each modifier, and put that KeyCode in our modifierTable for
114 // use when sending the character KeyCodes. Entries in the table returned by XGetModifierMapping
115 // are 0 if the entry indicates no code exists for the entry.
117 modifiers = XGetModifierMapping(dpy);
119 kp = modifiers->modifiermap;
121 // Now, iterate through the list, finding the first non-zero keycode for each
122 // modifier index. If no modifier keycode is found, that's ok, since not all
123 // modifiers exist on all systems. We have a fake modifer representing the "None"
124 // case that immediately follows Mod5MapIndex in the index list and tables. This
125 // doesn't correspond to any real modifier.
127 for (modifier_index = 0; modifier_index < 8; modifier_index++)
129 modifierTable[modifier_index] = 0; // Initialze the table entry
131 // Now, look through the array of modifiers for the first non-zero value
133 for (modifier_key = 0; modifier_key < modifiers->max_keypermod; modifier_key++)
135 int keycode = kp[modifier_index * modifiers->max_keypermod + modifier_key];
139 modifierTable[modifier_index] = keycode;
145 modifierTable[noModifierMapIndex] = 0; // Initialze the "None" entry
147 #ifdef USEMODEMODIFIER
148 // We need to find the modifier associated with the Mode_switch key. - ACTUALLY, DOESN'T WORK with XKB EXTENSION!
150 ModeModifierIndex = -1;
153 // Now determine which of the Mod1 through Mod5 codes correspond to the Alt, Meta, etc. modifiers
155 for (modifier_index = Mod1MapIndex; modifier_index <= Mod5MapIndex; modifier_index++)
157 if (modifierTable[modifier_index])
159 ks = XKeycodeToKeysym(dpy, modifierTable[modifier_index], 0);
165 MetaModifierIndex = modifier_index;
171 AltModifierIndex = modifier_index;
177 ShiftModifierIndex = modifier_index;
180 #ifdef USEMODEMODIFER
183 ModeModifierIndex = modifier_index;
184 ModeModifierMask = (1 << modifier_index); // Create a mask for the ModeModifier
191 #ifdef USEMODEMODIFER
192 // If the Mode_switch key is not assigned to a modifier, we have an error
194 if (ModeModifierIndex == -1)
199 for (modifier_index = ShiftMapIndex; modifier_index < numModifierIndexes; modifier_index++)
200 fprintf(stderr, "Keycode for modifier index %d is %x\n", modifier_index, modifierTable[modifier_index]);
201 fprintf(stderr, "Meta index is %d\n", MetaModifierIndex);
202 fprintf(stderr, "Alt index is %d\n", AltModifierIndex);
203 fprintf(stderr, "Shift index is %d\n", ShiftModifierIndex);
204 #ifdef USEMODEMODIFER
205 fprintf(stderr, "Mode_switch index is %d\n", ModeModifierIndex);
206 fprintf(stderr, "ModeModiferMask is %d\n", ModeModifierMask);
208 fprintf(stderr, "Mode_switch KeyCode is %d\n", ModeSwitchKeyCode);
215 // This routine looks up the KeySym table in the server. If the table contains 4 columns, it is already
216 // set up for use with the Mode_Shift key. If so, we just return it as-is. If not, a copy is made of the
217 // table into a new table that has empty definitions for the extra columns.
219 int loadKeySymTable()
224 // Now we load other variables fromthe server that we use in the other routines.
226 XDisplayKeycodes(dpy, &minKeycode, &maxKeycode);
227 keymap = XGetKeyboardMapping(dpy, minKeycode,
228 (maxKeycode - minKeycode + 1),
236 for (k = 0; k < (maxKeycode - minKeycode + 1); k++)
238 fprintf(stderr, "%-10d", (k + minKeycode));
239 for (n = 0; n < keysymsPerKeycode; n++)
240 fprintf(stderr, "%-10s\t", XKeysymToString(keymap[(k * keysymsPerKeycode + n)]));
241 fprintf(stderr, "\n");
245 // We take the KeySym table that the server gave us, and check to see if it
246 // contains 4 columns (i.e. 4 keysymsPerKeycode). If it does, the routine just
247 // returns, and we will use the table as-is. If it doesn't contain 4 columns, the
248 // routine copies the passed table into one that does, and returns that. That allows
249 // us to utilitize the "Mode_Shift" key to access columns 2 and 3 (of 0..3), and to
250 // assign those columns to any KeySyms that don't currently exist in the table. That
251 // allows the program to autoconfigure the server to include definitions for KeySyms
252 // that the Keyboard config file references, whether or not they exist in the table
253 // before the program runs.
255 // We check to see if the table contains 2 or 4 columns. Any other configuration is
256 // NOT supported! If 4, then just return the table that was returned by the GetKeyboardMapping
259 if (keysymsPerKeycode == 4)
262 if (keysymsPerKeycode == 2)
264 // We have to make a copy of the table by allocating one that has 4 columns instead of
265 // 2, copying the table entries, and then initializing all unused entries to NoSymbol.
269 KeySym *newKeymap = Xmalloc((maxKeycode - minKeycode + 1) * 4 * sizeof(KeySym));
271 for (k = 0; k < (maxKeycode - minKeycode + 1); k++)
273 // Initialize the new entries
275 for (n = 2; n < 4; n++)
276 newKeymap[((k * 4) + n)] = NoSymbol;
278 // Copy over the existing ones
280 for (n = 0; n < keysymsPerKeycode; n++)
281 newKeymap[((k * 4) + n)] = keymap[((k * keysymsPerKeycode) + n)];
284 // Indicate that the new table has 4 entries per Keycode
286 keysymsPerKeycode = 4;
288 // Discard the old keymap
298 fprintf(stderr, "Sorry - server Keyboard map doesn't contain either 2 or 4 KeySyms per Keycode - unsupported!\n");
303 // This routine takes a KeySym, a pointer to a keycodeEntry table array, and an optional labelBuffer
304 // pointer. It looks up the specified KeySym in the table of KeySym&KeyCodes, and stores the proper
305 // sequence of KeyCodes that should be generated, including Modifier keys, to cause the XServer to
306 // generate the KeySym on the other end of the wire. The optional labelBuffer pointer will be
307 // set to point to an allocated buffer containing the ASCII string corresponding to the label on the
308 // key. Not all programs care about this, so passing NULL for the pointer means no label will be
311 int lookupKeyCodeSequence(KeySym ks, struct keycodeEntry *table, char **labelBuffer)
315 int availableKeycode;
325 KeyCode ModeModifier;
328 // If these aren't set, then we aren't initialized
330 if ((dpy == NULL) || (keymap == NULL))
334 assignedKeycode = -1;
336 availableColumn = -1;
337 availableKeycode = -1;
339 // We do two things here - we look to see if the KeySym is already assigned, and if so,
340 // save its position in the table, while at the same time looking for the next available
341 // NoSymbol entry (for possible assignment).
343 for (keycode = 0; ((keycode < (maxKeycode - minKeycode + 1)) && !found); keycode++)
345 for (column = 0; ((column < keysymsPerKeycode) && !found); column++)
347 if (keymap[(keycode * keysymsPerKeycode + column)] == ks)
350 assignedKeycode = keycode;
351 assignedColumn = column;
353 else if (availableColumn == -1)
355 // We only save the first one we find, but only if the unshifted column
356 // is NOT one of the modifier keys. This is extremely important. If we tack-on
357 // definitions to the columns 2 and 3 of a modifier key, expecially Shift,
358 // we will actually send the wrong code if Mode_switch is followed by Shift.
360 if (!IsModifierKey(keymap[(keycode * keysymsPerKeycode + 0)]))
362 if (keymap[(keycode * keysymsPerKeycode + column)] == NoSymbol)
364 availableColumn = column;
365 availableKeycode = keycode;
376 fprintf(stderr, "KeySym not found - will assign at Keycode %d, Column %d\n",
377 (availableKeycode + minKeycode), availableColumn);
379 // We assign the KeySym to the next available NoSymbol entry, assuming there
380 // is one! We can tell because availableColumn will not be -1 if we found an entry
383 if (availableColumn == -1)
386 // Ok. We can assign the KeySym to the entry in the table at the available Column and Keycode.
387 // We must update the server when we do this, so we can look up the string associated with the
388 // assigned KeySym. This can cause a lot of server thrashing the first time it's done.
390 keymap[(availableKeycode * keysymsPerKeycode + availableColumn)] = ks;
393 // We point to only the row that we are changing, and say that we are chaing just one. Note that
394 // the keycode index passed must be based on minKeycode.
396 XChangeKeyboardMapping(dpy, (availableKeycode + minKeycode),
397 keysymsPerKeycode, &keymap[(availableKeycode * keysymsPerKeycode)], 1);
399 assignedKeycode = availableKeycode;
400 assignedColumn = availableColumn;
404 fprintf(stderr, "KeySym %x found at Keycode %d, Column %d\n", (unsigned int)ks, (assignedKeycode + minKeycode), assignedColumn);
407 // If we get here, we assigned it. Now set up the table with the appropriate
412 ModeModifier = modifierTable[ModeModifierIndex];
414 ModeModifier = ModeSwitchKeyCode;
417 switch (assignedColumn)
419 case 0: // Unshifted case
421 table[0].keycode = (assignedKeycode + minKeycode); // Store the keycode
422 table[0].direction = keyDownUp; // Store the key direction (in this case, Down and Up)
423 table[1].keycode = 0; // Store the sequence terminator
427 case 1: // Shifted case - we have to simulate pressing down the shift modifier,
428 // then the character key, then releasing shift
430 table[0].keycode = modifierTable[ShiftMapIndex];// Store the keycode for the shift key
431 table[0].direction = keyDown; // Store the key direction (in this case, just Down)
433 table[1].keycode = (assignedKeycode + minKeycode); // Store the keycode
434 table[1].direction = keyDownUp; // Store the key direction (in this case, Down and Up)
436 table[2].keycode = modifierTable[ShiftMapIndex];// Store the keycode for the shift key
437 table[2].direction = keyUp; // Store the key direction (in this case, just Up)
439 table[3].keycode = 0; // Store the sequence terminator
441 #ifdef USEMODEMODIFER
442 modifiers |= ShiftMask; // Add-in the modifier bit for the Mode_switch modifier
446 case 2: // Unshifted Mode_switch case
447 table[0].keycode = ModeModifier; // Store the keycode for the Mode switch code
448 table[0].direction = keyDown; // Store the key direction (in this case, Down)
450 table[1].keycode = (assignedKeycode + minKeycode); // Store the keycode
451 table[1].direction = keyDownUp; // Store the key direction (in this case, Down and Up)
453 table[2].keycode = ModeModifier; // Store the keycode for the Mode switch code
454 table[2].direction = keyUp; // Store the key direction (in this case, Up)
456 table[3].keycode = 0; // Store the sequence terminator
458 modifiers |= ModeModifierMask; // Add-in the modifier bit for the Mode_switch modifier
464 // Note that the order is important here - we first do the shift, so that the interpretation
465 // of the shift key is not impacted by the ModeModifier. This accounts for key maps where the
466 // interpretation of the shift key is not defined when Mode_switch is pressed first.
468 table[0].keycode = modifierTable[ShiftMapIndex];// Store the keycode for the shift key
469 table[0].direction = keyDown; // Store the key direction (in this case, just Down)
471 table[1].keycode = ModeModifier; // Store the keycode for the Mode switch code
472 table[1].direction = keyDown; // Store the key direction (in this case, Down)
474 table[2].keycode = (assignedKeycode + minKeycode); // Store the keycode
475 table[2].direction = keyDownUp; // Store the key direction (in this case, Down and Up)
477 table[3].keycode = ModeModifier; // Store the keycode for the Mode switch code
478 table[3].direction = keyUp; // Store the key direction (in this case, Up)
480 table[4].keycode = modifierTable[ShiftMapIndex];// Store the keycode for the shift key
481 table[4].direction = keyUp; // Store the key direction (in this case, just Up)
483 table[5].keycode = 0; // Store the sequence terminator
485 #ifdef USEMODEMODIFER
486 modifiers |= (ShiftMask | ModeModifierMask); // Add-in the modifier bit for the Mode_switch and Shift modifiers
492 // If the server is compiled with XKB, this does not work!!! The XKB extension uses additional state bits for
493 // Mode_switch, and the use of the ModeModifierMask doesn't work. So, we just interpret the KeySym directly for
496 #ifdef USEMODEMODIFER
497 // Now look up the string that represents the keycode in the correct state, taking
498 // into account the Shift and Mode_switch modifiers (set above).
500 fakeEvent.xkey.type = KeyPress;
501 fakeEvent.xkey.display = dpy;
502 fakeEvent.xkey.time = CurrentTime;
503 fakeEvent.xkey.x = fakeEvent.xkey.y = 0;
504 fakeEvent.xkey.x_root = fakeEvent.xkey.y_root = 0;
505 fakeEvent.xkey.state = modifiers;
506 fakeEvent.xkey.keycode = (assignedKeycode + minKeycode);
510 *labelBuffer = malloc(MAXLABELLEN+1);
512 len = XLookupString((XKeyEvent *)&fakeEvent, *labelBuffer, MAXLABELLEN, NULL, NULL);
514 (*labelBuffer)[len] = '\0';
517 fprintf(stderr, "modifiers = %x, keycode = %d, len = %d, labelBuffer = '%s'\n",
518 modifiers, fakeEvent.xkey.keycode, len, (len > 0 ? *labelBuffer : "(null)"));
523 *labelBuffer = malloc(2);
524 if ((ks & 0xff00) == 0xff00)
525 (*labelBuffer)[0] = ks;
527 (*labelBuffer)[0] = ks & 0xff;
528 (*labelBuffer)[1] = '\0';
530 fprintf(stderr, "labelBuffer = '%s'\n", *labelBuffer);
537 // Routine to test for and set up the XTest extension. Returns FALSE if the set up fails or if the extension
548 // does the display have the Xtest-extension?
550 if (!XTestQueryExtension(dpy, &event, &error, &major, &minor))
552 // nope, extension not supported
554 fprintf(stderr, "XTest extension not supported on server \"%s\"\n.", DisplayString(dpy));
570 // discard and even flush all events on the remote display
578 void sendKeySequence(struct keycodeEntry *entries, int controlMode, int metaMode, int altMode, int shiftMode)
586 // The Control, Meta, and Alt modifiers are set and unset outside the scope of the
587 // sequences in the table. The table sequences determine which column in the key table
588 // is selected, whereas the control, meta, and alt keys do not (they just set modifier
589 // bits in the receiving application. Thus, we press and unpress these modifiers before
590 // and after sending the sequence.
593 sendKey(modifierTable[ControlMapIndex], keyDown); // Send a down event for the control modifier key
596 sendKey(modifierTable[MetaModifierIndex], keyDown); // Send a down event for the meta modifier key
599 sendKey(modifierTable[AltModifierIndex], keyDown); // Send a down event for the alt modifier key
602 sendKey(modifierTable[ShiftModifierIndex], keyDown); // Send a down event for the shift modifier key
604 while ((kc = entries[s].keycode))
606 enum keyDirection kd = entries[s].direction;
612 // Now send the corresponding up events for the modifiers
615 sendKey(modifierTable[ControlMapIndex], keyUp); // Send an up event for the control modifier key
618 sendKey(modifierTable[MetaModifierIndex], keyUp); // Send an up event for the meta modifier key
621 sendKey(modifierTable[AltModifierIndex], keyUp); // Send an up event for the alt modifier key
624 sendKey(modifierTable[ShiftModifierIndex], keyUp); // Send an up event for the shift modifier key
628 void sendKey(KeyCode character, enum keyDirection keydirection)
633 switch (keydirection)
638 fprintf(stderr, "sending %04x key down\n", character);
640 //XTestFakeKeyEvent(dpy, (unsigned int) character, TRUE, 0);
641 //bthid_send(character, 1);
647 fprintf(stderr, "sending %04x key up\n", character);
649 //XTestFakeKeyEvent(dpy, (unsigned int) character, FALSE, 0);
650 //bthid_send(character, 0);
656 fprintf(stderr, "sending %04x key down\n", character);
658 //XTestFakeKeyEvent(dpy, (unsigned int) character, TRUE, 0);
659 //bthid_send(character, 1);
662 fprintf(stderr, "sending %04x key up\n", character);
664 //XTestFakeKeyEvent(dpy, (unsigned int) character, FALSE, 0);
665 //bthid_send(character, 0);
671 // This routine does the basic setup needed for loading X server key tables and such
674 int setupKeyboardVariables(Display *display)
676 // If the dpy variable is set, we've already been called once. Just return.
681 // Get the Keyboard Mapping table. This is indexed by keycode in one
682 // direction, and by the modifier index in the other. The loadKeyboardTable
683 // routine will take these two tables and convert the config file into a lookup
684 // table between stroke sequences and keycode/modifier keycode pairs.
686 // We set up a local static variable used by all of these routines. It is done this
687 // way for easy integration into Tk/TCL code, which quite often has no notion of
688 // the X display variable.
692 // Call to test for and set up the XTest extension
695 fprintf(stderr, "Setting up XTest\n");
697 if (setupXTest() == FALSE)
700 // Load the modifer map
703 fprintf(stderr, "Creating modifier table\n");
705 if (createModifierTable() == FALSE)
709 fprintf(stderr, "Loading KeySym table\n");
711 if (loadKeySymTable() == FALSE)