diff options
Diffstat (limited to 'x11vnc/keyboard.c')
-rw-r--r-- | x11vnc/keyboard.c | 2817 |
1 files changed, 2817 insertions, 0 deletions
diff --git a/x11vnc/keyboard.c b/x11vnc/keyboard.c new file mode 100644 index 0000000..1fba4c9 --- /dev/null +++ b/x11vnc/keyboard.c @@ -0,0 +1,2817 @@ +/* -- keyboard.c -- */ + +#include "x11vnc.h" +#include "xwrappers.h" +#include "xrecord.h" +#include "xinerama.h" +#include "pointer.h" +#include "userinput.h" +#include "win_utils.h" +#include "rates.h" +#include "cleanup.h" +#include "allowed_input_t.h" + +void get_keystate(int *keystate); +void clear_modifiers(int init); +int track_mod_state(rfbKeySym keysym, rfbBool down, rfbBool set); +void clear_keys(void); +int get_autorepeat_state(void); +int get_initial_autorepeat_state(void); +void autorepeat(int restore, int bequiet); +void check_add_keysyms(void); +int add_keysym(KeySym keysym); +void delete_added_keycodes(int bequiet); +void initialize_remap(char *infile); +int sloppy_key_check(int key, rfbBool down, rfbKeySym keysym, int *new); +void switch_to_xkb_if_better(void); +char *short_kmb(char *str); +void initialize_allowed_input(void); +void initialize_modtweak(void); +void initialize_keyboard_and_pointer(void); +void get_allowed_input(rfbClientPtr client, allowed_input_t *input); +double typing_rate(double time_window, int *repeating); +int skip_cr_when_scaling(char *mode); +void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client); + + +static void delete_keycode(KeyCode kc, int bequiet); +static int count_added_keycodes(void); +static void add_remap(char *line); +static void add_dead_keysyms(char *str); +static void initialize_xkb_modtweak(void); +static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, + rfbClientPtr client); +static void tweak_mod(signed char mod, rfbBool down); +static void modifier_tweak_keyboard(rfbBool down, rfbKeySym keysym, + rfbClientPtr client); +static void pipe_keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client); + + +/* + * Routine to retreive current state keyboard. 1 means down, 0 up. + */ +void get_keystate(int *keystate) { + int i, k; + char keys[32]; + + /* n.b. caller decides to X_LOCK or not. */ + XQueryKeymap(dpy, keys); + for (i=0; i<32; i++) { + char c = keys[i]; + + for (k=0; k < 8; k++) { + if (c & 0x1) { + keystate[8*i + k] = 1; + } else { + keystate[8*i + k] = 0; + } + c = c >> 1; + } + } +} + +/* + * Try to KeyRelease any non-Lock modifiers that are down. + */ +void clear_modifiers(int init) { + static KeyCode keycodes[256]; + static KeySym keysyms[256]; + static char *keystrs[256]; + static int kcount = 0, first = 1; + int keystate[256]; + int i, j, minkey, maxkey, syms_per_keycode; + KeySym *keymap; + KeySym keysym; + KeyCode keycode; + + /* n.b. caller decides to X_LOCK or not. */ + if (first) { + /* + * we store results in static arrays, to aid interrupted + * case, but modifiers could have changed during session... + */ + XDisplayKeycodes(dpy, &minkey, &maxkey); + + keymap = XGetKeyboardMapping(dpy, minkey, (maxkey - minkey + 1), + &syms_per_keycode); + + for (i = minkey; i <= maxkey; i++) { + for (j = 0; j < syms_per_keycode; j++) { + keysym = keymap[ (i - minkey) * syms_per_keycode + j ]; + if (keysym == NoSymbol || ! ismodkey(keysym)) { + continue; + } + keycode = XKeysymToKeycode(dpy, keysym); + if (keycode == NoSymbol) { + continue; + } + keycodes[kcount] = keycode; + keysyms[kcount] = keysym; + keystrs[kcount] = strdup(XKeysymToString(keysym)); + kcount++; + } + } + XFree((void *) keymap); + first = 0; + } + if (init) { + return; + } + + get_keystate(keystate); + for (i=0; i < kcount; i++) { + keysym = keysyms[i]; + keycode = keycodes[i]; + + if (! keystate[(int) keycode]) { + continue; + } + + if (clear_mods) { + rfbLog("clear_modifiers: up: %-10s (0x%x) " + "keycode=0x%x\n", keystrs[i], keysym, keycode); + } + XTestFakeKeyEvent_wr(dpy, keycode, False, CurrentTime); + } + XFlush(dpy); +} + +static KeySym simple_mods[] = { + XK_Shift_L, XK_Shift_R, + XK_Control_L, XK_Control_R, + XK_Meta_L, XK_Meta_R, + XK_Alt_L, XK_Alt_R, + XK_Super_L, XK_Super_R, + XK_Hyper_L, XK_Hyper_R, + XK_Mode_switch, + NoSymbol +}; +#define NSIMPLE_MODS 13 + +int track_mod_state(rfbKeySym keysym, rfbBool down, rfbBool set) { + KeySym sym = (KeySym) keysym; + static rfbBool isdown[NSIMPLE_MODS]; + static int first = 1; + int i, cnt = 0; + + /* + * simple tracking method for the modifier state without + * contacting the Xserver. Ignores, of course what keys are + * pressed on the physical display. + * + * This is unrelated to our mod_tweak and xkb stuff. + * Just a simple thing for wireframe/scroll heuristics, + * sloppy keys etc. + */ + + if (first) { + for (i=0; i<NSIMPLE_MODS; i++) { + isdown[i] = FALSE; + } + first = 0; + } + + if (sym != NoSymbol) { + for (i=0; i<NSIMPLE_MODS; i++) { + if (sym == simple_mods[i]) { + if (set) { + isdown[i] = down; + return 1; + } else { + if (isdown[i]) { + return 1; + } else { + return 0; + } + } + break; + } + } + /* not a modifier */ + if (set) { + return 0; + } else { + return -1; + } + } + + /* called with NoSymbol: return number currently pressed: */ + for (i=0; i<NSIMPLE_MODS; i++) { + if (isdown[i]) { + cnt++; + } + } + return cnt; +} + +/* + * Attempt to set all keys to Up position. Can mess up typing at the + * physical keyboard so use with caution. + */ +void clear_keys(void) { + int k, keystate[256]; + + /* n.b. caller decides to X_LOCK or not. */ + get_keystate(keystate); + for (k=0; k<256; k++) { + if (keystate[k]) { + KeyCode keycode = (KeyCode) k; + rfbLog("clear_keys: keycode=%d\n", keycode); + XTestFakeKeyEvent_wr(dpy, keycode, False, CurrentTime); + } + } + XFlush(dpy); +} + +/* + * Kludge for -norepeat option: we turn off keystroke autorepeat in + * the X server when clients are connected. This may annoy people at + * the physical display. We do this because 'key down' and 'key up' + * user input events may be separated by 100s of ms due to screen fb + * processing or link latency, thereby inducing the X server to apply + * autorepeat when it should not. Since the *client* is likely doing + * keystroke autorepeating as well, it kind of makes sense to shut it + * off if no one is at the physical display... + */ +static int save_auto_repeat = -1; + +int get_autorepeat_state(void) { + XKeyboardState kstate; + X_LOCK; + XGetKeyboardControl(dpy, &kstate); + X_UNLOCK; + return kstate.global_auto_repeat; +} + +int get_initial_autorepeat_state(void) { + if (save_auto_repeat < 0) { + save_auto_repeat = get_autorepeat_state(); + } + return save_auto_repeat; +} + +void autorepeat(int restore, int bequiet) { + int global_auto_repeat; + XKeyboardControl kctrl; + + if (raw_fb && ! dpy) return; /* raw_fb hack */ + + if (restore) { + if (save_auto_repeat < 0) { + return; /* nothing to restore */ + } + global_auto_repeat = get_autorepeat_state(); + X_LOCK; + /* read state and skip restore if equal (e.g. no clients) */ + if (global_auto_repeat == save_auto_repeat) { + X_UNLOCK; + return; + } + + kctrl.auto_repeat_mode = save_auto_repeat; + XChangeKeyboardControl(dpy, KBAutoRepeatMode, &kctrl); + XFlush(dpy); + X_UNLOCK; + + if (! bequiet && ! quiet) { + rfbLog("Restored X server key autorepeat to: %d\n", + save_auto_repeat); + } + } else { + global_auto_repeat = get_autorepeat_state(); + if (save_auto_repeat < 0) { + /* + * we only remember the state at startup + * to avoid confusing ourselves later on. + */ + save_auto_repeat = global_auto_repeat; + } + + X_LOCK; + kctrl.auto_repeat_mode = AutoRepeatModeOff; + XChangeKeyboardControl(dpy, KBAutoRepeatMode, &kctrl); + XFlush(dpy); + X_UNLOCK; + + if (! bequiet && ! quiet) { + rfbLog("Disabled X server key autorepeat.\n"); + if (no_repeat_countdown >= 0) { + rfbLog(" to force back on run: 'xset r on' (%d " + "times)\n", no_repeat_countdown+1); + } + } + } +} + +/* + * We periodically delete any keysyms we have added, this is to + * lessen our effect on the X server state if we are terminated abruptly + * and cannot clear them and also to clear out any strange little used + * ones that would just fill up the keymapping. + */ +void check_add_keysyms(void) { + static time_t last_check = 0; + int clear_freq = 300, quiet = 1, count; + time_t now = time(0); + if (now > last_check + clear_freq) { + count = count_added_keycodes(); + /* + * only really delete if they have not typed recently + * and we have added 8 or more. + */ + if (now > last_keyboard_input + 5 && count >= 8) { + X_LOCK; + delete_added_keycodes(quiet); + X_UNLOCK; + } + last_check = now; + } +} + +static KeySym added_keysyms[0x100]; + +/* these are just for rfbLog messages: */ +static KeySym alltime_added_keysyms[1024]; +static int alltime_len = 1024; +static int alltime_num = 0; + +int add_keysym(KeySym keysym) { + int minkey, maxkey, syms_per_keycode; + int kc, n, ret = 0; + static int first = 1; + KeySym *keymap; + + if (first) { + for (n=0; n < 0x100; n++) { + added_keysyms[n] = NoSymbol; + } + first = 0; + } + + if (raw_fb && ! dpy) return 0; /* raw_fb hack */ + + if (keysym == NoSymbol) { + return 0; + } + /* there can be a race before MappingNotify */ + for (n=0; n < 0x100; n++) { + if (added_keysyms[n] == keysym) { + return n; + } + } + + XDisplayKeycodes(dpy, &minkey, &maxkey); + keymap = XGetKeyboardMapping(dpy, minkey, (maxkey - minkey + 1), + &syms_per_keycode); + + for (kc = minkey+1; kc <= maxkey; kc++) { + int i, j, didmsg = 0, is_empty = 1; + char *str; + KeySym new[8]; + + for (n=0; n < syms_per_keycode; n++) { + if (keymap[ (kc-minkey) * syms_per_keycode + n] + != NoSymbol) { + is_empty = 0; + break; + } + } + if (! is_empty) { + continue; + } + + for (i=0; i<8; i++) { + new[i] = NoSymbol; + } + if (add_keysyms == 2) { + new[0] = keysym; /* XXX remove me */ + } else { + for(i=0; i < syms_per_keycode; i++) { + new[i] = keysym; + if (i >= 7) break; + } + } + + XChangeKeyboardMapping(dpy, kc, syms_per_keycode, + new, 1); + + if (alltime_num >= alltime_len) { + didmsg = 1; /* something weird */ + } else { + for (j=0; j<alltime_num; j++) { + if (alltime_added_keysyms[j] == keysym) { + didmsg = 1; + break; + } + } + } + if (! didmsg) { + str = XKeysymToString(keysym); + rfbLog("added missing keysym to X display: %03d " + "0x%x \"%s\"\n", kc, keysym, str ? str : "null"); + + if (alltime_num < alltime_len) { + alltime_added_keysyms[alltime_num++] = keysym; + } + } + + XFlush(dpy); + added_keysyms[kc] = keysym; + ret = kc; + break; + } + XFree(keymap); + return ret; +} + +static void delete_keycode(KeyCode kc, int bequiet) { + int minkey, maxkey, syms_per_keycode, i; + KeySym *keymap; + KeySym ksym, new[8]; + char *str; + + if (raw_fb && ! dpy) return; /* raw_fb hack */ + + XDisplayKeycodes(dpy, &minkey, &maxkey); + keymap = XGetKeyboardMapping(dpy, minkey, (maxkey - minkey + 1), + &syms_per_keycode); + + for (i=0; i<8; i++) { + new[i] = NoSymbol; + } + + XChangeKeyboardMapping(dpy, kc, syms_per_keycode, new, 1); + + if (! bequiet && ! quiet) { + ksym = XKeycodeToKeysym(dpy, kc, 0); + str = XKeysymToString(ksym); + rfbLog("deleted keycode from X display: %03d 0x%x \"%s\"\n", + kc, ksym, str ? str : "null"); + } + + XFree(keymap); + XFlush(dpy); +} + +static int count_added_keycodes(void) { + int kc, count = 0; + for (kc = 0; kc < 0x100; kc++) { + if (added_keysyms[kc] != NoSymbol) { + count++; + } + } + return count; +} + +void delete_added_keycodes(int bequiet) { + int kc; + for (kc = 0; kc < 0x100; kc++) { + if (added_keysyms[kc] != NoSymbol) { + delete_keycode(kc, bequiet); + added_keysyms[kc] = NoSymbol; + } + } +} + +/* + * The following is for an experimental -remap option to allow the user + * to remap keystrokes. It is currently confusing wrt modifiers... + */ +typedef struct keyremap { + KeySym before; + KeySym after; + int isbutton; + struct keyremap *next; +} keyremap_t; + +static keyremap_t *keyremaps = NULL; + +static void add_remap(char *line) { + char str1[256], str2[256]; + KeySym ksym1, ksym2; + int isbtn = 0; + unsigned int i; + static keyremap_t *current = NULL; + keyremap_t *remap; + + if (sscanf(line, "%s %s", str1, str2) != 2) { + rfbLogEnable(1); + rfbLog("remap: invalid line: %s\n", line); + clean_up_exit(1); + } + if (sscanf(str1, "0x%x", &i) == 1) { + ksym1 = (KeySym) i; + } else { + ksym1 = XStringToKeysym(str1); + } + if (sscanf(str2, "0x%x", &i) == 1) { + ksym2 = (KeySym) i; + } else { + ksym2 = XStringToKeysym(str2); + } + if (ksym2 == NoSymbol) { + if (sscanf(str2, "Button%u", &i) == 1) { + ksym2 = (KeySym) i; + isbtn = 1; + } + } + if (ksym1 == NoSymbol || ksym2 == NoSymbol) { + rfbLog("warning: skipping invalid remap line: %s", line); + return; + } + remap = (keyremap_t *) malloc((size_t) sizeof(keyremap_t)); + remap->before = ksym1; + remap->after = ksym2; + remap->isbutton = isbtn; + remap->next = NULL; + + rfbLog("remapping: (%s, 0x%x) -> (%s, 0x%x) isbtn=%d\n", str1, + ksym1, str2, ksym2, isbtn); + + if (keyremaps == NULL) { + keyremaps = remap; + } else { + current->next = remap; + } + current = remap; +} + +static void add_dead_keysyms(char *str) { + char *p, *q; + int i; + char *list[] = { + "g grave dead_grave", + "a acute dead_acute", + "c asciicircum dead_circumflex", + "t asciitilde dead_tilde", + "m macron dead_macron", + "b breve dead_breve", + "D abovedot dead_abovedot", + "d diaeresis dead_diaeresis", + "o degree dead_abovering", + "A doubleacute dead_doubleacute", + "r caron dead_caron", + "e cedilla dead_cedilla", +/* "x XXX-ogonek dead_ogonek", */ +/* "x XXX-belowdot dead_belowdot", */ +/* "x XXX-hook dead_hook", */ +/* "x XXX-horn dead_horn", */ + NULL + }; + + p = str; + + while (*p != '\0') { + if (isspace(*p)) { + *p = '\0'; + } + p++; + } + + if (!strcmp(str, "DEAD")) { + for (i = 0; list[i] != NULL; i++) { + p = list[i] + 2; + add_remap(p); + } + } else if (!strcmp(str, "DEAD=missing")) { + for (i = 0; list[i] != NULL; i++) { + KeySym ksym, ksym2; + int inmap = 0; + + p = strdup(list[i] + 2); + q = strchr(p, ' '); + if (q == NULL) { + free(p); + continue; + } + *q = '\0'; + ksym = XStringToKeysym(p); + *q = ' '; + if (ksym == NoSymbol) { + free(p); + continue; + } + if (XKeysymToKeycode(dpy, ksym)) { + inmap = 1; + } +#if LIBVNCSERVER_HAVE_XKEYBOARD + if (! inmap && xkb_present) { + int kc, grp, lvl; + for (kc = 0; kc < 0x100; kc++) { + for (grp = 0; grp < 4; grp++) { + for (lvl = 0; lvl < 8; lvl++) { + ksym2 = XkbKeycodeToKeysym(dpy, + kc, grp, lvl); + if (ksym2 == NoSymbol) { + continue; + } + if (ksym2 == ksym) { + inmap = 1; + break; + } + } + } + } + } +#endif + if (! inmap) { + add_remap(p); + } + free(p); + } + } else if ((p = strchr(str, '=')) != NULL) { + while (*p != '\0') { + for (i = 0; list[i] != NULL; i++) { + q = list[i]; + if (*p == *q) { + q += 2; + add_remap(q); + break; + } + } + p++; + } + } +} + +/* + * process the -remap string (file or mapping string) + */ +void initialize_remap(char *infile) { + FILE *in; + char *p, *q, line[256]; + + if (keyremaps != NULL) { + /* free last remapping */ + keyremap_t *next_remap, *curr_remap = keyremaps; + while (curr_remap != NULL) { + next_remap = curr_remap->next; + free(curr_remap); + curr_remap = next_remap; + } + keyremaps = NULL; + } + if (infile == NULL || *infile == '\0') { + /* just unset remapping */ + return; + } + + in = fopen(infile, "r"); + if (in == NULL) { + /* assume cmd line key1-key2,key3-key4 */ + if (strstr(infile, "DEAD") == infile) { + ; + } else if (!strchr(infile, '-')) { + rfbLogEnable(1); + rfbLog("remap: cannot open: %s\n", infile); + rfbLogPerror("fopen"); + clean_up_exit(1); + } + if ((in = tmpfile()) == NULL) { + rfbLogEnable(1); + rfbLog("remap: cannot open tmpfile for %s\n", infile); + rfbLogPerror("tmpfile"); + clean_up_exit(1); + } + + /* copy in the string to file format */ + p = infile; + while (*p) { + if (*p == '-') { + fprintf(in, " "); + } else if (*p == ',' || *p == ' ' || *p == '\t') { + fprintf(in, "\n"); + } else { + fprintf(in, "%c", *p); + } + p++; + } + fprintf(in, "\n"); + fflush(in); + rewind(in); + } + + while (fgets(line, 256, in) != NULL) { + p = lblanks(line); + if (*p == '\0') { + continue; + } + if (strchr(line, '#')) { + continue; + } + + if (strstr(p, "DEAD") == p) { + add_dead_keysyms(p); + continue; + } + if ((q = strchr(line, '-')) != NULL) { + /* allow Keysym1-Keysym2 notation */ + *q = ' '; + } + add_remap(p); + } + fclose(in); +} + +/* + * preliminary support for using the Xkb (XKEYBOARD) extension for handling + * user input. inelegant, slow, and incomplete currently... but initial + * tests show it is useful for some setups. + */ +typedef struct keychar { + KeyCode code; + int group; + int level; +} keychar_t; + +/* max number of key groups and shift levels we consider */ +#define GRP 4 +#define LVL 8 +static int lvl_max, grp_max, kc_min, kc_max; +static KeySym xkbkeysyms[0x100][GRP][LVL]; +static unsigned int xkbstate[0x100][GRP][LVL]; +static unsigned int xkbignore[0x100][GRP][LVL]; +static unsigned int xkbmodifiers[0x100][GRP][LVL]; +static int multi_key[0x100], mode_switch[0x100], skipkeycode[0x100]; +static int shift_keys[0x100]; + +/* + * for trying to order the keycodes to avoid problems, note the + * *first* keycode bound to it. kc_vec will be a permutation + * of 1...256 to get them in the preferred order. + */ +static int kc_vec[0x100]; +static int kc1_shift, kc1_control, kc1_caplock, kc1_alt; +static int kc1_meta, kc1_numlock, kc1_super, kc1_hyper; +static int kc1_mode_switch, kc1_iso_level3_shift, kc1_multi_key; + +int sloppy_key_check(int key, rfbBool down, rfbKeySym keysym, int *new) { + if (!sloppy_keys) { + return 0; + } + + if (!down && !keycode_state[key] && !IsModifierKey(keysym)) { + int i, cnt = 0, downkey; + int nmods_down = track_mod_state(NoSymbol, FALSE, FALSE); + int mods_down[256]; + + if (nmods_down) { + /* tracking to skip down modifier keycodes. */ + for(i=0; i<256; i++) { + mods_down[i] = 0; + } + i = 0; + while (simple_mods[i] != NoSymbol) { + KeySym ksym = simple_mods[i]; + KeyCode k = XKeysymToKeycode(dpy, ksym); + if (k != NoSymbol && keycode_state[(int) k]) { + mods_down[(int) k] = 1; + } + + i++; + } + } + /* + * the keycode is already up... look for a single one + * (non modifier) that is down + */ + for (i=0; i<256; i++) { + if (keycode_state[i]) { + if (nmods_down && mods_down[i]) { + continue; + } + cnt++; + downkey = i; + } + } + if (cnt == 1) { + if (debug_keyboard) { + fprintf(stderr, " sloppy_keys: %d/0x%x " + "-> %d/0x%x (nmods: %d)\n", (int) key, + (int) key, downkey, downkey, nmods_down); + } + *new = downkey; + return 1; + } + } + return 0; +} + +#if !LIBVNCSERVER_HAVE_XKEYBOARD + +/* empty functions for no xkb */ +static void initialize_xkb_modtweak(void) {} +static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, + rfbClientPtr client) { +} +void switch_to_xkb_if_better(void) {} + +#else + +void switch_to_xkb_if_better(void) { + KeySym keysym, *keymap; + int miss_noxkb[256], miss_xkb[256], missing_noxkb = 0, missing_xkb = 0; + int i, j, k, n, minkey, maxkey, syms_per_keycode; + int syms_gt_4 = 0; + int kc, grp, lvl; + + /* non-alphanumeric on us keyboard */ + KeySym must_have[] = { + XK_exclam, + XK_at, + XK_numbersign, + XK_dollar, + XK_percent, +/* XK_asciicircum, */ + XK_ampersand, + XK_asterisk, + XK_parenleft, + XK_parenright, + XK_underscore, + XK_plus, + XK_minus, + XK_equal, + XK_bracketleft, + XK_bracketright, + XK_braceleft, + XK_braceright, + XK_bar, + XK_backslash, + XK_semicolon, +/* XK_apostrophe, */ + XK_colon, + XK_quotedbl, + XK_comma, + XK_period, + XK_less, + XK_greater, + XK_slash, + XK_question, +/* XK_asciitilde, */ +/* XK_grave, */ + NoSymbol + }; + + if (! use_modifier_tweak || got_noxkb) { + return; + } + if (use_xkb_modtweak) { + /* already using it */ + return; + } + + XDisplayKeycodes(dpy, &minkey, &maxkey); + + keymap = XGetKeyboardMapping(dpy, minkey, (maxkey - minkey + 1), + &syms_per_keycode); + + /* handle alphabetic char with only one keysym (no upper + lower) */ + for (i = minkey; i <= maxkey; i++) { + KeySym lower, upper; + /* 2nd one */ + keysym = keymap[(i - minkey) * syms_per_keycode + 1]; + if (keysym != NoSymbol) { + continue; + } + /* 1st one */ + keysym = keymap[(i - minkey) * syms_per_keycode + 0]; + if (keysym == NoSymbol) { + continue; + } + XConvertCase(keysym, &lower, &upper); + if (lower != upper) { + keymap[(i - minkey) * syms_per_keycode + 0] = lower; + keymap[(i - minkey) * syms_per_keycode + 1] = upper; + } + } + + k = 0; + while (must_have[k] != NoSymbol) { + int gotit = 0; + KeySym must = must_have[k]; + for (i = minkey; i <= maxkey; i++) { + for (j = 0; j < syms_per_keycode; j++) { + keysym = keymap[(i-minkey) * syms_per_keycode + j]; + if (j >= 4) { + if (k == 0 && keysym != NoSymbol) { + /* for k=0 count the high keysyms */ + syms_gt_4++; + if (debug_keyboard > 1) { + char *str = XKeysymToString(keysym); + fprintf(stderr, "- high keysym mapping" + ": at %3d j=%d " + "'%s'\n", i, j, str ? str:"null"); + } + } + continue; + } + if (keysym == must) { + if (debug_keyboard > 1) { + char *str = XKeysymToString(must); + fprintf(stderr, "- at %3d j=%d found " + "'%s'\n", i, j, str ? str:"null"); + } + /* n.b. do not break, see syms_gt_4 above. */ + gotit = 1; + } + } + } + if (! gotit) { + if (debug_keyboard > 1) { + char *str = XKeysymToString(must); + KeyCode kc = XKeysymToKeycode(dpy, must); + fprintf(stderr, "- did not find 0x%lx '%s'\t" + "Ks2Kc: %d\n", must, str ? str:"null", kc); + if (kc != None) { + int j2; + for(j2=0; j2<syms_per_keycode; j2++) { + keysym = keymap[(kc-minkey) * + syms_per_keycode + j2]; + fprintf(stderr, " %d=0x%lx", + j2, keysym); + } + fprintf(stderr, "\n"); + } + } + missing_noxkb++; + miss_noxkb[k] = 1; + } else { + miss_noxkb[k] = 0; + } + k++; + } + n = k; + + XFree(keymap); + if (missing_noxkb == 0 && syms_gt_4 >= 8) { + rfbLog("XKEYBOARD: number of keysyms per keycode %d " + "is greater\n", syms_per_keycode); + rfbLog(" than 4 and %d keysyms are mapped above 4.\n", + syms_gt_4); + rfbLog(" Automatically switching to -xkb mode.\n"); + rfbLog(" If this makes the key mapping worse you can\n"); + rfbLog(" disable it with the \"-noxkb\" option.\n"); + rfbLog(" Also, remember \"-remap DEAD\" for accenting" + " characters.\n"); + + use_xkb_modtweak = 1; + return; + + } else if (missing_noxkb == 0) { + rfbLog("XKEYBOARD: all %d \"must have\" keysyms accounted" + " for.\n", n); + rfbLog(" Not automatically switching to -xkb mode.\n"); + rfbLog(" If some keys still cannot be typed, try using" + " -xkb.\n"); + rfbLog(" Also, remember \"-remap DEAD\" for accenting" + " characters.\n"); + return; + } + + for (k=0; k<n; k++) { + miss_xkb[k] = 1; + } + + for (kc = 0; kc < 0x100; kc++) { + for (grp = 0; grp < GRP; grp++) { + for (lvl = 0; lvl < LVL; lvl++) { + /* look up the Keysym, if any */ + keysym = XkbKeycodeToKeysym(dpy, kc, grp, lvl); + if (keysym == NoSymbol) { + continue; + } + for (k=0; k<n; k++) { + if (keysym == must_have[k]) { + miss_xkb[k] = 0; + } + } + } + } + } + + for (k=0; k<n; k++) { + if (miss_xkb[k]) { + missing_xkb++; + } + } + + rfbLog("\n"); + if (missing_xkb < missing_noxkb) { + rfbLog("XKEYBOARD:\n"); + rfbLog("Switching to -xkb mode to recover these keysyms:\n"); + } else { + rfbLog("XKEYBOARD: \"must have\" keysyms better accounted" + " for\n"); + rfbLog("under -noxkb mode: not switching to -xkb mode:\n"); + } + + rfbLog(" xkb noxkb Keysym (\"X\" means present)\n"); + rfbLog(" --- ----- -----------------------------\n"); + for (k=0; k<n; k++) { + char *xx, *xn, *name; + + keysym = must_have[k]; + if (keysym == NoSymbol) { + continue; + } + if (!miss_xkb[k] && !miss_noxkb[k]) { + continue; + } + if (miss_xkb[k]) { + xx = " "; + } else { + xx = " X "; + } + if (miss_noxkb[k]) { + xn = " "; + } else { + xn = " X "; + } + name = XKeysymToString(keysym); + rfbLog(" %s %s 0x%lx %s\n", xx, xn, keysym, + name ? name : "null"); + } + rfbLog("\n"); + + if (missing_xkb < missing_noxkb) { + rfbLog(" If this makes the key mapping worse you can\n"); + rfbLog(" disable it with the \"-noxkb\" option.\n"); + rfbLog("\n"); + + use_xkb_modtweak = 1; + + } else { + rfbLog(" If some keys still cannot be typed, try using" + " -xkb.\n"); + rfbLog(" Also, remember \"-remap DEAD\" for accenting" + " characters.\n"); + } +} + +/* sets up all the keymapping info via Xkb API */ + +static void initialize_xkb_modtweak(void) { + KeySym ks; + int kc, grp, lvl, k; + unsigned int state; + +/* + * Here is a guide: + +Workarounds arrays: + +multi_key[] indicates which keycodes have Multi_key (Compose) + bound to them. +mode_switch[] indicates which keycodes have Mode_switch (AltGr) + bound to them. +shift_keys[] indicates which keycodes have Shift bound to them. +skipkeycode[] indicates which keycodes are to be skipped + for any lookups from -skip_keycodes option. + +Groups and Levels, here is an example: + + ^ -------- + | L2 | A AE | + shift | | + level L1 | a ae | + -------- + G1 G2 + + group -> + +Traditionally this it all a key could do. L1 vs. L2 selected via Shift +and G1 vs. G2 selected via Mode_switch. Up to 4 Keysyms could be bound +to a key. See initialize_modtweak() for an example of using that type +of keymap from XGetKeyboardMapping(). + +Xkb gives us up to 4 groups and 63 shift levels per key, with the +situation being potentially different for each key. This is complicated, +and I don't claim to understand it all, but in the following we just think +of ranging over the group and level indices as covering all of the cases. +This gives us an accurate view of the keymap. The main tricky part +is mapping between group+level and modifier state. + +On current linux/XFree86 setups (Xkb is enabled by default) the +information from XGetKeyboardMapping() (evidently the compat map) +is incomplete and inaccurate, so we are really forced to use the +Xkb API. + +xkbkeysyms[] For a (keycode,group,level) holds the KeySym (0 for none) +xkbstate[] For a (keycode,group,level) holds the corresponding + modifier state needed to get that KeySym +xkbignore[] For a (keycode,group,level) which modifiers can be + ignored (the 0 bits can be ignored). +xkbmodifiers[] For the KeySym bound to this (keycode,group,level) store + the modifier mask. + * + */ + + /* initialize all the arrays: */ + for (kc = 0; kc < 0x100; kc++) { + multi_key[kc] = 0; + mode_switch[kc] = 0; + skipkeycode[kc] = 0; + shift_keys[kc] = 0; + + for (grp = 0; grp < GRP; grp++) { + for (lvl = 0; lvl < LVL; lvl++) { + xkbkeysyms[kc][grp][lvl] = NoSymbol; + xkbmodifiers[kc][grp][lvl] = -1; + xkbstate[kc][grp][lvl] = -1; + } + } + } + + /* + * the array is 256*LVL*GRP, but we can make the searched region + * smaller by computing the actual ranges. + */ + lvl_max = 0; + grp_max = 0; + kc_max = 0; + kc_min = 0x100; + + /* first keycode for a modifier type (multi_key too) */ + kc1_shift = -1; + kc1_control = -1; + kc1_caplock = -1; + kc1_alt = -1; + kc1_meta = -1; + kc1_numlock = -1; + kc1_super = -1; + kc1_hyper = -1; + kc1_mode_switch = -1; + kc1_iso_level3_shift = -1; + kc1_multi_key = -1; + + /* + * loop over all possible (keycode, group, level) triples + * and record what we find for it: + */ + if (debug_keyboard > 1) { + rfbLog("initialize_xkb_modtweak: XKB keycode -> keysyms " + "mapping info:\n"); + } + for (kc = 0; kc < 0x100; kc++) { + for (grp = 0; grp < GRP; grp++) { + for (lvl = 0; lvl < LVL; lvl++) { + unsigned int ms, mods; + int state_save = -1, mods_save; + KeySym ks2; + + /* look up the Keysym, if any */ + ks = XkbKeycodeToKeysym(dpy, kc, grp, lvl); + xkbkeysyms[kc][grp][lvl] = ks; + + /* if no Keysym, on to next */ + if (ks == NoSymbol) { + continue; + } + /* + * for various workarounds, note where these special + * keys are bound to. + */ + if (ks == XK_Multi_key) { + multi_key[kc] = lvl+1; + } + if (ks == XK_Mode_switch) { + mode_switch[kc] = lvl+1; + } + if (ks == XK_Shift_L || ks == XK_Shift_R) { + shift_keys[kc] = lvl+1; + } + + if (ks == XK_Shift_L || ks == XK_Shift_R) { + if (kc1_shift == -1) { + kc1_shift = kc; + } + } + if (ks == XK_Control_L || ks == XK_Control_R) { + if (kc1_control == -1) { + kc1_control = kc; + } + } + if (ks == XK_Caps_Lock || ks == XK_Caps_Lock) { + if (kc1_caplock == -1) { + kc1_caplock = kc; + } + } + if (ks == XK_Alt_L || ks == XK_Alt_R) { + if (kc1_alt == -1) { + kc1_alt = kc; + } + } + if (ks == XK_Meta_L || ks == XK_Meta_R) { + if (kc1_meta == -1) { + kc1_meta = kc; + } + } + if (ks == XK_Num_Lock) { + if (kc1_numlock == -1) { + kc1_numlock = kc; + } + } + if (ks == XK_Super_L || ks == XK_Super_R) { + if (kc1_super == -1) { + kc1_super = kc; + } + } + if (ks == XK_Hyper_L || ks == XK_Hyper_R) { + if (kc1_hyper == -1) { + kc1_hyper = kc; + } + } + if (ks == XK_Mode_switch) { + if (kc1_mode_switch == -1) { + kc1_mode_switch = kc; + } + } + if (ks == XK_ISO_Level3_Shift) { + if (kc1_iso_level3_shift == -1) { + kc1_iso_level3_shift = kc; + } + } + if (ks == XK_Multi_key) { /* not a modifier.. */ + if (kc1_multi_key == -1) { + kc1_multi_key = kc; + } + } + + /* + * record maximum extent for group/level indices + * and keycode range: + */ + if (grp > grp_max) { + grp_max = grp; + } + if (lvl > lvl_max) { + lvl_max = lvl; + } + if (kc > kc_max) { + kc_max = kc; + } + if (kc < kc_min) { + kc_min = kc; + } + + /* + * lookup on *keysym* (i.e. not kc, grp, lvl) + * and get the modifier mask. this is 0 for + * most keysyms, only non zero for modifiers. + */ + ms = XkbKeysymToModifiers(dpy, ks); + xkbmodifiers[kc][grp][lvl] = ms; + + /* + * Amusing heuristic (may have bugs). There are + * 8 modifier bits, so 256 possible modifier + * states. We loop over all of them for this + * keycode (simulating Key "events") and ask + * XkbLookupKeySym to tell us the Keysym. Once it + * matches the Keysym we have for this (keycode, + * group, level), gotten via XkbKeycodeToKeysym() + * above, we then (hopefully...) know that state + * of modifiers needed to generate this keysym. + * + * Yes... keep your fingers crossed. + * + * Note that many of the 256 states give the + * Keysym, we just need one, and we take the + * first one found. + */ + state = 0; + while(state < 256) { + if (XkbLookupKeySym(dpy, kc, state, &mods, + &ks2)) { + + /* save these for workaround below */ + if (state_save == -1) { + state_save = state; + mods_save = mods; + } + if (ks2 == ks) { + /* + * zero the irrelevant bits + * by anding with mods. + */ + xkbstate[kc][grp][lvl] + = state & mods; + /* + * also remember the irrelevant + * bits since it is handy. + */ + xkbignore[kc][grp][lvl] = mods; + + break; + } + } + state++; + } + if (xkbstate[kc][grp][lvl] == (unsigned int) -1 + && grp == 1) { + /* + * Hack on Solaris 9 for Mode_switch + * for Group2 characters. We force the + * Mode_switch modifier bit on. + * XXX Need to figure out better what is + * happening here. Is compat on somehow?? + */ + unsigned int ms2; + ms2 = XkbKeysymToModifiers(dpy, XK_Mode_switch); + + xkbstate[kc][grp][lvl] + = (state_save & mods_save) | ms2; + + xkbignore[kc][grp][lvl] = mods_save | ms2; + } + + if (debug_keyboard > 1) { + fprintf(stderr, " %03d G%d L%d mod=%s ", + kc, grp+1, lvl+1, bitprint(ms, 8)); + fprintf(stderr, "state=%s ", + bitprint(xkbstate[kc][grp][lvl], 8)); + fprintf(stderr, "ignore=%s ", + bitprint(xkbignore[kc][grp][lvl], 8)); + fprintf(stderr, " ks=0x%08lx \"%s\"\n", + ks, XKeysymToString(ks)); + } + } + } + } + + /* + * kc_vec will be used in some places to find modifiers, etc + * we apply some permutations to it as workarounds. + */ + for (kc = 0; kc < 0x100; kc++) { + kc_vec[kc] = kc; + } + + if (kc1_mode_switch != -1 && kc1_iso_level3_shift != -1) { + if (kc1_mode_switch < kc1_iso_level3_shift) { + /* we prefer XK_ISO_Level3_Shift: */ + kc_vec[kc1_mode_switch] = kc1_iso_level3_shift; + kc_vec[kc1_iso_level3_shift] = kc1_mode_switch; + } + } + /* any more? need to watch for undoing the above. */ + + /* + * process the user supplied -skip_keycodes string. + * This is presumably a list if "ghost" keycodes, the X server + * thinks they exist, but they do not. ghosts can lead to + * ambiguities in the reverse map: Keysym -> KeyCode + Modstate, + * so if we can ignore them so much the better. Presumably the + * user can never generate them from the physical keyboard. + * There may be other reasons to deaden some keys. + */ + if (skip_keycodes != NULL) { + char *p, *str = strdup(skip_keycodes); + p = strtok(str, ", \t\n\r"); + while (p) { + k = 1; + if (sscanf(p, "%d", &k) != 1 || k < 0 || k >= 0x100) { + rfbLogEnable(1); + rfbLog("invalid skip_keycodes: %s %s\n", + skip_keycodes, p); + clean_up_exit(1); + } + skipkeycode[k] = 1; + p = strtok(NULL, ", \t\n\r"); + } + free(str); + } + if (debug_keyboard > 1) { + fprintf(stderr, "grp_max=%d lvl_max=%d\n", grp_max, lvl_max); + } +} + +/* + * Called on user keyboard input. Try to solve the reverse mapping + * problem: KeySym (from VNC client) => KeyCode(s) to press to generate + * it. The one-to-many KeySym => KeyCode mapping makes it difficult, as + * does working out what changes to the modifier keypresses are needed. + */ +static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, + rfbClientPtr client) { + + int kc, grp, lvl, i, kci; + int kc_f[0x100], grp_f[0x100], lvl_f[0x100], state_f[0x100], found; + int ignore_f[0x100]; + unsigned int state; + + + /* these are used for finding modifiers, etc */ + XkbStateRec kbstate; + int got_kbstate = 0; + int Kc_f, Grp_f, Lvl_f; + static int Kc_last_down = -1; + static KeySym Ks_last_down = NoSymbol; + + if (client) {} /* unused vars warning: */ + + X_LOCK; + + if (debug_keyboard) { + char *str = XKeysymToString(keysym); + + if (debug_keyboard > 1) { + rfbLog("----------start-xkb_tweak_keyboard (%s) " + "--------\n", down ? "DOWN" : "UP"); + } + + rfbLog("xkb_tweak_keyboard: %s keysym=0x%x \"%s\"\n", + down ? "down" : "up", (int) keysym, str ? str : "null"); + } + + /* + * set everything to not-yet-found. + * these "found" arrays (*_f) let us dynamically consider the + * one-to-many Keysym -> Keycode issue. we set the size at 256, + * but of course only very few will be found. + */ + for (i = 0; i < 0x100; i++) { + kc_f[i] = -1; + grp_f[i] = -1; + lvl_f[i] = -1; + state_f[i] = -1; + ignore_f[i] = -1; + } + found = 0; + + /* + * loop over all (keycode, group, level) triples looking for + * matching keysyms. Amazingly this isn't slow (but maybe if + * you type really fast...). Hash lookup into a linked list of + * (keycode,grp,lvl) triples would be the way to improve this + * in the future if needed. + */ + for (kc = kc_min; kc <= kc_max; kc++) { + for (grp = 0; grp < grp_max+1; grp++) { + for (lvl = 0; lvl < lvl_max+1; lvl++) { + if (keysym != xkbkeysyms[kc][grp][lvl]) { + continue; + } + /* got a keysym match */ + state = xkbstate[kc][grp][lvl]; + + if (debug_keyboard > 1) { + fprintf(stderr, " got match kc=%03d=0x%02x G%d" + " L%d ks=0x%x \"%s\" (basesym: \"%s\")\n", + kc, kc, grp+1, lvl+1, keysym, + XKeysymToString(keysym), XKeysymToString( + XKeycodeToKeysym(dpy, kc, 0))); + fprintf(stderr, " need state: %s\n", + bitprint(state, 8)); + fprintf(stderr, " ignorable : %s\n", + bitprint(xkbignore[kc][grp][lvl], 8)); + } + + /* save it if state is OK and not told to skip */ + if (state == (unsigned int) -1) { + continue; + } + if (skipkeycode[kc] && debug_keyboard) { + fprintf(stderr, " xxx skipping keycode: %d " + "G%d/L%d\n", kc, grp+1, lvl+1); + } + if (skipkeycode[kc]) { + continue; + } + if (found > 0 && kc == kc_f[found-1]) { + /* ignore repeats for same keycode */ + continue; + } + kc_f[found] = kc; + grp_f[found] = grp; + lvl_f[found] = lvl; + state_f[found] = state; + ignore_f[found] = xkbignore[kc][grp][lvl]; + found++; + } + } + } + +#define PKBSTATE \ + fprintf(stderr, " --- current mod state:\n"); \ + fprintf(stderr, " mods : %s\n", bitprint(kbstate.mods, 8)); \ + fprintf(stderr, " base_mods : %s\n", bitprint(kbstate.base_mods, 8)); \ + fprintf(stderr, " latch_mods: %s\n", bitprint(kbstate.latched_mods, 8)); \ + fprintf(stderr, " lock_mods : %s\n", bitprint(kbstate.locked_mods, 8)); \ + fprintf(stderr, " compat : %s\n", bitprint(kbstate.compat_state, 8)); + + /* + * Now get the current state of the keyboard from the X server. + * This seems to be the safest way to go as opposed to our + * keeping track of the modifier state on our own. Again, + * this is fortunately not too slow. + */ + + if (debug_keyboard > 1) { + /* get state early for debug output */ + XkbGetState(dpy, XkbUseCoreKbd, &kbstate); + got_kbstate = 1; + PKBSTATE + } + + if (!found && add_keysyms && keysym && ! IsModifierKey(keysym)) { + int new_kc = add_keysym(keysym); + if (new_kc != 0) { + found = 1; + kc_f[0] = new_kc; + grp_f[0] = 0; + lvl_f[0] = 0; + state_f[0] = 0; + } + } + + if (!found && debug_keyboard) { + char *str = XKeysymToString(keysym); + fprintf(stderr, " *** NO key found for: 0x%x %s " + "*keystroke ignored*\n", keysym, str ? str : "null"); + } + if (!found) { + X_UNLOCK; + return; + } + + /* + * we try to optimize here if found > 1 + * e.g. minimize lvl or grp, or other things to give + * "safest" scenario to simulate the keystrokes. + */ + + if (found > 1) { + if (down) { + int l, score[0x100]; + int best, best_score = -1; + /* need to break the tie... */ + if (! got_kbstate) { + XkbGetState(dpy, XkbUseCoreKbd, &kbstate); + got_kbstate = 1; + } + for (l=0; l < found; l++) { + int myscore = 0, b = 0x1, i; + int curr, curr_state = kbstate.mods; + int need, need_state = state_f[l]; + int ignore_state = ignore_f[l]; + + /* see how many modifiers need to be changed */ + for (i=0; i<8; i++) { + curr = b & curr_state; + need = b & need_state; + if (! (b & ignore_state)) { + ; + } else if (curr == need) { + ; + } else { + myscore++; + } + b = b << 1; + } + myscore *= 100; + + /* throw in some minimization of lvl too: */ + myscore += 2*lvl_f[l] + grp_f[l]; + + /* + * XXX since we now internally track + * keycode_state[], we could throw that into + * the score as well. I.e. if it is already + * down, it is pointless to think we can + * press it down further! E.g. + * myscore += 1000 * keycode_state[kc_f[l]]; + * Also could watch multiple modifier + * problem, e.g. Shift+key -> Alt + * keycode = 125 on my keyboard. + */ + + score[l] = myscore; + if (debug_keyboard > 1) { + fprintf(stderr, " *** score for " + "keycode %03d: %4d\n", + kc_f[l], myscore); + } + } + for (l=0; l < found; l++) { + int myscore = score[l]; + if (best_score == -1 || myscore < best_score) { + best = l; + best_score = myscore; + } + } + Kc_f = kc_f[best]; + Grp_f = grp_f[best]; + Lvl_f = lvl_f[best]; + state = state_f[best]; + + } else { + /* up */ + Kc_f = -1; + if (keysym == Ks_last_down) { + int l; + for (l=0; l < found; l++) { + if (Kc_last_down == kc_f[l]) { + Kc_f = Kc_last_down; + break; + } + } + } + if (Kc_f == -1) { + int l; + /* + * If it is already down, that is + * a great hint. Use it. + * + * note: keycode_state in internal and + * ignores someone pressing keys on the + * physical display (but is updated + * periodically to clean out stale info). + */ + for (l=0; l < found; l++) { + int key = (int) kc_f[l]; + if (keycode_state[key]) { + Kc_f = kc_f[l]; + break; + } + } + } + + if (Kc_f == -1) { + /* hope for the best... XXX check mods */ + Kc_f = kc_f[0]; + } + } + } else { + Kc_f = kc_f[0]; + Grp_f = grp_f[0]; + Lvl_f = lvl_f[0]; + state = state_f[0]; + } + + if (debug_keyboard && found > 1) { + int l; + char *str; + fprintf(stderr, " *** found more than one keycode: "); + for (l = 0; l < found; l++) { + fprintf(stderr, "%03d ", kc_f[l]); + } + for (l = 0; l < found; l++) { + str = XKeysymToString(XKeycodeToKeysym(dpy,kc_f[l],0)); + fprintf(stderr, " \"%s\"", str ? str : "null"); + } + fprintf(stderr, ", picked this one: %03d (last down: %03d)\n", + Kc_f, Kc_last_down); + } + + if (sloppy_keys) { + int new_kc; + if (sloppy_key_check(Kc_f, down, keysym, &new_kc)) { + Kc_f = new_kc; + } + } + + if (down) { + /* + * need to set up the mods for tweaking and other workarounds + */ + int needmods[8], sentmods[8], Ilist[8], keystate[256]; + int involves_multi_key, shift_is_down; + int i, j, b, curr, need; + unsigned int ms; + KeySym ks; + Bool dn; + + /* remember these to aid the subsequent up case: */ + Ks_last_down = keysym; + Kc_last_down = Kc_f; + + if (! got_kbstate) { + /* get the current modifier state if we haven't yet */ + XkbGetState(dpy, XkbUseCoreKbd, &kbstate); + got_kbstate = 1; + } + + /* + * needmods[] whether or not that modifier bit needs + * something done to it. + * < 0 means no, + * 0 means needs to go up. + * 1 means needs to go down. + * + * -1, -2, -3 are used for debugging info to indicate + * why nothing needs to be done with the modifier, see below. + * + * sentmods[] is the corresponding keycode to use + * to acheive the needmods[] requirement for the bit. + */ + + for (i=0; i<8; i++) { + needmods[i] = -1; + sentmods[i] = 0; + } + + /* + * Loop over the 8 modifier bits and check if the current + * setting is what we need it to be or whether it should + * be changed (by us sending some keycode event) + * + * If nothing needs to be done to it record why: + * -1 the modifier bit is ignored. + * -2 the modifier bit is ignored, but is correct anyway. + * -3 the modifier bit is correct. + */ + + b = 0x1; + for (i=0; i<8; i++) { + curr = b & kbstate.mods; + need = b & state; + + if (! (b & xkbignore[Kc_f][Grp_f][Lvl_f])) { + /* irrelevant modifier bit */ + needmods[i] = -1; + if (curr == need) needmods[i] = -2; + } else if (curr == need) { + /* already correct */ + needmods[i] = -3; + } else if (! curr && need) { + /* need it down */ + needmods[i] = 1; + } else if (curr && ! need) { + /* need it up */ + needmods[i] = 0; + } + + b = b << 1; + } + + /* + * Again we dynamically probe the X server for information, + * this time for the state of all the keycodes. Useful + * info, and evidently is not too slow... + */ + get_keystate(keystate); + + /* + * We try to determine if Shift is down (since that can + * screw up ISO_Level3_Shift manipulations). + */ + shift_is_down = 0; + + for (kc = kc_min; kc <= kc_max; kc++) { + if (skipkeycode[kc] && debug_keyboard) { + fprintf(stderr, " xxx skipping keycode: " + "%d\n", kc); + } + if (skipkeycode[kc]) { + continue; + } + if (shift_keys[kc] && keystate[kc]) { + shift_is_down = kc; + break; + } + } + + /* + * Now loop over the modifier bits and try to deduce the + * keycode presses/release require to match the desired + * state. + */ + for (i=0; i<8; i++) { + if (needmods[i] < 0 && debug_keyboard > 1) { + int k = -needmods[i] - 1; + char *words[] = {"ignorable", + "bitset+ignorable", "bitset"}; + fprintf(stderr, " +++ needmods: mod=%d is " + "OK (%s)\n", i, words[k]); + } + if (needmods[i] < 0) { + continue; + } + + b = 1 << i; + + if (debug_keyboard > 1) { + fprintf(stderr, " +++ needmods: mod=%d %s " + "need it to be: %d %s\n", i, bitprint(b, 8), + needmods[i], needmods[i] ? "down" : "up"); + } + + /* + * Again, an inefficient loop, this time just + * looking for modifiers... + * + * note the use of kc_vec to prefer XK_ISO_Level3_Shift + * over XK_Mode_switch. + */ + for (kci = kc_min; kci <= kc_max; kci++) { + for (grp = 0; grp < grp_max+1; grp++) { + for (lvl = 0; lvl < lvl_max+1; lvl++) { + int skip = 1, dbmsg = 0; + + kc = kc_vec[kci]; + + ms = xkbmodifiers[kc][grp][lvl]; + if (! ms || ms != (unsigned int) b) { + continue; + } + + if (skipkeycode[kc] && debug_keyboard) { + fprintf(stderr, " xxx skipping keycode:" + " %d G%d/L%d\n", kc, grp+1, lvl+1); + } + if (skipkeycode[kc]) { + continue; + } + + ks = xkbkeysyms[kc][grp][lvl]; + if (! ks) { + continue; + } + + if (ks == XK_Shift_L) { + skip = 0; + } else if (ks == XK_Shift_R) { + skip = 0; + } else if (ks == XK_Mode_switch) { + skip = 0; + } else if (ks == XK_ISO_Level3_Shift) { + skip = 0; + } + /* + * Alt, Meta, Control, Super, + * Hyper, Num, Caps are skipped. + * + * XXX need more work on Locks, + * and non-standard modifiers. + * (e.g. XF86_Next_VMode using + * Ctrl+Alt) + */ + if (debug_keyboard > 1) { + char *str = XKeysymToString(ks); + int kt = keystate[kc]; + fprintf(stderr, " === for mod=%s " + "found kc=%03d/G%d/L%d it is %d " + "%s skip=%d (%s)\n", bitprint(b,8), + kc, grp+1, lvl+1, kt, kt ? + "down" : "up ", skip, str ? + str : "null"); + } + + if (! skip && needmods[i] != + keystate[kc] && sentmods[i] == 0) { + sentmods[i] = kc; + dbmsg = 1; + } + + if (debug_keyboard > 1 && dbmsg) { + int nm = needmods[i]; + fprintf(stderr, " >>> we choose " + "kc=%03d=0x%02x to change it to: " + "%d %s\n", kc, kc, nm, nm ? + "down" : "up"); + } + + } + } + } + } + for (i=0; i<8; i++) { + /* + * reverse order is useful for tweaking + * ISO_Level3_Shift before Shift, but assumes they + * are in that order (i.e. Shift is first bit). + */ + int reverse = 1; + if (reverse) { + Ilist[i] = 7 - i; + } else { + Ilist[i] = i; + } + } + + /* + * check to see if Multi_key is bound to one of the Mods + * we have to tweak + */ + involves_multi_key = 0; + for (j=0; j<8; j++) { + i = Ilist[j]; + if (sentmods[i] == 0) continue; + dn = (Bool) needmods[i]; + if (!dn) continue; + if (multi_key[sentmods[i]]) { + involves_multi_key = i+1; + } + } + + if (involves_multi_key && shift_is_down && needmods[0] < 0) { + /* + * Workaround for Multi_key and shift. + * Assumes Shift is bit 1 (needmods[0]) + */ + if (debug_keyboard) { + fprintf(stderr, " ^^^ trying to avoid " + "inadvertent Multi_key from Shift " + "(doing %03d up now)\n", shift_is_down); + } + XTestFakeKeyEvent_wr(dpy, shift_is_down, False, + CurrentTime); + } else { + involves_multi_key = 0; + } + + for (j=0; j<8; j++) { + /* do the Mod ups */ + i = Ilist[j]; + if (sentmods[i] == 0) continue; + dn = (Bool) needmods[i]; + if (dn) continue; + XTestFakeKeyEvent_wr(dpy, sentmods[i], dn, CurrentTime); + } + for (j=0; j<8; j++) { + /* next, do the Mod downs */ + i = Ilist[j]; + if (sentmods[i] == 0) continue; + dn = (Bool) needmods[i]; + if (!dn) continue; + XTestFakeKeyEvent_wr(dpy, sentmods[i], dn, CurrentTime); + } + + if (involves_multi_key) { + /* + * Reverse workaround for Multi_key and shift. + */ + if (debug_keyboard) { + fprintf(stderr, " vvv trying to avoid " + "inadvertent Multi_key from Shift " + "(doing %03d down now)\n", shift_is_down); + } + XTestFakeKeyEvent_wr(dpy, shift_is_down, True, + CurrentTime); + } + + /* + * With the above modifier work done, send the actual keycode: + */ + XTestFakeKeyEvent_wr(dpy, Kc_f, (Bool) down, CurrentTime); + + /* + * Now undo the modifier work: + */ + for (j=7; j>=0; j--) { + /* reverse Mod downs we did */ + i = Ilist[j]; + if (sentmods[i] == 0) continue; + dn = (Bool) needmods[i]; + if (!dn) continue; + XTestFakeKeyEvent_wr(dpy, sentmods[i], !dn, + CurrentTime); + } + for (j=7; j>=0; j--) { + /* finally reverse the Mod ups we did */ + i = Ilist[j]; + if (sentmods[i] == 0) continue; + dn = (Bool) needmods[i]; + if (dn) continue; + XTestFakeKeyEvent_wr(dpy, sentmods[i], !dn, + CurrentTime); + } + + } else { /* for up case, hopefully just need to pop it up: */ + + XTestFakeKeyEvent_wr(dpy, Kc_f, (Bool) down, CurrentTime); + } + X_UNLOCK; +} +#endif + +/* + * For tweaking modifiers wrt the Alt-Graph key, etc. + */ +#define LEFTSHIFT 1 +#define RIGHTSHIFT 2 +#define ALTGR 4 +static char mod_state = 0; + +static char modifiers[0x100]; +static KeyCode keycodes[0x100]; +static KeyCode left_shift_code, right_shift_code, altgr_code, iso_level3_code; + +/* workaround for X11R5, Latin 1 only */ +#ifndef XConvertCase +#define XConvertCase(sym, lower, upper) \ +*(lower) = sym; \ +*(upper) = sym; \ +if (sym >> 8 == 0) { \ + if ((sym >= XK_A) && (sym <= XK_Z)) \ + *(lower) += (XK_a - XK_A); \ + else if ((sym >= XK_a) && (sym <= XK_z)) \ + *(upper) -= (XK_a - XK_A); \ + else if ((sym >= XK_Agrave) && (sym <= XK_Odiaeresis)) \ + *(lower) += (XK_agrave - XK_Agrave); \ + else if ((sym >= XK_agrave) && (sym <= XK_odiaeresis)) \ + *(upper) -= (XK_agrave - XK_Agrave); \ + else if ((sym >= XK_Ooblique) && (sym <= XK_Thorn)) \ + *(lower) += (XK_oslash - XK_Ooblique); \ + else if ((sym >= XK_oslash) && (sym <= XK_thorn)) \ + *(upper) -= (XK_oslash - XK_Ooblique); \ +} +#endif + +char *short_kmb(char *str) { + int i, saw_k = 0, saw_m = 0, saw_b = 0, n = 10; + char *p, tmp[10]; + + for (i=0; i<n; i++) { + tmp[i] = '\0'; + } + + p = str; + i = 0; + while (*p) { + if ((*p == 'K' || *p == 'k') && !saw_k) { + tmp[i++] = 'K'; + saw_k = 1; + } else if ((*p == 'M' || *p == 'm') && !saw_m) { + tmp[i++] = 'M'; + saw_m = 1; + } else if ((*p == 'B' || *p == 'b') && !saw_b) { + tmp[i++] = 'B'; + saw_b = 1; + } + p++; + } + return(strdup(tmp)); +} + +void initialize_allowed_input(void) { + char *str; + + if (allowed_input_normal) { + free(allowed_input_normal); + allowed_input_normal = NULL; + } + if (allowed_input_view_only) { + free(allowed_input_view_only); + allowed_input_view_only = NULL; + } + + if (! allowed_input_str) { + allowed_input_normal = strdup("KMB"); + allowed_input_view_only = strdup(""); + } else { + char *p, *str = strdup(allowed_input_str); + p = strchr(str, ','); + if (p) { + allowed_input_view_only = strdup(p+1); + *p = '\0'; + allowed_input_normal = strdup(str); + } else { + allowed_input_normal = strdup(str); + allowed_input_view_only = strdup(""); + } + free(str); + } + + /* shorten them */ + str = short_kmb(allowed_input_normal); + free(allowed_input_normal); + allowed_input_normal = str; + + str = short_kmb(allowed_input_view_only); + free(allowed_input_view_only); + allowed_input_view_only = str; + + if (screen) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + ClientData *cd = (ClientData *) cl->clientData; + + if (cd->input[0] == '=') { + ; /* custom setting */ + } else if (cd->login_viewonly) { + if (*allowed_input_view_only != '\0') { + cl->viewOnly = FALSE; + cd->input[0] = '\0'; + strncpy(cd->input, + allowed_input_view_only, CILEN); + } else { + cl->viewOnly = TRUE; + } + } else { + if (allowed_input_normal) { + cd->input[0] = '\0'; + strncpy(cd->input, + allowed_input_normal, CILEN); + } + } + } + rfbReleaseClientIterator(iter); + } +} + +void initialize_modtweak(void) { + KeySym keysym, *keymap; + int i, j, minkey, maxkey, syms_per_keycode; + + if (use_xkb_modtweak) { + initialize_xkb_modtweak(); + return; + } + memset(modifiers, -1, sizeof(modifiers)); + for (i=0; i<0x100; i++) { + keycodes[i] = NoSymbol; + } + + X_LOCK; + XDisplayKeycodes(dpy, &minkey, &maxkey); + + keymap = XGetKeyboardMapping(dpy, minkey, (maxkey - minkey + 1), + &syms_per_keycode); + + /* handle alphabetic char with only one keysym (no upper + lower) */ + for (i = minkey; i <= maxkey; i++) { + KeySym lower, upper; + /* 2nd one */ + keysym = keymap[(i - minkey) * syms_per_keycode + 1]; + if (keysym != NoSymbol) { + continue; + } + /* 1st one */ + keysym = keymap[(i - minkey) * syms_per_keycode + 0]; + if (keysym == NoSymbol) { + continue; + } + XConvertCase(keysym, &lower, &upper); + if (lower != upper) { + keymap[(i - minkey) * syms_per_keycode + 0] = lower; + keymap[(i - minkey) * syms_per_keycode + 1] = upper; + } + } + for (i = minkey; i <= maxkey; i++) { + if (debug_keyboard) { + if (i == minkey) { + rfbLog("initialize_modtweak: keycode -> " + "keysyms mapping info:\n"); + } + fprintf(stderr, " %03d ", i); + } + for (j = 0; j < syms_per_keycode; j++) { + if (debug_keyboard) { + char *sym; +#if 0 + sym =XKeysymToString(XKeycodeToKeysym(dpy,i,j)); +#else + keysym = keymap[(i-minkey)*syms_per_keycode+j]; + sym = XKeysymToString(keysym); +#endif + fprintf(stderr, "%-18s ", sym ? sym : "null"); + if (j == syms_per_keycode - 1) { + fprintf(stderr, "\n"); + } + } + if (j >= 4) { + /* + * Something wacky in the keymapping. + * Ignore these non Shift/AltGr chords + * for now... n.b. we try to automatically + * switch to -xkb for this case. + */ + continue; + } + keysym = keymap[ (i - minkey) * syms_per_keycode + j ]; + if ( keysym >= ' ' && keysym < 0x100 + && i == XKeysymToKeycode(dpy, keysym) ) { + keycodes[keysym] = i; + modifiers[keysym] = j; + } + } + } + + left_shift_code = XKeysymToKeycode(dpy, XK_Shift_L); + right_shift_code = XKeysymToKeycode(dpy, XK_Shift_R); + altgr_code = XKeysymToKeycode(dpy, XK_Mode_switch); + iso_level3_code = NoSymbol; +#ifdef XK_ISO_Level3_Shift + iso_level3_code = XKeysymToKeycode(dpy, XK_ISO_Level3_Shift); +#endif + + XFree ((void *) keymap); + + X_UNLOCK; +} + +/* + * does the actual tweak: + */ +static void tweak_mod(signed char mod, rfbBool down) { + rfbBool is_shift = mod_state & (LEFTSHIFT|RIGHTSHIFT); + Bool dn = (Bool) down; + KeyCode altgr = altgr_code; + + if (mod < 0) { + if (debug_keyboard) { + rfbLog("tweak_mod: Skip: down=%d index=%d\n", down, + (int) mod); + } + return; + } + if (debug_keyboard) { + rfbLog("tweak_mod: Start: down=%d index=%d mod_state=0x%x" + " is_shift=%d\n", down, (int) mod, (int) mod_state, + is_shift); + } + + if (use_iso_level3 && iso_level3_code) { + altgr = iso_level3_code; + } + + X_LOCK; + if (is_shift && mod != 1) { + if (mod_state & LEFTSHIFT) { + XTestFakeKeyEvent_wr(dpy, left_shift_code, !dn, CurrentTime); + } + if (mod_state & RIGHTSHIFT) { + XTestFakeKeyEvent_wr(dpy, right_shift_code, !dn, CurrentTime); + } + } + if ( ! is_shift && mod == 1 ) { + XTestFakeKeyEvent_wr(dpy, left_shift_code, dn, CurrentTime); + } + if ( altgr && (mod_state & ALTGR) && mod != 2 ) { + XTestFakeKeyEvent_wr(dpy, altgr, !dn, CurrentTime); + } + if ( altgr && ! (mod_state & ALTGR) && mod == 2 ) { + XTestFakeKeyEvent_wr(dpy, altgr, dn, CurrentTime); + } + X_UNLOCK; + if (debug_keyboard) { + rfbLog("tweak_mod: Finish: down=%d index=%d mod_state=0x%x" + " is_shift=%d\n", down, (int) mod, (int) mod_state, + is_shift); + } +} + +/* + * tweak the modifier under -modtweak + */ +static void modifier_tweak_keyboard(rfbBool down, rfbKeySym keysym, + rfbClientPtr client) { + KeyCode k; + int tweak = 0; + + if (use_xkb_modtweak) { + xkb_tweak_keyboard(down, keysym, client); + return; + } + if (debug_keyboard) { + rfbLog("modifier_tweak_keyboard: %s keysym=0x%x\n", + down ? "down" : "up", (int) keysym); + } + +#define ADJUSTMOD(sym, state) \ + if (keysym == sym) { \ + if (down) { \ + mod_state |= state; \ + } else { \ + mod_state &= ~state; \ + } \ + } + + ADJUSTMOD(XK_Shift_L, LEFTSHIFT) + ADJUSTMOD(XK_Shift_R, RIGHTSHIFT) + ADJUSTMOD(XK_Mode_switch, ALTGR) + + if ( down && keysym >= ' ' && keysym < 0x100 ) { + tweak = 1; + tweak_mod(modifiers[keysym], True); + k = keycodes[keysym]; + } else { + X_LOCK; + k = XKeysymToKeycode(dpy, (KeySym) keysym); + X_UNLOCK; + } + if (k == NoSymbol && add_keysyms && ! IsModifierKey(keysym)) { + int new_kc = add_keysym(keysym); + if (new_kc) { + k = new_kc; + } + } + + if (sloppy_keys) { + int new_kc; + if (sloppy_key_check((int) k, down, keysym, &new_kc)) { + k = (KeyCode) new_kc; + } + } + + if (debug_keyboard) { + rfbLog("modifier_tweak_keyboard: KeySym 0x%x \"%s\" -> " + "KeyCode 0x%x%s\n", (int) keysym, XKeysymToString(keysym), + (int) k, k ? "" : " *ignored*"); + } + if ( k != NoSymbol ) { + X_LOCK; + XTestFakeKeyEvent_wr(dpy, k, (Bool) down, CurrentTime); + X_UNLOCK; + } + + if ( tweak ) { + tweak_mod(modifiers[keysym], False); + } +} + +void initialize_keyboard_and_pointer(void) { + + if (raw_fb && ! dpy) return; /* raw_fb hack */ + + if (use_modifier_tweak) { + initialize_modtweak(); + } + if (remap_file != NULL) { + initialize_remap(remap_file); + } + + initialize_pointer_map(pointer_remap); + + clear_modifiers(1); + if (clear_mods == 1) { + clear_modifiers(0); + } +} + +void get_allowed_input(rfbClientPtr client, allowed_input_t *input) { + ClientData *cd; + char *str; + + input->keystroke = 0; + input->motion = 0; + input->button = 0; + + if (! client) { + return; + } + + cd = (ClientData *) client->clientData; + + if (cd->input[0] != '-') { + str = cd->input; + } else if (client->viewOnly) { + if (allowed_input_view_only) { + str = allowed_input_view_only; + } else { + str = ""; + } + } else { + if (allowed_input_normal) { + str = allowed_input_normal; + } else { + str = "KMB"; + } + } + + while (*str) { + if (*str == 'K') { + input->keystroke = 1; + } else if (*str == 'M') { + input->motion = 1; + } else if (*str == 'B') { + input->button = 1; + } + str++; + } +} + +/* for -pipeinput mode */ +static void pipe_keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { + int can_input = 0, uid; + allowed_input_t input; + char *name; + ClientData *cd = (ClientData *) client->clientData; + + if (pipeinput_fh == NULL) { + return; + } + + if (! view_only) { + get_allowed_input(client, &input); + if (input.motion || input.button) { + can_input = 1; /* XXX distinguish later */ + } + } + uid = cd->uid; + if (! can_input) { + uid = -uid; + } + + X_LOCK; + name = XKeysymToString(keysym); + X_UNLOCK; + + fprintf(pipeinput_fh, "Keysym %d %d %u %s %s\n", uid, down, + keysym, name, down ? "KeyPress" : "KeyRelease"); + + fflush(pipeinput_fh); + check_pipeinput(); +} + +typedef struct keyevent { + rfbKeySym sym; + rfbBool down; + double time; +} keyevent_t; + +#define KEY_HIST 256 +static int key_history_idx = -1; +static keyevent_t key_history[KEY_HIST]; + +double typing_rate(double time_window, int *repeating) { + double dt = 1.0, now = dnow(); + KeySym key = NoSymbol; + int i, idx, cnt = 0, repeat_keys = 0; + + if (key_history_idx == -1) { + if (repeating) { + *repeating = 0; + } + return 0.0; + } + if (time_window > 0.0) { + dt = time_window; + } + for (i=0; i<KEY_HIST; i++) { + idx = key_history_idx - i; + if (idx < 0) { + idx += KEY_HIST; + } + if (! key_history[idx].down) { + continue; + } + if (now > key_history[idx].time + dt) { + break; + } + cnt++; + if (key == NoSymbol) { + key = key_history[idx].sym; + repeat_keys = 1; + } else if (key == key_history[idx].sym) { + repeat_keys++; + } + } + + if (repeating) { + if (repeat_keys >= 2) { + *repeating = repeat_keys; + } else { + *repeating = 0; + } + } + + /* + * n.b. keyrate could seem very high with libvncserver buffering them + * so avoid using small dt. + */ + return ((double) cnt)/dt; +} + +int skip_cr_when_scaling(char *mode) { + int got = 0; + + if (!scaling) { + return 0; + } + + if (scaling_copyrect != scaling_copyrect0) { + /* user override via -scale: */ + if (! scaling_copyrect) { + return 1; + } else { + return 0; + } + } + if (*mode == 's') { + got = got_scrollcopyrect; + } else if (*mode == 'w') { + got = got_wirecopyrect; + } + if (scaling_copyrect || got) { + int lat, rate; + int link = link_rate(&lat, &rate); + if (link == LR_DIALUP) { + return 1; + } else if (rate < 25) { + /* the fill-in of the repair may be too slow */ + return 1; + } else { + return 0; + } + } else { + return 1; + } +} + +/* + * key event handler. See the above functions for contortions for + * running under -modtweak. + */ +static rfbClientPtr last_keyboard_client = NULL; + +void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { + KeyCode k; + int idx, isbutton = 0; + allowed_input_t input; + time_t now = time(0); + double tnow; + static int skipped_last_down; + static rfbBool last_down; + static rfbKeySym last_keysym = NoSymbol; + static rfbKeySym max_keyrepeat_last_keysym = NoSymbol; + static double max_keyrepeat_last_time = 0.0; + + dtime0(&tnow); + got_keyboard_calls++; + + if (debug_keyboard) { + char *str; + X_LOCK; + str = XKeysymToString(keysym); + rfbLog("# keyboard(%s, 0x%x \"%s\") %.4f\n", down ? "down":"up", + (int) keysym, str ? str : "null", tnow - x11vnc_start); + X_UNLOCK; + } + + if (skip_duplicate_key_events) { + if (keysym == last_keysym && down == last_down) { + if (debug_keyboard) { + rfbLog("skipping dup key event: %d 0x%x\n", + down, keysym); + } + return; + } + } + + last_down = down; + last_keysym = keysym; + last_keyboard_time = tnow; + + last_rfb_down = down; + last_rfb_keysym = keysym; + last_rfb_keytime = tnow; + last_rfb_key_accepted = FALSE; + + if (key_history_idx == -1) { + for (idx=0; idx<KEY_HIST; idx++) { + key_history[idx].sym = NoSymbol; + key_history[idx].down = FALSE; + key_history[idx].time = 0.0; + } + } + idx = ++key_history_idx; + if (key_history_idx >= KEY_HIST) { + key_history_idx = 0; + idx = 0; + } + key_history[idx].sym = keysym; + key_history[idx].down = down; + key_history[idx].time = tnow; + + if (down && (keysym == XK_Alt_L || keysym == XK_Super_L)) { + int i, k, run = 0, ups = 0; + double delay = 1.0; + KeySym ks; + for (i=0; i<16; i++) { + k = idx - i; + if (k < 0) k += KEY_HIST; + if (!key_history[k].down) { + ups++; + continue; + } + ks = key_history[k].sym; + if (key_history[k].time < tnow - delay) { + break; + } else if (ks == keysym && ks == XK_Alt_L) { + run++; + } else if (ks == keysym && ks == XK_Super_L) { + run++; + } else { + break; + } + } + if (ups < 2) { + ; + } else if (run == 3 && keysym == XK_Alt_L) { + rfbLog("3*Alt_L, calling: refresh_screen(0)\n"); + refresh_screen(0); + } else if (run == 4 && keysym == XK_Alt_L) { + rfbLog("4*Alt_L, setting: do_copy_screen\n"); + do_copy_screen = 1; + } else if (run == 5 && keysym == XK_Alt_L) { + ; + } else if (run == 3 && keysym == XK_Super_L) { + rfbLog("3*Super_L, calling: set_xdamage_mark()\n"); + set_xdamage_mark(0, 0, dpy_x, dpy_y); + } else if (run == 4 && keysym == XK_Super_L) { + rfbLog("4*Super_L, calling: check_xrecord_reset()\n"); + check_xrecord_reset(1); + } else if (run == 5 && keysym == XK_Super_L) { + rfbLog("5*Super_L, calling: push_black_screen(0)\n"); + push_black_screen(0); + } + } + + if (!down && skipped_last_down) { + int db = debug_scroll; + if (keysym == max_keyrepeat_last_keysym) { + skipped_last_down = 0; + if (db) rfbLog("--- scroll keyrate skipping 0x%lx %s " + "%.4f %.4f\n", keysym, down ? "down":"up ", + tnow - x11vnc_start, tnow - max_keyrepeat_last_time); + return; + } + } + if (down && max_keyrepeat_time > 0.0) { + int skip = 0; + int db = debug_scroll; + + if (max_keyrepeat_last_keysym != NoSymbol && + max_keyrepeat_last_keysym != keysym) { + ; + } else { + if (tnow < max_keyrepeat_last_time+max_keyrepeat_time) { + skip = 1; + } + } + max_keyrepeat_time = 0.0; + if (skip) { + if (db) rfbLog("--- scroll keyrate skipping 0x%lx %s " + "%.4f %.4f\n", keysym, down ? "down":"up ", + tnow - x11vnc_start, tnow - max_keyrepeat_last_time); + max_keyrepeat_last_keysym = keysym; + skipped_last_down = 1; + return; + } else { + if (db) rfbLog("--- scroll keyrate KEEPING 0x%lx %s " + "%.4f %.4f\n", keysym, down ? "down":"up ", + tnow - x11vnc_start, tnow - max_keyrepeat_last_time); + } + } + max_keyrepeat_last_keysym = keysym; + max_keyrepeat_last_time = tnow; + skipped_last_down = 0; + last_rfb_key_accepted = TRUE; + + if (pipeinput_fh != NULL) { + pipe_keyboard(down, keysym, client); + if (! pipeinput_tee) { + if (! view_only || raw_fb) { /* raw_fb hack */ + last_keyboard_client = client; + last_event = last_input = now; + last_keyboard_input = now; + + last_keysym = keysym; + + last_rfb_down = down; + last_rfb_keysym = keysym; + last_rfb_keytime = tnow; + + got_user_input++; + got_keyboard_input++; + } + return; + } + } + + if (view_only) { + return; + } + get_allowed_input(client, &input); + if (! input.keystroke) { + return; + } + + track_mod_state(keysym, down, TRUE); /* ignores remaps */ + + last_keyboard_client = client; + last_event = last_input = now; + last_keyboard_input = now; + + last_keysym = keysym; + + last_rfb_down = down; + last_rfb_keysym = keysym; + last_rfb_keytime = tnow; + + got_user_input++; + got_keyboard_input++; + + if (raw_fb && ! dpy) return; /* raw_fb hack */ + + if (keyremaps) { + keyremap_t *remap = keyremaps; + while (remap != NULL) { + if (remap->before == keysym) { + keysym = remap->after; + isbutton = remap->isbutton; + if (debug_keyboard) { + X_LOCK; + rfbLog("keyboard(): remapping keysym: " + "0x%x \"%s\" -> 0x%x \"%s\"\n", + (int) remap->before, + XKeysymToString(remap->before), + (int) remap->after, + remap->isbutton ? "button" : + XKeysymToString(remap->after)); + X_UNLOCK; + } + break; + } + remap = remap->next; + } + } + + if (use_xrecord && ! xrecording && down) { + + if (!strcmp(scroll_copyrect, "never")) { + ; + } else if (!strcmp(scroll_copyrect, "mouse")) { + ; + } else if (skip_cr_when_scaling("scroll")) { + ; + } else if (! xrecord_skip_keysym(keysym)) { + snapshot_stack_list(0, 0.25); + xrecord_watch(1, SCR_KEY); + xrecord_set_by_keys = 1; + xrecord_keysym = keysym; + } else { + if (debug_scroll) { + char *str = XKeysymToString(keysym); + rfbLog("xrecord_skip_keysym: %s\n", + str ? str : "NoSymbol"); + } + } + } + + if (isbutton) { + int mask, button = (int) keysym; + if (! down) { + return; /* nothing to send */ + } + if (debug_keyboard) { + rfbLog("keyboard(): remapping keystroke to button %d" + " click\n", button); + } + dtime0(&last_key_to_button_remap_time); + + X_LOCK; + /* + * This in principle can be a little dicey... i.e. even + * remap the button click to keystroke sequences! + * Usually just will simulate the button click. + */ + mask = 1<<(button-1); + do_button_mask_change(mask, button); /* down */ + mask = 0; + do_button_mask_change(mask, button); /* up */ + XFlush(dpy); + X_UNLOCK; + return; + } + + if (use_modifier_tweak) { + modifier_tweak_keyboard(down, keysym, client); + X_LOCK; + XFlush(dpy); + X_UNLOCK; + return; + } + + X_LOCK; + + k = XKeysymToKeycode(dpy, (KeySym) keysym); + + if (k == NoSymbol && add_keysyms && ! IsModifierKey(keysym)) { + int new_kc = add_keysym(keysym); + if (new_kc) { + k = new_kc; + } + } + if (debug_keyboard) { + char *str = XKeysymToString(keysym); + rfbLog("keyboard(): KeySym 0x%x \"%s\" -> KeyCode 0x%x%s\n", + (int) keysym, str ? str : "null", (int) k, + k ? "" : " *ignored*"); + } + + if ( k != NoSymbol ) { + XTestFakeKeyEvent_wr(dpy, k, (Bool) down, CurrentTime); + XFlush(dpy); + } + + X_UNLOCK; +} + + |