/*
 * Copyright (C) 2003 Sun Microsystems, Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 *
 * Authors:
 *      Mark McLoughlin <mark@skynet.ie>
 *
 *
 *   The keyboard and pointer handling code is borrowed from
 *   x11vnc.c in libvncserver/contrib which is:
 *
 *     Copyright (c) 2002-2003 Karl J. Runge <runge@karlrunge.com>
 *
 *   x11vnc.c itself is based heavily on:
 *       the originial x11vnc.c in libvncserver (Johannes E. Schindelin)
 *       krfb, the KDE desktopsharing project (Tim Jansen)
 *       x0rfbserver, the original native X vnc server (Jens Wagner)
 */

/*
 * Theory of keyboard operation
 * 
 * The remote VNC client sends us a series of key press and release
 * events identified as X keysyms. (Even Windows clients use X
 * keysyms; that's just how the protocol works.) We then use our
 * knowledge of the keyboard layout to translate the keysyms into
 * keycodes, and use XTEST to send events for the appropriate
 * keycodes.
 * 
 * 
 * Keyboard layouts
 * 
 * The XKEYBOARD extension describes the keyboard as having up to 4
 * "groups" (layouts), and each key on the keyboard has some number of
 * "levels" corresponding to combinations of modifier keys. Different
 * keys may have different numbers of levels, with different meanings.
 * The "Alt-Gr" key on the keyboard is bound to the keysym
 * ISO_Level3_Shift, which is a modifer that switches to a level
 * called "Level3" on some keys.
 * 
 * The core X protocol (on non-XKB servers) has a simpler model, in
 * which there are normally 2 groups, and each group has 2 levels
 * (unshifted and shifted). The "Alt-Gr" key is bound to the keysym
 * Mode_switch, which is a modifier key that switches to the second
 * group when it is held down.
 * 
 * Our model is a simplification of the XKB model that also fits in
 * well with the core model; there are between 1 and 4 groups
 * (keyboard layouts), and each group has exactly 4 levels (plain,
 * Shift or NumLock, Alt-Gr, and Shift+Alt-Gr).
 * 
 * 
 * Details of key event processing
 * 
 * When a modifier keypress is received, vino sends a KeyPress to the
 * server, and when the corresponding keyrelease event is received, it
 * sends a KeyRelease to the server. But for non-modifier keys, it
 * always sends both a KeyPress and a KeyRelease together when it
 * receives just the keypress event. (This is because we assume that
 * the client side will handle "key repeat", and if we left the key
 * pressed down on the server as well, we'd get double repeating.)
 *
 * Shift and Alt-Gr are treated slightly different from the other
 * modifier keys; they are considered to be just implementation
 * details of how you type a particular keysym. Eg, if you type
 * Control+Shift+? (on a US keyboard), you're typing Control because
 * you want the Control key to be pressed, but you're only typing
 * Shift because it's required to get a "?" rather than a "/".
 * Different keyboard layouts require Shift and Alt-Gr to be used for
 * different keysyms, so if the client and server keyboards are not of
 * the same type, vino will sometimes need to send fake press and
 * release events for Shift and Alt-Gr in order to be able to type the
 * keys the client intended to type. (On keys that have no shifted
 * state, like the arrow and function keys, Shift and Alt-Gr behave
 * as ordinary modifiers, the same way Control and Alt do.)
 * 
 * Two further bits of special behavior are needed for NumLock; first,
 * the second key level, which is normally "Shift", actually
 * corresponds to "NumLock" on keypad keys. Second, since the server's
 * NumLock key could plausibly be locked in either state, we need to
 * take its state into account when typing keypad keys. If the server
 * supports XKB, we use the XkbStateNotify event to track NumLock
 * state changes. If the server doesn't support XKB, we have to
 * explicitly query the modifier state any time the client types a
 * keypad key.
 * 
 * On servers that support XKB, we also have to worry about multiple
 * keyboard layouts. We need to keep track of what keysyms are
 * available in what layouts, and what layout (or "group") the server
 * is currently using. When a keysym arrives, we look for a way to
 * type that keysym in the current group, but if it's only available
 * in a different group, we have to change groups temporarily to type
 * it (in the same way we have to temporarily change modifiers
 * sometimes). Doing this programmatically is a little bit tricky; XKB
 * provides XkbLatchGroup(), but if the layout doesn't use
 * "XkbWrapIntoRange" semantics, then that can only switch to
 * higher-numbered groups. Alternatively, we could find the
 * group-switching keys on the keyboard and use them the way we use
 * the Shift and Alt-Gr keys, but there are several different ways
 * that group switching could be set up, and we'd need to support all
 * of them. Anyway, all of the layouts in the xkeyboard-config package
 * use XkbWrapIntoRange, so XkbLatchGroup() works for us in that case,
 * so we just require that, and don't support multiple groups if the
 * layout uses XkbRedirectIntoRange or XkbClampIntoRange.
 * 
 * Two additional problems show up with Windows-based clients; first,
 * Windows considers Alt-Gr to be equivalent to Control_L + Alt_R, so
 * we have to translate that. Second, Windows clients never send
 * events for dead keys. Instead, if you type a dead acute accent key
 * followed by "e", it sends a single keypress event for XK_eacute.
 * This works fine when the current keyboard layout has a key for that
 * precomposed keysym, but if it doesn't, we need to decompose it back
 * into multiple keypresses. There is no guarantee that the
 * decompositions we use will actually work, but since compositions
 * happen in the library, not the server, there is no way to find out
 * 100% reliably what compositions are available to the window
 * currently being typed into, so this is the best we can do.
 */

#include <config.h>

#include "vino-input.h"

#include <string.h>
#include <gdk/gdkx.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#ifdef HAVE_XTEST
#include <X11/extensions/XTest.h>
#endif
#ifdef HAVE_XKB
#include <X11/XKBlib.h>
#endif

#include "vino-util.h"

#define VINO_IS_MODIFIER_KEYSYM(k) (((k) >= XK_Shift_L && (k) <= XK_Hyper_R) || \
                                     (k) == XK_Num_Lock                      || \
                                     (k) == XK_Mode_switch                   || \
                                     (k) == XK_ISO_Level3_Shift)

#define VINO_IS_KEYPAD_KEYSYM(k)    ((k) >= XK_KP_Space && (k) <= XK_KP_Equal)

typedef enum
{
  VINO_LEFT_SHIFT    = 1 << 0,
  VINO_RIGHT_SHIFT   = 1 << 1,
  VINO_LEFT_CONTROL  = 1 << 2,
  VINO_RIGHT_ALT     = 1 << 3,
  VINO_ALT_GR        = 1 << 4,
  VINO_NUM_LOCK      = 1 << 5
} VinoModifierState;

typedef enum
{
  VINO_LEVEL_PLAIN        = 0,
  VINO_LEVEL_SHIFT        = 1,
  VINO_LEVEL_ALTGR        = 2,
  VINO_LEVEL_SHIFT_ALTGR  = 3,
  VINO_LEVEL_NUM_LOCK     = 4
} VinoLevel;

#define VINO_NUM_LEVELS 4  /* NumLock is special and doesn't count */
#define VINO_NUM_GROUPS 4

#define VINO_LEVEL(state) ((((state) & (VINO_LEFT_SHIFT | VINO_RIGHT_SHIFT)) ? VINO_LEVEL_SHIFT : 0) | \
			   (((state) & VINO_ALT_GR) ? VINO_LEVEL_ALTGR : 0) | \
			   (((state) & VINO_NUM_LOCK) ? VINO_LEVEL_NUM_LOCK : 0))

#define VINO_LEVEL_IS_SHIFT(level)    (level & VINO_LEVEL_SHIFT)
#define VINO_LEVEL_IS_ALTGR(level)    (level & VINO_LEVEL_ALTGR)
#define VINO_LEVEL_IS_NUM_LOCK(level) (level & VINO_LEVEL_NUM_LOCK)

typedef struct
{
  KeyCode  keycode;
  guint    level;
  gboolean keypad;
} VinoKeybinding;

typedef struct
{
  guint8             button_mask;
  VinoModifierState  modifier_state;
#ifdef HAVE_XKB
  int                current_group;
#endif

  GHashTable        *keybindings;
  GHashTable        *decompositions;
  KeyCode            left_shift_keycode;
  KeyCode            right_shift_keycode;
  KeyCode            left_control_keycode;
  KeyCode            alt_gr_keycode;
  KeyCode            num_lock_keycode;

  KeySym             alt_gr_keysym;
  guint              num_lock_mod;

#ifdef HAVE_XKB
  int                xkb_num_groups;
  int                xkb_event_type;
#endif

  guint              initialized : 1;
  guint              xtest_supported : 1;
  guint              xkb_supported : 1;
  guint              n_pointer_buttons;
} VinoInputData;

/* Data is per-display, but we only handle a single display.
 */
static VinoInputData global_input_data = { 0, };

#ifdef HAVE_XTEST

static struct {
  guint32 composed;
  guint32 decomposed[3];
} decompositions[] = {
  { XK_Cabovedot, { XK_dead_abovedot, 'C', 0 } },
  { XK_Eabovedot, { XK_dead_abovedot, 'E', 0 } },
  { XK_Gabovedot, { XK_dead_abovedot, 'G', 0 } },
  { XK_Iabovedot, { XK_dead_abovedot, 'I', 0 } },
  { XK_Zabovedot, { XK_dead_abovedot, 'Z', 0 } },
  { XK_cabovedot, { XK_dead_abovedot, 'c', 0 } },
  { XK_eabovedot, { XK_dead_abovedot, 'e', 0 } },
  { XK_gabovedot, { XK_dead_abovedot, 'g', 0 } },
  { XK_idotless,  { XK_dead_abovedot, 'i', 0 } },
  { XK_zabovedot, { XK_dead_abovedot, 'z', 0 } },
  { XK_abovedot,  { XK_dead_abovedot, XK_dead_abovedot, 0 } },

  { XK_Aring,  { XK_dead_abovering, 'A', 0 } },
  { XK_Uring,  { XK_dead_abovering, 'U', 0 } },
  { XK_aring,  { XK_dead_abovering, 'a', 0 } },
  { XK_uring,  { XK_dead_abovering, 'u', 0 } },
  { XK_degree, { XK_dead_abovering, XK_dead_abovering, 0 } },

  { XK_Aacute, { XK_dead_acute, 'A', 0 } },
  { XK_Cacute, { XK_dead_acute, 'C', 0 } },
  { XK_Eacute, { XK_dead_acute, 'E', 0 } },
  { XK_Iacute, { XK_dead_acute, 'I', 0 } },
  { XK_Lacute, { XK_dead_acute, 'L', 0 } },
  { XK_Nacute, { XK_dead_acute, 'N', 0 } },
  { XK_Oacute, { XK_dead_acute, 'O', 0 } },
  { XK_Racute, { XK_dead_acute, 'R', 0 } },
  { XK_Sacute, { XK_dead_acute, 'S', 0 } },
  { XK_Uacute, { XK_dead_acute, 'U', 0 } },
  { XK_Yacute, { XK_dead_acute, 'Y', 0 } },
  { XK_Zacute, { XK_dead_acute, 'Z', 0 } },
  { XK_aacute, { XK_dead_acute, 'a', 0 } },
  { XK_cacute, { XK_dead_acute, 'c', 0 } },
  { XK_eacute, { XK_dead_acute, 'e', 0 } },
  { XK_iacute, { XK_dead_acute, 'i', 0 } },
  { XK_lacute, { XK_dead_acute, 'l', 0 } },
  { XK_nacute, { XK_dead_acute, 'n', 0 } },
  { XK_oacute, { XK_dead_acute, 'o', 0 } },
  { XK_racute, { XK_dead_acute, 'r', 0 } },
  { XK_sacute, { XK_dead_acute, 's', 0 } },
  { XK_uacute, { XK_dead_acute, 'u', 0 } },
  { XK_yacute, { XK_dead_acute, 'y', 0 } },
  { XK_zacute, { XK_dead_acute, 'z', 0 } },
  { XK_acute,  { XK_dead_acute, XK_dead_acute, 0 } },

  { XK_Abreve, { XK_dead_breve, 'A', 0 } },
  { XK_Gbreve, { XK_dead_breve, 'G', 0 } },
  { XK_Ubreve, { XK_dead_breve, 'U', 0 } },
  { XK_abreve, { XK_dead_breve, 'a', 0 } },
  { XK_gbreve, { XK_dead_breve, 'g', 0 } },
  { XK_ubreve, { XK_dead_breve, 'u', 0 } },
  { XK_breve,  { XK_dead_breve, XK_dead_breve, 0 } },

  { XK_Ccaron, { XK_dead_caron, 'C', 0 } },
  { XK_Dcaron, { XK_dead_caron, 'D', 0 } },
  { XK_Ecaron, { XK_dead_caron, 'E', 0 } },
  { XK_Lcaron, { XK_dead_caron, 'L', 0 } },
  { XK_Ncaron, { XK_dead_caron, 'N', 0 } },
  { XK_Rcaron, { XK_dead_caron, 'R', 0 } },
  { XK_Scaron, { XK_dead_caron, 'S', 0 } },
  { XK_Tcaron, { XK_dead_caron, 'T', 0 } },
  { XK_Zcaron, { XK_dead_caron, 'Z', 0 } },
  { XK_ccaron, { XK_dead_caron, 'c', 0 } },
  { XK_dcaron, { XK_dead_caron, 'd', 0 } },
  { XK_ecaron, { XK_dead_caron, 'e', 0 } },
  { XK_lcaron, { XK_dead_caron, 'l', 0 } },
  { XK_ncaron, { XK_dead_caron, 'n', 0 } },
  { XK_rcaron, { XK_dead_caron, 'r', 0 } },
  { XK_scaron, { XK_dead_caron, 's', 0 } },
  { XK_tcaron, { XK_dead_caron, 't', 0 } },
  { XK_zcaron, { XK_dead_caron, 'z', 0 } },
  { XK_caron,  { XK_dead_caron, XK_dead_caron, 0 } },

  { XK_Ccedilla, { XK_dead_cedilla, 'C', 0 } },
  { XK_Gcedilla, { XK_dead_cedilla, 'G', 0 } },
  { XK_Kcedilla, { XK_dead_cedilla, 'K', 0 } },
  { XK_Lcedilla, { XK_dead_cedilla, 'L', 0 } },
  { XK_Ncedilla, { XK_dead_cedilla, 'N', 0 } },
  { XK_Rcedilla, { XK_dead_cedilla, 'R', 0 } },
  { XK_Scedilla, { XK_dead_cedilla, 'S', 0 } },
  { XK_Tcedilla, { XK_dead_cedilla, 'T', 0 } },
  { XK_ccedilla, { XK_dead_cedilla, 'c', 0 } },
  { XK_gcedilla, { XK_dead_cedilla, 'g', 0 } },
  { XK_kcedilla, { XK_dead_cedilla, 'k', 0 } },
  { XK_lcedilla, { XK_dead_cedilla, 'l', 0 } },
  { XK_ncedilla, { XK_dead_cedilla, 'n', 0 } },
  { XK_rcedilla, { XK_dead_cedilla, 'r', 0 } },
  { XK_scedilla, { XK_dead_cedilla, 's', 0 } },
  { XK_tcedilla, { XK_dead_cedilla, 't', 0 } },
  { XK_cedilla,  { XK_dead_cedilla, XK_dead_cedilla, 0 } },

  { XK_Acircumflex, { XK_dead_circumflex, 'A', 0 } },
  { XK_Ccircumflex, { XK_dead_circumflex, 'C', 0 } },
  { XK_Ecircumflex, { XK_dead_circumflex, 'E', 0 } },
  { XK_Gcircumflex, { XK_dead_circumflex, 'G', 0 } },
  { XK_Hcircumflex, { XK_dead_circumflex, 'H', 0 } },
  { XK_Icircumflex, { XK_dead_circumflex, 'I', 0 } },
  { XK_Jcircumflex, { XK_dead_circumflex, 'J', 0 } },
  { XK_Ocircumflex, { XK_dead_circumflex, 'O', 0 } },
  { XK_Scircumflex, { XK_dead_circumflex, 'S', 0 } },
  { XK_Ucircumflex, { XK_dead_circumflex, 'U', 0 } },
  { XK_acircumflex, { XK_dead_circumflex, 'a', 0 } },
  { XK_ccircumflex, { XK_dead_circumflex, 'c', 0 } },
  { XK_ecircumflex, { XK_dead_circumflex, 'e', 0 } },
  { XK_gcircumflex, { XK_dead_circumflex, 'g', 0 } },
  { XK_hcircumflex, { XK_dead_circumflex, 'h', 0 } },
  { XK_icircumflex, { XK_dead_circumflex, 'i', 0 } },
  { XK_jcircumflex, { XK_dead_circumflex, 'j', 0 } },
  { XK_ocircumflex, { XK_dead_circumflex, 'o', 0 } },
  { XK_scircumflex, { XK_dead_circumflex, 's', 0 } },
  { XK_ucircumflex, { XK_dead_circumflex, 'u', 0 } },
  { XK_asciicircum, { XK_dead_circumflex, XK_dead_circumflex, 0 } },

  { XK_Adiaeresis, { XK_dead_diaeresis, 'A', 0 } },
  { XK_Ediaeresis, { XK_dead_diaeresis, 'E', 0 } },
  { XK_Idiaeresis, { XK_dead_diaeresis, 'I', 0 } },
  { XK_Odiaeresis, { XK_dead_diaeresis, 'O', 0 } },
  { XK_Udiaeresis, { XK_dead_diaeresis, 'U', 0 } },
  { XK_adiaeresis, { XK_dead_diaeresis, 'a', 0 } },
  { XK_ediaeresis, { XK_dead_diaeresis, 'e', 0 } },
  { XK_idiaeresis, { XK_dead_diaeresis, 'i', 0 } },
  { XK_odiaeresis, { XK_dead_diaeresis, 'o', 0 } },
  { XK_udiaeresis, { XK_dead_diaeresis, 'u', 0 } },
  { XK_ydiaeresis, { XK_dead_diaeresis, 'y', 0 } },
  { XK_diaeresis,  { XK_dead_diaeresis, XK_dead_diaeresis, 0 } },

  { XK_Odoubleacute, { XK_dead_doubleacute, 'O', 0 } },
  { XK_Udoubleacute, { XK_dead_doubleacute, 'U', 0 } },
  { XK_odoubleacute, { XK_dead_doubleacute, 'o', 0 } },
  { XK_udoubleacute, { XK_dead_doubleacute, 'u', 0 } },
  { XK_doubleacute,  { XK_dead_doubleacute, XK_dead_doubleacute, 0 } },

  { XK_Agrave, { XK_dead_grave, 'A', 0 } },
  { XK_Egrave, { XK_dead_grave, 'E', 0 } },
  { XK_Igrave, { XK_dead_grave, 'I', 0 } },
  { XK_Ograve, { XK_dead_grave, 'O', 0 } },
  { XK_Ugrave, { XK_dead_grave, 'U', 0 } },
  { XK_agrave, { XK_dead_grave, 'a', 0 } },
  { XK_egrave, { XK_dead_grave, 'e', 0 } },
  { XK_igrave, { XK_dead_grave, 'i', 0 } },
  { XK_ograve, { XK_dead_grave, 'o', 0 } },
  { XK_ugrave, { XK_dead_grave, 'u', 0 } },
  { XK_grave,  { XK_dead_grave, XK_dead_grave, 0 } },

  { XK_Amacron, { XK_dead_macron, 'A', 0 } },
  { XK_Emacron, { XK_dead_macron, 'E', 0 } },
  { XK_Imacron, { XK_dead_macron, 'I', 0 } },
  { XK_Omacron, { XK_dead_macron, 'O', 0 } },
  { XK_Umacron, { XK_dead_macron, 'U', 0 } },
  { XK_amacron, { XK_dead_macron, 'a', 0 } },
  { XK_emacron, { XK_dead_macron, 'e', 0 } },
  { XK_imacron, { XK_dead_macron, 'i', 0 } },
  { XK_omacron, { XK_dead_macron, 'o', 0 } },
  { XK_umacron, { XK_dead_macron, 'u', 0 } },
  { XK_macron,  { XK_dead_macron, XK_dead_macron, 0 } },

  { XK_Aogonek, { XK_dead_ogonek, 'A', 0 } },
  { XK_Eogonek, { XK_dead_ogonek, 'E', 0 } },
  { XK_Iogonek, { XK_dead_ogonek, 'I', 0 } },
  { XK_Uogonek, { XK_dead_ogonek, 'U', 0 } },
  { XK_aogonek, { XK_dead_ogonek, 'a', 0 } },
  { XK_eogonek, { XK_dead_ogonek, 'e', 0 } },
  { XK_iogonek, { XK_dead_ogonek, 'i', 0 } },
  { XK_uogonek, { XK_dead_ogonek, 'u', 0 } },
  { XK_ogonek,  { XK_dead_ogonek, XK_dead_ogonek, 0 } },

  { XK_Atilde, 	 { XK_dead_tilde, 'A', 0 } },
  { XK_Itilde, 	 { XK_dead_tilde, 'I', 0 } },
  { XK_Ntilde, 	 { XK_dead_tilde, 'N', 0 } },
  { XK_Otilde, 	 { XK_dead_tilde, 'O', 0 } },
  { XK_Utilde, 	 { XK_dead_tilde, 'U', 0 } },
  { XK_atilde, 	 { XK_dead_tilde, 'a', 0 } },
  { XK_itilde, 	 { XK_dead_tilde, 'i', 0 } },
  { XK_ntilde, 	 { XK_dead_tilde, 'n', 0 } },
  { XK_otilde, 	 { XK_dead_tilde, 'o', 0 } },
  { XK_utilde, 	 { XK_dead_tilde, 'u', 0 } },
  { XK_asciitilde, { XK_dead_tilde, XK_dead_tilde, 0 } },

  { XK_Greek_ALPHAaccent,   { XK_dead_acute, XK_Greek_ALPHA, 0 } },
  { XK_Greek_EPSILONaccent, { XK_dead_acute, XK_Greek_EPSILON, 0 } },
  { XK_Greek_ETAaccent,     { XK_dead_acute, XK_Greek_ETA, 0 } },
  { XK_Greek_IOTAaccent,    { XK_dead_acute, XK_Greek_IOTA, 0 } },
  { XK_Greek_OMICRONaccent, { XK_dead_acute, XK_Greek_OMICRON, 0 } },
  { XK_Greek_UPSILONaccent, { XK_dead_acute, XK_Greek_UPSILON, 0 } },
  { XK_Greek_OMEGAaccent,   { XK_dead_acute, XK_Greek_OMEGA, 0 } },
  { XK_Greek_alphaaccent,   { XK_dead_acute, XK_Greek_alpha, 0 } },
  { XK_Greek_epsilonaccent, { XK_dead_acute, XK_Greek_epsilon, 0 } },
  { XK_Greek_etaaccent,     { XK_dead_acute, XK_Greek_eta, 0 } },
  { XK_Greek_iotaaccent,    { XK_dead_acute, XK_Greek_iota, 0 } },
  { XK_Greek_omicronaccent, { XK_dead_acute, XK_Greek_omicron, 0 } },
  { XK_Greek_upsilonaccent, { XK_dead_acute, XK_Greek_upsilon, 0 } },
  { XK_Greek_omegaaccent,   { XK_dead_acute, XK_Greek_omega, 0 } },

  { XK_Greek_IOTAdieresis,    { XK_dead_diaeresis, XK_Greek_IOTA, 0 } },
  { XK_Greek_UPSILONdieresis, { XK_dead_diaeresis, XK_Greek_UPSILON, 0 } },
  { XK_Greek_iotadieresis,    { XK_dead_diaeresis, XK_Greek_iota, 0 } },
  { XK_Greek_upsilondieresis, { XK_dead_diaeresis, XK_Greek_upsilon, 0 } },

  { XK_Greek_iotaaccentdieresis,    { XK_dead_acute, XK_dead_diaeresis, XK_Greek_iota } },
  { XK_Greek_upsilonaccentdieresis, { XK_dead_acute, XK_dead_diaeresis, XK_Greek_upsilon } }
};
static const int num_decompositions = G_N_ELEMENTS (decompositions);

static void vino_input_initialize_keycodes (Display *xdisplay);

static void
vino_input_initialize_keycodes_core (Display *xdisplay)
{
  int      min_keycodes, max_keycodes;
  int      keysyms_per_keycode;
  KeySym   sym;
  int      keycode, level, i;
  XModifierKeymap *modmap;

  global_input_data.alt_gr_keysym = XK_Mode_switch;

  XDisplayKeycodes (xdisplay, &min_keycodes, &max_keycodes);
  XGetKeyboardMapping (xdisplay, min_keycodes, 0, &keysyms_per_keycode);

  /* We iterate by level first, then by keycode, to ensure that we
   * find an unshifted match for each keysym, if possible.
   */
  for (level = 0; level < MAX (keysyms_per_keycode, VINO_NUM_LEVELS); level++)
    {
      for (keycode = min_keycodes; keycode < max_keycodes; keycode++)
	{
	  VinoKeybinding *binding;
	  gboolean unmodifiable = FALSE;

	  sym = XKeycodeToKeysym (xdisplay, keycode, level);
	  if (sym == NoSymbol)
	    continue;

	  if (g_hash_table_lookup (global_input_data.keybindings,
				   GUINT_TO_POINTER (sym)))
	    continue;

	  binding = g_new (VinoKeybinding, VINO_NUM_GROUPS);
	  g_hash_table_insert (global_input_data.keybindings,
			       GUINT_TO_POINTER (sym), binding);

	  /* There's only one group in plain X, so use "binding[0]". */
	  binding[0].keycode = keycode;
	  binding[0].keypad = VINO_IS_KEYPAD_KEYSYM (sym);

	  /* Check if this is a key like XK_uparrow that doesn't change
	   * when you press Shift/Alt-Gr.
	   */
	  if (level == VINO_LEVEL_PLAIN)
	    {
	      KeySym shiftsym, altgrsym;

	      shiftsym = XKeycodeToKeysym (xdisplay, keycode, VINO_LEVEL_SHIFT);
	      altgrsym = (keysyms_per_keycode <= VINO_LEVEL_ALTGR) ? NoSymbol :
		XKeycodeToKeysym (xdisplay, keycode, VINO_LEVEL_ALTGR);

	      unmodifiable = (shiftsym == NoSymbol) && (altgrsym == NoSymbol);
	    }

	  if (unmodifiable)
	    binding[0].level = -1;
	  else if (binding[0].keypad && level == VINO_LEVEL_SHIFT)
	    binding[0].level = VINO_LEVEL_NUM_LOCK;
	  else
	    binding[0].level = level;

	  dprintf (INPUT, "\t0x%.2x (%s) -> key %d level %d\n", (guint)sym,
		   XKeysymToString (sym), keycode, level);
	}
    }

  /* Find the modifier mask corresponding to NumLock */
  keycode = XKeysymToKeycode (xdisplay, XK_Num_Lock);
  modmap = XGetModifierMapping (xdisplay);
  for (i = 0; i < 8 * modmap->max_keypermod; i++)
    {
      if (modmap->modifiermap[i] == keycode)
	{
	  global_input_data.num_lock_mod = 1 << (i / modmap->max_keypermod);
	  break;
	}
    }
  XFreeModifiermap (modmap);
}

#ifdef HAVE_XKB
static void
vino_input_initialize_keycodes_xkb (Display *xdisplay)
{
  XkbDescPtr xkb;
  XkbStateRec state;
  int levelmap[XkbMaxKeyTypes][VINO_NUM_LEVELS], kc, kt, level, group;
  int LevelThreeMask;
  VinoKeybinding *binding;
  KeySym sym;

  global_input_data.alt_gr_keysym = XK_ISO_Level3_Shift;

  xkb = XkbGetMap (xdisplay, XkbAllClientInfoMask, XkbUseCoreKbd);
  g_assert (xkb != NULL);

  LevelThreeMask = XkbKeysymToModifiers (xdisplay, XK_ISO_Level3_Shift);

  /* XKB's levels don't map to VinoLevel, and in fact, may be
   * different for different types of keys (eg, the second level on
   * the function keys is Ctrl+Alt, not Shift). So we create
   * "levelmap" to map VinoLevel values to keytype-specific levels.
   */
  for (kt = 0; kt < xkb->map->num_types; kt++)
    {
      XkbKeyTypePtr type;
      XkbModsPtr mods;
      int ktl;

      levelmap[kt][VINO_LEVEL_PLAIN] = 0;
      levelmap[kt][VINO_LEVEL_SHIFT] = -1;
      levelmap[kt][VINO_LEVEL_ALTGR] = -1;
      levelmap[kt][VINO_LEVEL_SHIFT_ALTGR] = -1;

      type = &xkb->map->types[kt];
      for (ktl = 0; ktl < type->map_count; ktl++)
	{
	  if (!type->map[ktl].active)
	    continue;

	  mods = &type->map[ktl].mods;
	  if (mods->mask == ShiftMask)
	    levelmap[kt][VINO_LEVEL_SHIFT] = type->map[ktl].level;
	  else if (mods->mask == LevelThreeMask)
	    levelmap[kt][VINO_LEVEL_ALTGR] = type->map[ktl].level;
	  else if (mods->mask == (ShiftMask | LevelThreeMask))
	    levelmap[kt][VINO_LEVEL_SHIFT_ALTGR] = type->map[ktl].level;
	}

      if (levelmap[kt][VINO_LEVEL_SHIFT] == -1)
	levelmap[kt][VINO_LEVEL_SHIFT] = levelmap[kt][VINO_LEVEL_PLAIN];
      if (levelmap[kt][VINO_LEVEL_ALTGR] == -1)
	levelmap[kt][VINO_LEVEL_ALTGR] = levelmap[kt][VINO_LEVEL_PLAIN];
      if (levelmap[kt][VINO_LEVEL_SHIFT_ALTGR] == -1)
	levelmap[kt][VINO_LEVEL_SHIFT_ALTGR] = levelmap[kt][VINO_LEVEL_SHIFT];
    }

  /* Now map out the keysyms. As in the core case, we iterate levels
   * before keycodes, so our first match for a keysym in each group
   * will be unshifted if possible.
   */
  for (group = 0; group < VINO_NUM_GROUPS; group++)
    {
      for (level = 0; level < VINO_NUM_LEVELS; level++)
	{
	  for (kc = xkb->min_key_code; kc <= xkb->max_key_code; kc++)
	    {
	      int ngroups, kgroup;

	      ngroups = XkbKeyNumGroups (xkb, kc);
	      if (ngroups == 0)
		continue;

	      /* If the key has fewer groups than the keyboard as a
	       * whole does, figure out which of the key's groups
	       * ("kgroup") will end up being used when the keyboard
	       * as a whole is using group "group".
	       */
	      if (group >= ngroups)
		{
		  if (xkb->map->key_sym_map[kc].group_info & XkbRedirectIntoRange)
		    kgroup = 0;
		  else if (xkb->map->key_sym_map[kc].group_info & XkbClampIntoRange)
		    kgroup = ngroups - 1;
		  else
		    kgroup = group % ngroups;
		}
	      else
		kgroup = group;

	      kt = xkb->map->key_sym_map[kc].kt_index[kgroup];
	      sym = XkbKeySymEntry (xkb, kc, levelmap[kt][level], kgroup);
	      if (!sym)
		continue;

	      binding = g_hash_table_lookup (global_input_data.keybindings,
					     GUINT_TO_POINTER (sym));
	      if (!binding)
		{
		  binding = g_new0 (VinoKeybinding, VINO_NUM_GROUPS);
		  g_hash_table_insert (global_input_data.keybindings,
				       GUINT_TO_POINTER (sym), binding);
		}

	      if (!binding[group].keycode)
		{
		  binding[group].keycode = kc;
		  binding[group].keypad = (kt == XkbKeypadIndex);
		  if (xkb->map->types[kt].num_levels == 1)
		    binding[group].level = -1;
		  else if (binding[group].keypad && level == VINO_LEVEL_SHIFT)
		    binding[group].level = VINO_LEVEL_NUM_LOCK;
		  else
		    binding[group].level = level;
		}

	      if (kgroup == group)
		{
		  dprintf (INPUT, "\t0x%.2x (%s) -> key %d group %d level %d\n",
			   (guint)sym, XKeysymToString (sym), kc, group, level);
		}
	    }
	}
    }

  /* Figure out how switching between groups works; we only support
   * XkbWrapIntoRange. (In theory, xkb->ctrls->group_info should equal
   * XkbWrapIntoRange (ie, 0) when that's the case, but in practice
   * there seems to be junk in the lower 4 bits, so we test for the
   * absence of the other two flags instead.)
   */
  if (XkbGetControls (xdisplay, XkbGroupsWrapMask, xkb) == Success)
    {
      if (!(xkb->ctrls->groups_wrap & (XkbClampIntoRange | XkbRedirectIntoRange)))
	{
	  dprintf (INPUT, "%d groups configured\n", xkb->ctrls->num_groups);
	  global_input_data.xkb_num_groups = xkb->ctrls->num_groups;
	}
      else if (xkb->ctrls->num_groups > 1)
	{
	  dprintf (INPUT, "groups_wrap is not WrapIntoRange, can't use\n");
	  global_input_data.xkb_num_groups = -1;
	}
    }

  /* Find NumLock modifier mask and get initial NumLock state */
  global_input_data.num_lock_mod = XkbKeysymToModifiers (xdisplay, XK_Num_Lock);
  if (XkbGetState (xdisplay, XkbUseCoreKbd, &state) == Success)
    {
      if (state.locked_mods & global_input_data.num_lock_mod)
	global_input_data.modifier_state |= VINO_NUM_LOCK;
    }

  XkbFreeKeyboard (xkb, 0, True);
}

static GdkFilterReturn
xkb_event_filter (GdkXEvent *xevent, GdkEvent *event, gpointer user_data)
{
  VinoInputData *data = user_data;
  XkbStateNotifyEvent *state;

  if (((XEvent *)xevent)->type == data->xkb_event_type)
    {
      switch (((XkbAnyEvent *)xevent)->xkb_type)
	{
	case XkbStateNotify:
	  state = (XkbStateNotifyEvent *)xevent;

	  if (state->changed & XkbGroupStateMask)
	    {
	      data->current_group = state->group;
	      dprintf (INPUT, "current_group -> %d\n", data->current_group);
	    }
	  if (state->changed & XkbModifierLockMask)
	    {
	      if (state->locked_mods & data->num_lock_mod)
		data->modifier_state |= VINO_NUM_LOCK;
	      else
		data->modifier_state &= ~VINO_NUM_LOCK;
	      dprintf (INPUT, "locked_mods -> %u, modifier_state -> %u\n",
		       state->locked_mods, data->modifier_state);
	    }
	  break;

	case XkbMapNotify:
	  XkbRefreshKeyboardMapping ((XkbMapNotifyEvent *)xevent);
	  /* fall through */

	case XkbControlsNotify:
	  vino_input_initialize_keycodes (((XkbAnyEvent *)xevent)->display);
	  break;
	}
    }

  return GDK_FILTER_CONTINUE;
}
#endif

static void
vino_input_initialize_keycodes (Display *xdisplay)
{
  dprintf (INPUT, "Initializing keysym to keycode/modifier mapping\n");

  if (global_input_data.keybindings)
    g_hash_table_destroy (global_input_data.keybindings);
  global_input_data.keybindings =
    g_hash_table_new_full (NULL, NULL, NULL, g_free);

#ifdef HAVE_XKB
  if (global_input_data.xkb_supported)
    vino_input_initialize_keycodes_xkb (xdisplay);
#endif
  if (!global_input_data.xkb_supported)
    vino_input_initialize_keycodes_core (xdisplay);

  global_input_data.left_shift_keycode   = XKeysymToKeycode (xdisplay, XK_Shift_L);
  global_input_data.right_shift_keycode  = XKeysymToKeycode (xdisplay, XK_Shift_R);
  global_input_data.left_control_keycode = XKeysymToKeycode (xdisplay, XK_Control_L);
  global_input_data.num_lock_keycode     = XKeysymToKeycode (xdisplay, XK_Num_Lock);
  global_input_data.alt_gr_keycode       = XKeysymToKeycode (xdisplay, global_input_data.alt_gr_keysym);
}
#endif /* HAVE_XTEST */

gboolean
vino_input_init (GdkDisplay *display)
{
#ifdef HAVE_XTEST
  Display *xdisplay;
  int      ignore, *i = &ignore;
  int      d;

  g_assert (global_input_data.initialized != TRUE);

  xdisplay = GDK_DISPLAY_XDISPLAY (display);

  if (XTestQueryExtension (xdisplay, i, i, i, i))
    {
      XTestGrabControl (xdisplay, True);

      global_input_data.xtest_supported = TRUE;
    }

#ifdef HAVE_XKB
  if (XkbQueryExtension (xdisplay, NULL, &global_input_data.xkb_event_type,
			 NULL, NULL, NULL))
    {
      XkbStateRec state;
  global_input_data.n_pointer_buttons = XGetPointerMapping(xdisplay, NULL, 0);

      dprintf (INPUT, "Using XKB\n");

      XkbGetState (xdisplay, XkbUseCoreKbd, &state);
      global_input_data.current_group = state.group;

      XkbSelectEventDetails (xdisplay, XkbUseCoreKbd, XkbStateNotify,
			     XkbGroupStateMask | XkbModifierLockMask,
			     XkbGroupStateMask | XkbModifierLockMask);
      XkbSelectEventDetails (xdisplay, XkbUseCoreKbd, XkbMapNotify,
			     XkbAllClientInfoMask, XkbAllClientInfoMask);
      XkbSelectEventDetails (xdisplay, XkbUseCoreKbd, XkbControlsNotify,
			     XkbGroupsWrapMask, XkbGroupsWrapMask);

      gdk_window_add_filter (NULL, xkb_event_filter, &global_input_data);

      global_input_data.xkb_supported = TRUE;
    }
  else
#endif
    global_input_data.xkb_supported = FALSE;

  global_input_data.decompositions = g_hash_table_new (NULL, NULL);
  for (d = 0; d < num_decompositions; d++)
    {
      g_hash_table_insert (global_input_data.decompositions,
			   GUINT_TO_POINTER (decompositions[d].composed),
			   decompositions[d].decomposed);
    }

  vino_input_initialize_keycodes (xdisplay);

  global_input_data.initialized = TRUE;

  return global_input_data.xtest_supported;
#else
  return global_input_data.xtest_supported = FALSE;
#endif /* HAVE_XTEST */
}

void
vino_input_handle_pointer_event (GdkScreen *screen,
				 guint8     button_mask,
				 guint16    x,
				 guint16    y)
{
#ifdef HAVE_XTEST
  Display *xdisplay;
  guint8   prev_mask = global_input_data.button_mask;
  int      i;

  xdisplay = GDK_DISPLAY_XDISPLAY (gdk_screen_get_display (screen));

  XTestFakeMotionEvent (xdisplay,
			gdk_screen_get_number (screen),
			x, y,
			CurrentTime);
  
  dprintf (INPUT, "Injected motion event: %d, %d\n", x, y);

  for (i = 0; i < global_input_data.n_pointer_buttons; i++)
    {
      gboolean button_down      = (button_mask & (1 << i)) != FALSE;
      gboolean prev_button_down = (prev_mask   & (1 << i)) != FALSE;

      if (button_down != prev_button_down)
	{
	  XTestFakeButtonEvent (xdisplay, i + 1, button_down, CurrentTime);
	  
	  dprintf (INPUT, "Injected button %d %s\n",
		   i + 1, button_down ? "press" : "release");
	}
    }

  global_input_data.button_mask = button_mask;
#endif /* HAVE_XTEST */
}

#ifdef HAVE_XTEST
static inline void
vino_input_update_modifier_state (VinoInputData    *input_data,
				  VinoModifierState state,
				  guint32           check_keysym,
				  guint32           keysym,
				  gboolean          key_press)
{
  if (keysym == check_keysym)
    {
      if (key_press)
	input_data->modifier_state |= state;
      else
	input_data->modifier_state &= ~state;
    }
}

/* If @key_press is %TRUE, adjusts the modifier state on the keyboard
 * such that it's possible to type @binding. If @key_press is %FALSE,
 * undoes that.
 */
static void
vino_input_fake_modifiers (Display           *xdisplay,
			   VinoInputData     *input_data,
			   VinoKeybinding    *binding,
			   gboolean           key_press)
{
  VinoModifierState modifier_state = input_data->modifier_state;
  int cur_level;

  if (binding->level == -1)
    {
      /* The keysym is unaffected by modifier keys, so we should leave
       * the modifiers in their current state.
       */
      return;
    }

  if (binding->keypad && !input_data->xkb_supported)
    {
      Window root, child;
      int rx, ry, wx, wy;
      unsigned int mask;

      XQueryPointer (xdisplay, RootWindow (xdisplay, DefaultScreen (xdisplay)),
		     &root, &child, &rx, &ry, &wx, &wy, &mask);
      if (mask & input_data->num_lock_mod)
	modifier_state |= VINO_NUM_LOCK;
      else
	modifier_state &= ~VINO_NUM_LOCK;
    }

  cur_level = VINO_LEVEL (modifier_state);

  if (VINO_LEVEL_IS_SHIFT (cur_level) && !VINO_LEVEL_IS_SHIFT (binding->level))
    {
      dprintf (INPUT, "Faking Shift %s\n", key_press ? "release" : "press");

      if (modifier_state & VINO_LEFT_SHIFT)
	XTestFakeKeyEvent (xdisplay,
			   input_data->left_shift_keycode,
			   !key_press,
			   CurrentTime);
      
      if (modifier_state & VINO_RIGHT_SHIFT)
	XTestFakeKeyEvent (xdisplay,
			   input_data->right_shift_keycode,
			   !key_press,
			   CurrentTime);
    }

  if (!VINO_LEVEL_IS_SHIFT (cur_level) && VINO_LEVEL_IS_SHIFT (binding->level))
    {
      dprintf (INPUT, "Faking Shift %s\n", key_press ? "press" : "release");

      XTestFakeKeyEvent (xdisplay,
			 input_data->left_shift_keycode,
			 key_press,
			 CurrentTime);
    }

  if (VINO_LEVEL_IS_ALTGR (cur_level) != VINO_LEVEL_IS_ALTGR (binding->level))
    {
      gboolean want_pressed = VINO_LEVEL_IS_ALTGR (binding->level) ? key_press : !key_press;

      dprintf (INPUT, "Faking Alt-Gr %s\n", want_pressed ? "press" : "release");

      XTestFakeKeyEvent (xdisplay,
			 input_data->alt_gr_keycode,
			 want_pressed,
			 CurrentTime);
    }

  if (VINO_LEVEL_IS_NUM_LOCK (cur_level) != VINO_LEVEL_IS_NUM_LOCK (binding->level))
    {
      dprintf (INPUT, "Faking NumLock press/release\n");

      XTestFakeKeyEvent (xdisplay,
			 input_data->num_lock_keycode,
			 True,
			 CurrentTime);
      XTestFakeKeyEvent (xdisplay,
			 input_data->num_lock_keycode,
			 False,
			 CurrentTime);
    }
}

static gboolean
vino_input_fake_keypress (Display *xdisplay, guint32 keysym)
{
  VinoKeybinding *binding = g_hash_table_lookup (global_input_data.keybindings,
						 GUINT_TO_POINTER (keysym));

  if (binding)
    {
      int group;

#ifdef HAVE_XKB
      if (global_input_data.xkb_supported)
	{
	  if (binding[global_input_data.current_group].keycode)
	    group = global_input_data.current_group;
	  else if (global_input_data.xkb_num_groups > 1)
	    {
	      for (group = 0; group < VINO_NUM_GROUPS; group ++)
		{
		  if (binding[group].keycode)
		    {
		      dprintf (INPUT, "Latching group to %d\n", group);
		      XkbLatchGroup (xdisplay, XkbUseCoreKbd,
				     (group - global_input_data.current_group) %
				     global_input_data.xkb_num_groups);
		      break;
		    }
		}
	      if (group == VINO_NUM_GROUPS)
		return FALSE;
	    }
	  else
	    return FALSE;
	}
      else
#endif
	group = 0;

      vino_input_fake_modifiers (xdisplay, &global_input_data,
				 &binding[group], TRUE);

      dprintf (INPUT, "Injecting keysym 0x%.2x (%s) press/release (via keycode %d, level %d)\n",
	       keysym, XKeysymToString (keysym), binding[group].keycode, binding[group].level);

      XTestFakeKeyEvent (xdisplay, binding[group].keycode, TRUE,  CurrentTime);
      XTestFakeKeyEvent (xdisplay, binding[group].keycode, FALSE, CurrentTime);

      vino_input_fake_modifiers (xdisplay, &global_input_data,
				 &binding[group], FALSE);

      return TRUE;
    }
  else
    {
      guint32 *decomposition =
	g_hash_table_lookup (global_input_data.decompositions,
			     GUINT_TO_POINTER (keysym));
      int i;

      if (decomposition)
	{
	  for (i = 0; i < 3 && decomposition[i]; i++)
	    {
	      if (!vino_input_fake_keypress (xdisplay, decomposition[i]))
		return FALSE;
	    }

	  return TRUE;
	}
    }

  return FALSE;
}
#endif /* HAVE_XTEST */

void
vino_input_handle_key_event (GdkScreen *screen,
			     guint32    keysym,
			     gboolean   key_press)
{
#ifdef HAVE_XTEST
  Display *xdisplay;

  dprintf (INPUT, "Got key %s for %s\n", key_press ? "press" : "release",
	   XKeysymToString (keysym));

  /* 
   * We inject a key press/release pair for all key presses 
   * and ignore key releases. The exception is modifiers.
   */

  if (!key_press && !VINO_IS_MODIFIER_KEYSYM (keysym))
    return;

  xdisplay = GDK_DISPLAY_XDISPLAY (gdk_screen_get_display (screen));
  
  vino_input_update_modifier_state (&global_input_data,
				    VINO_LEFT_SHIFT, XK_Shift_L,
				    keysym, key_press);

  vino_input_update_modifier_state (&global_input_data,
				    VINO_RIGHT_SHIFT, XK_Shift_R,
				    keysym, key_press);

  vino_input_update_modifier_state (&global_input_data,
				    VINO_LEFT_CONTROL, XK_Control_L,
				    keysym, key_press);

  vino_input_update_modifier_state (&global_input_data,
				    VINO_ALT_GR, global_input_data.alt_gr_keysym,
				    keysym, key_press);

  if (!VINO_IS_MODIFIER_KEYSYM (keysym))
    {
      vino_input_fake_keypress (xdisplay, keysym);
    }
  else if (key_press && keysym == XK_Alt_R &&
	   (global_input_data.modifier_state & VINO_LEFT_CONTROL))
    {
      /* Windows translates Alt-Gr to XK_Ctrl_L + XK_Alt_R */

      dprintf (INPUT, "Translating Ctrl+Alt press into Alt-Gr\n");

      XTestFakeKeyEvent (xdisplay, global_input_data.left_control_keycode,
			 FALSE,  CurrentTime);
      XTestFakeKeyEvent (xdisplay, global_input_data.alt_gr_keycode,
			 TRUE, CurrentTime);

      global_input_data.modifier_state |= (VINO_RIGHT_ALT | VINO_ALT_GR);
    }
  else if (!key_press && keysym == XK_Control_L &&
	   (global_input_data.modifier_state & VINO_RIGHT_ALT))
    {
      dprintf (INPUT, "Translating Ctrl+Alt release into Alt-Gr\n");

      XTestFakeKeyEvent (xdisplay, global_input_data.alt_gr_keycode,
			 FALSE, CurrentTime);

      global_input_data.modifier_state &= ~(VINO_RIGHT_ALT | VINO_ALT_GR);
    }
  else if (keysym != XK_Caps_Lock && keysym != XK_Num_Lock)
    {
      KeyCode keycode;

      if ((keycode = XKeysymToKeycode (xdisplay, keysym)) != NoSymbol)
	{
	  dprintf (INPUT, "Injecting keysym 0x%.2x (%s) %s (keycode %d)\n",
		   keysym, XKeysymToString (keysym),
		   key_press ? "press" : "release", keycode);

	  XTestFakeKeyEvent (xdisplay, keycode, key_press, CurrentTime);
	}
    }

  XFlush (xdisplay);

#endif /* HAVE_XTEST */
}

void
vino_input_handle_clipboard_event (GdkScreen *screen,
				   char      *text,
				   int        len)
{
}


syntax highlighted by Code2HTML, v. 0.9.1