[X2Go-Commits] [nx-libs] 02/14: Make nxagent-specific keyboard bindings configurable (320_nxagent_configurable-keystrokes.full.patch).

git-admin at x2go.org git-admin at x2go.org
Fri Feb 13 13:59:10 CET 2015


This is an automated email from the git hooks/post-receive script.

x2go pushed a commit to branch 3.6.x
in repository nx-libs.

commit e91277d02bf1288909daed3b0de8f876f6403acf
Author: Alexander Wuerstlein <arw at arw.name>
Date:   Fri Feb 13 09:52:21 2015 +0100

    Make nxagent-specific keyboard bindings configurable (320_nxagent_configurable-keystrokes.full.patch).
    
      Replaces the hardcoded nxagent keybindings by a configurable
      table of keybindings. The default configuration is the same as the
      original one, to maintain compatibility. A user/administrator can either
      specify a command line parameter, environment variable or place a file
      in ~/.nx/config/keystrokes.cfg or /etc/nxagent/keystrokes.cfg to reconfigure
      these keybindings.
    
      The configuration file format is XML, a dependency on libxml2 is added
      to allow parsing the configuration.
---
 .../320_nxagent_configurable-keystrokes.full.patch | 1031 --------------------
 debian/patches/series                              |    1 -
 nx-X11/programs/Xserver/Imakefile                  |    9 +-
 nx-X11/programs/Xserver/hw/nxagent/Args.c          |   16 +
 nx-X11/programs/Xserver/hw/nxagent/Args.h          |    2 +
 nx-X11/programs/Xserver/hw/nxagent/Imakefile       |    9 +-
 nx-X11/programs/Xserver/hw/nxagent/Keystroke.c     |  700 ++++++++-----
 nx-X11/programs/Xserver/hw/nxagent/Keystroke.h     |   47 +
 8 files changed, 546 insertions(+), 1269 deletions(-)

diff --git a/debian/patches/320_nxagent_configurable-keystrokes.full.patch b/debian/patches/320_nxagent_configurable-keystrokes.full.patch
deleted file mode 100644
index c799c8b..0000000
--- a/debian/patches/320_nxagent_configurable-keystrokes.full.patch
+++ /dev/null
@@ -1,1031 +0,0 @@
-Author: Alexander Wuerstlein <arw at arw.name>
-Description: Make nxagent-specific keyboard bindings configurable
- Replaces the hardcoded nxagent keybindings by a configurable
- table of keybindings. The default configuration is the same as the
- original one, to maintain compatibility. A user/administrator can either
- specify a command line parameter, environment variable or place a file
- in ~/.nx/config/keystrokes.cfg or /etc/nxagent/keystrokes.cfg to reconfigure
- these keybindings.
- .
- The configuration file format is XML, a dependency on libxml2 is added
- to allow parsing the configuration.
---- a/nx-X11/programs/Xserver/Imakefile
-+++ b/nx-X11/programs/Xserver/Imakefile
-@@ -1013,15 +1013,18 @@
- #if defined(SunArchitecture)
- NXAGENTNXLIBS = -L ../../../nxcomp -L ../../../nxcompext -L ../../../nxcompshad \
-                 -lXcomp -lXcompext -lXcompshad -lrt -L/usr/sfw/lib -lXrender -lXfixes \
--                -L../../../nx-X11/exports/lib -lXtst -lXdamage -lXrandr -lXcomposite -lXdmcp
-+                -L../../../nx-X11/exports/lib -lXtst -lXdamage -lXrandr -lXcomposite -lXdmcp \
-+`pkg-config --libs libxml-2.0`
- #elif defined(cygwinArchitecture)
- NXAGENTNXLIBS = -L ../../../nxcomp -L ../../../nxcompext \
-                 -lXcomp -lXcompext -lXrender -lX11 -lXext -lXcomposite -lXfixes \
--                -L ../../../nxcompshad -lXcompshad -L../../../nx-X11/exports/lib -lXtst -lXdmcp
-+                -L ../../../nxcompshad -lXcompshad -L../../../nx-X11/exports/lib -lXtst -lXdmcp \
-+`pkg-config --libs libxml-2.0`
- #else
- NXAGENTNXLIBS = -L ../../../nxcomp -L ../../../nxcompext -L ../../../nxcompshad \
-                 -lXcomp -lXcompext -lXcompshad -lXrender -lX11 -lXext -lXfixes \
--                -L../../../nx-X11/exports/lib -lXtst -lXdamage -lXrandr -lXcomposite -lXinerama -lXdmcp
-+                -L../../../nx-X11/exports/lib -lXtst -lXdamage -lXrandr -lXcomposite -lXinerama -lXdmcp \
-+`pkg-config --libs libxml-2.0`
- #endif
- 
- #endif
---- a/nx-X11/programs/Xserver/hw/nxagent/Imakefile
-+++ b/nx-X11/programs/Xserver/hw/nxagent/Imakefile
-@@ -142,7 +142,8 @@
-            -I../../miext/damage -I../../miext/cw \
- 	   -I../../GL/glx -I../../GL/include -I../../../../lib/GL/include -I../../Xext \
-            -I$(EXTINCSRC) -I$(XINCLUDESRC) \
--	   $(VFBINCLUDES) $(NXFONTINCLUDES) $(LIBXRANDRINCLUDES)
-+	   $(VFBINCLUDES) $(NXFONTINCLUDES) $(LIBXRANDRINCLUDES) \
-+           `pkg-config --cflags-only-I libxml-2.0`
- #ifdef SunArchitecture
- INCLUDES = -I. -I../../../../../nxcomp -I../../../../../nxcompext -I../../../../../nxcompshad \
-            -I../../../../extras/Mesa/include \
-@@ -152,7 +153,8 @@
- 	   -I../../GL/glx -I../../GL/include -I../../../../lib/GL/include -I../../Xext \
-            -I../../miext/damage -I../../miext/cw \
-            -I$(EXTINCSRC) -I$(XINCLUDESRC) \
--	   $(VFBINCLUDES) $(NXFONTINCLUDES) $(LIBXRANDRINCLUDES)
-+	   $(VFBINCLUDES) $(NXFONTINCLUDES) $(LIBXRANDRINCLUDES) \
-+           `pkg-config --cflags-only-I libxml-2.0`
- #else
- #ifdef cygwinArchitecture
- INCLUDES = -I. -I$(XBUILDINCDIR) -I$(FONTINCSRC) \
-@@ -162,7 +164,8 @@
-            -I../../../../../nxcomp -I../../../../../nxcompext -I../../../../../nxcompshad \
-            -I../../../../extras/Mesa/include \
-            -I$(EXTINCSRC) -I$(XINCLUDESRC) \
--	   $(VFBINCLUDES) $(NXFONTINCLUDES) $(LIBXRANDRINCLUDES)
-+	   $(VFBINCLUDES) $(NXFONTINCLUDES) $(LIBXRANDRINCLUDES) \
-+           `pkg-config --cflags-only-I libxml-2.0`
- #endif
- #endif
- 
---- a/nx-X11/programs/Xserver/hw/nxagent/Keystroke.c
-+++ b/nx-X11/programs/Xserver/hw/nxagent/Keystroke.c
-@@ -28,8 +28,15 @@
- #include "Keystroke.h"
- #include "Drawable.h"
- 
-+#include <unistd.h>
-+
-+#include <libxml/parser.h>
-+#include <libxml/tree.h>
-+
- extern Bool nxagentWMIsRunning;
- extern Bool nxagentIpaq;
-+extern char *nxagentKeystrokeFile;
-+Bool nxagentKeystrokeFileParsed = False;
- 
- #ifdef NX_DEBUG_INPUT
- int nxagentDebugInputDevices = 0;
-@@ -47,297 +54,528 @@
- #undef  DEBUG
- #undef  DUMP
- 
--int nxagentCheckSpecialKeystroke(XKeyEvent *X, enum HandleEventResult *result)
--{
--  KeySym sym;
--  int index = 0;
- 
--  *result = doNothing;
-+/* this table is used to parse actions given on the command line or in the
-+ * config file, therefore indices have to match the enum in Keystroke.h */
-+char * nxagentSpecialKeystrokeNames[] = {
-+       "end_marker",
-+       "close_session",
-+       "switch_all_screens",
-+       "minimize",
-+       "left",
-+       "up",
-+       "right",
-+       "down",
-+       "resize",
-+       "defer",
-+       "ignore",
-+       "force_synchronization",
-+
-+       "debug_tree",
-+       "regions_on_screen",
-+       "test_input",
-+       "deactivate_input_devices_grab",
-+
-+       "fullscreen",
-+       "viewport_move_left",
-+       "viewport_move_up",
-+       "viewport_move_right",
-+       "viewport_move_down",
-+       NULL,
-+};
-+
-+struct nxagentSpecialKeystrokeMap default_map[] = {
-+  /* stroke, modifierMask, modifierAltMeta, keysym */
-+  {KEYSTROKE_DEBUG_TREE, ControlMask, 1, XK_q},
-+  {KEYSTROKE_DEBUG_TREE, ControlMask, 1, XK_Q},
-+  {KEYSTROKE_CLOSE_SESSION, ControlMask, 1, XK_t},
-+  {KEYSTROKE_CLOSE_SESSION, ControlMask, 1, XK_T},
-+  {KEYSTROKE_SWITCH_ALL_SCREENS, ControlMask, 1, XK_f},
-+  {KEYSTROKE_SWITCH_ALL_SCREENS, ControlMask, 1, XK_F},
-+  {KEYSTROKE_MINIMIZE, ControlMask, 1, XK_m},
-+  {KEYSTROKE_MINIMIZE, ControlMask, 1, XK_M},
-+  {KEYSTROKE_LEFT, ControlMask, 1, XK_Left},
-+  {KEYSTROKE_LEFT, ControlMask, 1, XK_KP_Left},
-+  {KEYSTROKE_UP, ControlMask, 1, XK_Up},
-+  {KEYSTROKE_UP, ControlMask, 1, XK_KP_Up},
-+  {KEYSTROKE_RIGHT, ControlMask, 1, XK_Right},
-+  {KEYSTROKE_RIGHT, ControlMask, 1, XK_KP_Right},
-+  {KEYSTROKE_DOWN, ControlMask, 1, XK_Down},
-+  {KEYSTROKE_DOWN, ControlMask, 1, XK_KP_Down},
-+  {KEYSTROKE_RESIZE, ControlMask, 1, XK_r},
-+  {KEYSTROKE_RESIZE, ControlMask, 1, XK_R},
-+  {KEYSTROKE_DEFER, ControlMask, 1, XK_e},
-+  {KEYSTROKE_DEFER, ControlMask, 1, XK_E},
-+  {KEYSTROKE_IGNORE, ControlMask, 1, XK_BackSpace},
-+  {KEYSTROKE_IGNORE, 0, 0, XK_Terminate_Server},
-+  {KEYSTROKE_FORCE_SYNCHRONIZATION, ControlMask, 1, XK_j},
-+  {KEYSTROKE_FORCE_SYNCHRONIZATION, ControlMask, 1, XK_J},
-+  {KEYSTROKE_REGIONS_ON_SCREEN, ControlMask, 1, XK_a},
-+  {KEYSTROKE_REGIONS_ON_SCREEN, ControlMask, 1, XK_A},
-+  {KEYSTROKE_TEST_INPUT, ControlMask, 1, XK_x},
-+  {KEYSTROKE_TEST_INPUT, ControlMask, 1, XK_X},
-+  {KEYSTROKE_DEACTIVATE_INPUT_DEVICES_GRAB, ControlMask, 1, XK_y},
-+  {KEYSTROKE_DEACTIVATE_INPUT_DEVICES_GRAB, ControlMask, 1, XK_Y},
-+  {KEYSTROKE_FULLSCREEN, ControlMask | ShiftMask, 1, XK_f},
-+  {KEYSTROKE_FULLSCREEN, ControlMask | ShiftMask, 1, XK_F},
-+  {KEYSTROKE_VIEWPORT_MOVE_LEFT, ControlMask | ShiftMask, 1, XK_Left},
-+  {KEYSTROKE_VIEWPORT_MOVE_LEFT, ControlMask | ShiftMask, 1, XK_KP_Left},
-+  {KEYSTROKE_VIEWPORT_MOVE_UP, ControlMask | ShiftMask, 1, XK_Up},
-+  {KEYSTROKE_VIEWPORT_MOVE_UP, ControlMask | ShiftMask, 1, XK_KP_Up},
-+  {KEYSTROKE_VIEWPORT_MOVE_RIGHT, ControlMask | ShiftMask, 1, XK_Right},
-+  {KEYSTROKE_VIEWPORT_MOVE_RIGHT, ControlMask | ShiftMask, 1, XK_KP_Right},
-+  {KEYSTROKE_VIEWPORT_MOVE_DOWN, ControlMask | ShiftMask, 1, XK_Down},
-+  {KEYSTROKE_VIEWPORT_MOVE_DOWN, ControlMask | ShiftMask, 1, XK_KP_Down},
-+  {KEYSTROKE_END_MARKER, 0, 0, 0},
-+};
-+struct nxagentSpecialKeystrokeMap *map = default_map;
- 
--  /*
--   * I don't know how much hard work is doing this operation.
--   * Do we need a cache ?
-+static int modifier_matches(unsigned int mask, int compare_alt_meta, unsigned int state)
-+{
-+  /* nxagentAltMetaMask needs special handling
-+   * it seems to me its an and-ed mask of all possible meta and alt keys
-+   * somehow...
-+   *
-+   * otherwise this function would be just a simple bitop
-    */
-+  int ret = 1;
- 
--  sym = XKeycodeToKeysym(nxagentDisplay, X -> keycode, index);
-+  if (compare_alt_meta) {
-+    if (! (state & nxagentAltMetaMask)) {
-+      ret = 0;
-+    }
- 
--  if (sym == XK_VoidSymbol || sym == NoSymbol)
--  {
--    return 0;
-+    mask &= ~nxagentAltMetaMask;
-   }
- 
--  #ifdef TEST
--  fprintf(stderr, "nxagentCheckSpecialKeystroke: got code %x - state %x - sym %lx\n",
--              X -> keycode, X -> state, sym);
--  #endif
--
--  /*
--   * Check special keys.
--   */
--
--  /*
--   * FIXME: We should use the keysym instead that the keycode
--   *        here.
--   */
-+  /* all modifiers except meta/alt have to match exactly, extra bits are evil */
-+  if ((mask & state) != mask) {
-+    ret = 0;
-+  }
- 
--  if (X -> keycode == 130 && nxagentIpaq)
--  {
--    *result = doStartKbd;
-+  return ret;
-+}
- 
--    return 1;
--  }
-+static int read_binding_from_xmlnode(xmlNode *node, struct nxagentSpecialKeystrokeMap *ret)
-+{
-+  int successful = 0;
-+  struct nxagentSpecialKeystrokeMap new = {0, 0, 0, 0};
-+  xmlAttr *attr;
- 
--  if ((X -> state & nxagentAltMetaMask) &&
--          ((X -> state & (ControlMask | ShiftMask)) == ControlMask))
-+  for (attr = node->properties; attr; attr = attr->next)
-   {
--    switch (sym)
-+    /* ignore attributes without data (which should never happen anyways) */
-+    if (attr->children->content == NULL)
-     {
--      #ifdef DEBUG_TREE
--
--      case XK_q:
--      case XK_Q:
--      {
--        *result = doDebugTree;
--
--        break;
--      }
--
--      #endif /* DEBUG_TREE */
--
--      case XK_t:
--      case XK_T:
--      {
--        *result = doCloseSession;
--
--        break;
--      }
--      case XK_f:
--      case XK_F:
-+      char *aname = (attr->name)?(attr->name):"unknown";
-+      fprintf(stderr, "attribute %s with NULL value", aname);
-+      continue;
-+    }
-+    if (strcmp((char *)attr->name, "action") == 0)
-+    {
-+      int i;
-+      for (i = 0; nxagentSpecialKeystrokeNames[i] != NULL; i++)
-       {
--        if (nxagentOption(Rootless) == False)
-+        if (strcmp(nxagentSpecialKeystrokeNames[i],(char *)attr->children->content) == 0)
-         {
--          *result = doSwitchAllScreens;
-+          /* this relies on the values of enum nxagentSpecialKeystroke and the
-+           * indices of nxagentSpecialKeystrokeNames being in sync */
-+          new.stroke = i;
-+          break;
-         }
--
--        break;
-       }
--      case XK_m:
--      case XK_M:
-+      continue;
-+    }
-+    else if (strcmp((char *)attr->name, "key") == 0)
-+    {
-+      new.keysym = XStringToKeysym((char *)attr->children->content);
-+      /* NoSymbol is usually 0, but could there be weird implementations? */
-+      if (new.keysym == NoSymbol)
-       {
--        if (nxagentOption(Rootless) == False)
--        {
--          *result = doMinimize;
--        }
--
--        break;
-+        new.keysym = 0;
-       }
--      case XK_Left:
--      case XK_KP_Left:
--      {
--        if (nxagentOption(Rootless) == False &&
--                nxagentOption(DesktopResize) == False)
--        {
--          *result = doViewportLeft;
--        }
-+      continue;
-+    }
- 
--        break;
--      }
--      case XK_Up:
--      case XK_KP_Up:
--      {
--        if (nxagentOption(Rootless) == False &&
--                nxagentOption(DesktopResize) == False)
--        {
--          *result = doViewportUp;
--        }
-+    /* ignore attributes with value="0" or "false", everything else is interpreted as true */
-+    if (strcmp((char *)attr->children->content, "0") == 0 || strcmp((char *)attr->children->content, "false") == 0)
-+      continue;
- 
--        break;
--      }
--      case XK_Right:
--      case XK_KP_Right:
--      {
--        if (nxagentOption(Rootless) == False &&
--                nxagentOption(DesktopResize) == False)
--        {
--          *result = doViewportRight;
--        }
-+    if (strcmp((char *)attr->name, "Mod1") == 0)
-+    {
-+      new.modifierMask |= Mod1Mask;
-+    }
-+    else if (strcmp((char *)attr->name, "Mod2") == 0)
-+    {
-+      new.modifierMask |= Mod2Mask;
-+    }
-+    else if (strcmp((char *)attr->name, "Mod3") == 0)
-+    {
-+      new.modifierMask |= Mod3Mask;
-+    }
-+    else if (strcmp((char *)attr->name, "Mod4") == 0)
-+    {
-+      new.modifierMask |= Mod4Mask;
-+    }
-+    else if (strcmp((char *)attr->name, "Control") == 0)
-+    {
-+      new.modifierMask |= ControlMask;
-+    }
-+    else if (strcmp((char *)attr->name, "Shift") == 0)
-+    {
-+      new.modifierMask |= ShiftMask;
-+    }
-+    else if (strcmp((char *)attr->name, "Lock") == 0)
-+    {
-+      new.modifierMask |= LockMask;
-+    }
-+    else if (strcmp((char *)attr->name, "AltMeta") == 0)
-+    {
-+      new.modifierAltMeta = 1;
-+    }
-+  }
- 
--        break;
--      }
--      case XK_Down:
--      case XK_KP_Down:
--      {
--        if (nxagentOption(Rootless) == 0 &&
--                nxagentOption(DesktopResize) == 0)
--        {
--          *result = doViewportDown;
--        }
-+  if (new.stroke != 0 && new.keysym != 0)
-+  {
-+    /* keysym and stroke are required, everything else is optional */
-+    successful = 1;
-+    memcpy(ret, &new, sizeof(struct nxagentSpecialKeystrokeMap));
-+  }
-+  return successful;
-+}
- 
--        break;
--      }
--      case XK_R:
--      case XK_r:
--      {
--        if (nxagentOption(Rootless) == 0)
--        {
--          *result = doSwitchResizeMode;
--        }
-+/*
-+ * searches a keystroke xml file
-+ *
-+ * search order:
-+ *  - '-keystrokefile' commandline parameter
-+ *  - $NXAGENT_KEYSTROKEFILE environment variable
-+ *  - $HOME/.nx/config/keystrokes.cfg
-+ *  - /etc/nxagent/keystrokes.cfg
-+ *  - hardcoded traditional NX default settings
-+ */
-+static void parse_keystroke_file(void)
-+{
-+  char *filename = NULL;
- 
--        break;
--      }
--      case XK_E:
--      case XK_e:
--      {
--        *result = doSwitchDeferMode;
-+  char *homefile = "/.nx/config/keystrokes.cfg";
-+  char *etcfile = "/etc/nxagent/keystrokes.cfg";
- 
--        break;
-+  if (nxagentKeystrokeFile != NULL && access(nxagentKeystrokeFile, R_OK) == 0)
-+  {
-+    filename = strdup(nxagentKeystrokeFile);
-+    if (filename == NULL)
-+    {
-+      fprintf(stderr, "malloc failed");
-+      exit(EXIT_FAILURE);
-+    }
-+  }
-+  else if ((filename = getenv("NXAGENT_KEYSTROKEFILE")) != NULL && access(filename, R_OK) == 0)
-+  {
-+    filename = strdup(filename);
-+    if (filename == NULL)
-+    {
-+      fprintf(stderr, "malloc failed");
-+      exit(EXIT_FAILURE);
-+    }
-+  }
-+  else
-+  {
-+    char *homedir = getenv("HOME");
-+    filename = NULL;
-+    if (homedir != NULL)
-+    {
-+      homedir = strdup(homedir);
-+      if (homedir == NULL)
-+      {
-+        fprintf(stderr, "malloc failed");
-+exit(EXIT_FAILURE);
-       }
--      case XK_BackSpace:
--      case XK_Terminate_Server:
-+      filename = calloc(1, strlen(homefile) + strlen(homedir) + 1);
-+      if (filename == NULL)
-       {
--        /*
--         * Discard Ctrl-Alt-BackSpace key.
--         */
--
--        return 1;
--
--        break;
-+        fprintf(stderr, "malloc failed");
-+        exit(EXIT_FAILURE);
-       }
--
--      case XK_J:
--      case XK_j:
-+      strcpy(filename, homedir);
-+      strcpy(filename + strlen(homedir), homefile);
-+      if (homedir)
-       {
--        nxagentForceSynchronization = 1;
--
--        return 1;
-+        free(homedir);
-       }
-+    }
- 
--      #ifdef DUMP
--
--      case XK_A:
--      case XK_a:
-+    if (access(filename, R_OK) == 0)
-+    {
-+      /* empty */
-+    }
-+    else if (access(etcfile, R_OK) == 0)
-+    {
-+      if (filename)
-+        free(filename);
-+      filename = strdup(etcfile);
-+      if (filename == NULL)
-       {
--        /*
--         * Used to test the lazy encoding.
--         */
--
--        nxagentRegionsOnScreen();
--
--        return 1;
-+        fprintf(stderr, "malloc failed");
-+        exit(EXIT_FAILURE);
-       }
-+    }
-+    else
-+    {
-+      if (filename)
-+free(filename);
-+      filename = NULL;
-+    }
-+  }
- 
--      #endif
--
--      #ifdef NX_DEBUG_INPUT
-+  /* now we know which file to read, if any */
-+  if (filename)
-+  {
-+    xmlDoc *doc = NULL;
-+    xmlNode *root = NULL;
-+    LIBXML_TEST_VERSION
-+    doc = xmlReadFile(filename, NULL, 0);
-+    if (doc != NULL)
-+    {
-+      xmlNode *cur = NULL;
-+      root = xmlDocGetRootElement(doc);
- 
--      case XK_X:
--      case XK_x:
-+      for (cur = root; cur; cur = cur->next)
-       {
--        /*
--         * Used to test the input devices state.
--         */
-+        if (cur->type == XML_ELEMENT_NODE && strcmp((char *)cur->name, "keystrokes") == 0)
-+{
-+          xmlNode *bindings = NULL;
-+          int num = 0;
-+          int idx = 0;
- 
--        if (X -> type == KeyPress)
--        {
--          if (nxagentDebugInputDevices == 0)
-+          for (bindings = cur->children; bindings; bindings = bindings->next)
-           {
--            fprintf(stderr, "Info: Turning input devices debug ON.\n");
--    
--            nxagentDebugInputDevices = 1;
-+            if (bindings->type == XML_ELEMENT_NODE && strcmp((char *)bindings->name, "keystroke") == 0)
-+            {
-+              num++;
-+            }
-           }
--          else
-+          map = calloc((num + 1), sizeof(struct nxagentSpecialKeystrokeMap));
-+          if (map == NULL)
-           {
--            fprintf(stderr, "Info: Turning input devices debug OFF.\n");
--    
--            nxagentDebugInputDevices = 0;
--    
--            nxagentLastInputDevicesDumpTime = 0;
-+            fprintf(stderr, "malloc failed");
-+            exit(EXIT_FAILURE);
-           }
--        }
--
--        return 1;
--      }
- 
--      case XK_Y:
--      case XK_y:
--      {
--        /*
--         * Used to deactivate input devices grab.
--         */
-+          for (bindings = cur->children; bindings; bindings = bindings->next)
-+          {
-+            if (bindings->type == XML_ELEMENT_NODE && strcmp((char *)bindings->name, "keystroke") == 0)
-+            {
-+              int res = 0;
-+              res = read_binding_from_xmlnode(bindings, &(map[idx]));
-+              if (res)
-+                idx++;
-+            }
-+          }
- 
--        if (X -> type == KeyPress)
--        {
--          nxagentDeactivateInputDevicesGrabs();
-+          map[idx].stroke = KEYSTROKE_END_MARKER;
-         }
--
--        return 1;
-       }
- 
-+      xmlFreeDoc(doc);
-+      xmlCleanupParser();
-+    }
-+    else
-+    {
-+      #ifdef DEBUG
-+      fprintf("XML parsing for %s failed\n", filename);
-       #endif
-     }
-+    free(filename);
-   }
--  else if ((X -> state & nxagentAltMetaMask) &&
--               ((X -> state & (ControlMask | ShiftMask)) == (ControlMask |
--                   ShiftMask)))
-+}
-+
-+static enum nxagentSpecialKeystroke find_keystroke(XKeyEvent *X)
-+{
-+  KeySym keysym = XKeycodeToKeysym(nxagentDisplay, X->keycode, 0);
-+  struct nxagentSpecialKeystrokeMap *cur = map;
-+
-+  if (! nxagentKeystrokeFileParsed)
-   {
--    switch (sym)
--    {
--      case XK_f:
--      case XK_F:
--      {
--        if (nxagentOption(Rootless) == 0)
--        {
--          *result = doSwitchFullscreen;
--        }
-+    parse_keystroke_file();
-+    nxagentKeystrokeFileParsed = True;
-+  }
- 
--        break;
--      }
--      case XK_Left:
--      case XK_KP_Left:
--      {
--        if (nxagentOption(Rootless) == 0 &&
--                nxagentOption(DesktopResize) == 0)
--        {
--          *result = doViewportMoveLeft;
--        }
-+  enum nxagentSpecialKeystroke ret = KEYSTROKE_NOTHING;
- 
--        break;
--      }
--      case XK_Up:
--      case XK_KP_Up:
--      {
--        if (nxagentOption(Rootless) == 0 &&
--                nxagentOption(DesktopResize) == 0)
--        {
--          *result = doViewportMoveUp;
--        }
-+  while (cur->stroke != KEYSTROKE_END_MARKER) {
-+    if (cur->keysym == keysym && modifier_matches(cur->modifierMask, cur->modifierAltMeta, X->state)) {
-+      return cur->stroke;
-+    }
-+    cur++;
-+  }
- 
--        break;
--      }
--      case XK_Right:
--      case XK_KP_Right:
--      {
--        if (nxagentOption(Rootless) == 0 &&
--                nxagentOption(DesktopResize) == 0)
--        {
--          *result = doViewportMoveRight;
--        }
-+  return ret;
-+}
- 
--        break;
--      }
--      case XK_Down:
--      case XK_KP_Down:
--      {
--        if (nxagentOption(Rootless) == 0 &&
--                nxagentOption(DesktopResize) == 0)
--        {
--          *result = doViewportMoveDown;
--        }
-+int nxagentCheckSpecialKeystroke(XKeyEvent *X, enum HandleEventResult *result)
-+{
-+  KeySym sym;
-+  int index = 0;
-+  enum nxagentSpecialKeystroke stroke = find_keystroke(X);
- 
--        break;
--      }
--    }
-+  *result = doNothing;
-+
-+  /*
-+   * I don't know how much hard work is doing this operation.
-+   * Do we need a cache ?
-+   */
-+
-+  sym = XKeycodeToKeysym(nxagentDisplay, X -> keycode, index);
-+
-+  if (sym == XK_VoidSymbol || sym == NoSymbol)
-+  {
-+    return 0;
-   }
- 
-+  #ifdef TEST
-+  fprintf(stderr, "nxagentCheckSpecialKeystroke: got code %x - state %x - sym %lx\n",
-+              X -> keycode, X -> state, sym);
-+  #endif
-+
-+  /*
-+   * Check special keys.
-+   */
-+
-+  /*
-+   * FIXME: We should use the keysym instead that the keycode
-+   *        here.
-+   */
-+
-+  if (X -> keycode == 130 && nxagentIpaq)
-+  {
-+    *result = doStartKbd;
-+
-+    return 1;
-+  }
-+
-+  switch (stroke) {
-+    case KEYSTROKE_DEBUG_TREE:
-+      #ifdef DEBUG_TREE
-+      *result = doDebugTree;
-+      #endif
-+      break;
-+    case KEYSTROKE_CLOSE_SESSION:
-+      *result = doCloseSession;
-+      break;
-+    case KEYSTROKE_SWITCH_ALL_SCREENS:
-+      if (nxagentOption(Rootless) == False) {
-+        *result = doSwitchAllScreens;
-+      }
-+      break;
-+    case KEYSTROKE_MINIMIZE:
-+      if (nxagentOption(Rootless) == False) {
-+        *result = doMinimize;
-+      }
-+      break;
-+    case KEYSTROKE_LEFT:
-+      if (nxagentOption(Rootless) == False &&
-+          nxagentOption(DesktopResize) == False) {
-+        *result = doViewportLeft;
-+      }
-+      break;
-+    case KEYSTROKE_UP:
-+      if (nxagentOption(Rootless) == False &&
-+          nxagentOption(DesktopResize) == False) {
-+        *result = doViewportUp;
-+      }
-+      break;
-+    case KEYSTROKE_RIGHT:
-+      if (nxagentOption(Rootless) == False &&
-+          nxagentOption(DesktopResize) == False) {
-+        *result = doViewportRight;
-+      }
-+      break;
-+    case KEYSTROKE_DOWN:
-+      if (nxagentOption(Rootless) == False &&
-+          nxagentOption(DesktopResize) == False) {
-+        *result = doViewportDown;
-+      }
-+      break;
-+    case KEYSTROKE_RESIZE:
-+      if (nxagentOption(Rootless) == False) {
-+        *result = doSwitchResizeMode;
-+      }
-+      break;
-+    case KEYSTROKE_DEFER:
-+      *result = doSwitchDeferMode;
-+      break;
-+    case KEYSTROKE_IGNORE:
-+      /* this is used e.g. to ignore C-A-Backspace aka XK_Terminate_Server */
-+      return 1;
-+      break;
-+    case KEYSTROKE_FORCE_SYNCHRONIZATION:
-+      nxagentForceSynchronization = 1;
-+      break;
-+    case KEYSTROKE_REGIONS_ON_SCREEN:
-+      #ifdef DUMP
-+      nxagentRegionsOnScreen();
-+      #endif
-+      break;
-+    case KEYSTROKE_TEST_INPUT:
-+      /*
-+       * Used to test the input devices state.
-+       */
-+      #ifdef NX_DEBUG_INPUT
-+      if (X -> type == KeyPress) {
-+        if (nxagentDebugInputDevices == 0) {
-+          fprintf(stderr, "Info: Turning input devices debug ON.\n");
-+          nxagentDebugInputDevices = 1;
-+        } else {
-+          fprintf(stderr, "Info: Turning input devices debug OFF.\n");
-+          nxagentDebugInputDevices = 0;
-+          nxagentLastInputDevicesDumpTime = 0;
-+        }
-+      }
-+      return 1;
-+      #endif
-+      break;
-+    case KEYSTROKE_DEACTIVATE_INPUT_DEVICES_GRAB:
-+      #ifdef NX_DEBUG_INPUT
-+      if (X->type == KeyPress) {
-+        nxagentDeactivateInputDevicesGrab();
-+      }
-+      return 1;
-+      #endif
-+      break;
-+    case KEYSTROKE_FULLSCREEN:
-+      if (nxagentOption(Rootless) == 0) {
-+        *result = doSwitchFullscreen;
-+      }
-+      break;
-+    case KEYSTROKE_VIEWPORT_MOVE_LEFT:
-+      if (nxagentOption(Rootless) == 0 &&
-+          nxagentOption(DesktopResize) == 0) {
-+        *result = doViewportMoveLeft;
-+      }
-+      break;
-+    case KEYSTROKE_VIEWPORT_MOVE_UP:
-+      if (nxagentOption(Rootless) == 0 &&
-+          nxagentOption(DesktopResize) == 0) {
-+        *result = doViewportMoveUp;
-+      }
-+      break;
-+    case KEYSTROKE_VIEWPORT_MOVE_RIGHT:
-+      if (nxagentOption(Rootless) == 0 &&
-+          nxagentOption(DesktopResize) == 0) {
-+        *result = doViewportMoveRight;
-+      }
-+      break;
-+    case KEYSTROKE_VIEWPORT_MOVE_DOWN:
-+      if (nxagentOption(Rootless) == 0 &&
-+          nxagentOption(DesktopResize) == 0) {
-+        *result = doViewportMoveDown;
-+      }
-+      break;
-+    case KEYSTROKE_NOTHING: /* do nothing. difference to KEYSTROKE_IGNORE is the return value */
-+    case KEYSTROKE_END_MARKER: /* just to make gcc STFU */
-+    case KEYSTROKE_MAX:
-+      break;
-+  }
-   return (*result == doNothing) ? 0 : 1;
- }
---- a/nx-X11/programs/Xserver/hw/nxagent/Keystroke.h
-+++ b/nx-X11/programs/Xserver/hw/nxagent/Keystroke.h
-@@ -24,4 +24,51 @@
- 
- unsigned int nxagentAltMetaMask;
- 
-+/* keep this sorted, do not rely on any numerical value in this enum, and be aware
-+ * that KEYSTROKE_MAX may be used in a malloc */
-+
-+/* also be aware that if changing any numerical values, you also need to change values
-+ * Keystroke.c nxagentSpecialKeystrokeNames */
-+enum nxagentSpecialKeystroke {
-+       /* 0 is used as end marker */
-+       KEYSTROKE_END_MARKER = 0,
-+       KEYSTROKE_CLOSE_SESSION = 1,
-+       KEYSTROKE_SWITCH_ALL_SCREENS = 2,
-+       KEYSTROKE_MINIMIZE = 3,
-+       KEYSTROKE_LEFT = 4,
-+       KEYSTROKE_UP = 5,
-+       KEYSTROKE_RIGHT = 6,
-+       KEYSTROKE_DOWN = 7,
-+       KEYSTROKE_RESIZE = 8,
-+       KEYSTROKE_DEFER = 9,
-+       KEYSTROKE_IGNORE = 10,
-+       KEYSTROKE_FORCE_SYNCHRONIZATION = 11,
-+
-+       /* stuff used for debugging, probably not useful for most people */
-+       KEYSTROKE_DEBUG_TREE = 12,
-+       KEYSTROKE_REGIONS_ON_SCREEN = 13,
-+       KEYSTROKE_TEST_INPUT = 14,
-+       KEYSTROKE_DEACTIVATE_INPUT_DEVICES_GRAB = 15,
-+
-+       KEYSTROKE_FULLSCREEN = 16,
-+       KEYSTROKE_VIEWPORT_MOVE_LEFT = 17,
-+       KEYSTROKE_VIEWPORT_MOVE_UP = 18,
-+       KEYSTROKE_VIEWPORT_MOVE_RIGHT = 19,
-+       KEYSTROKE_VIEWPORT_MOVE_DOWN = 20,
-+
-+       KEYSTROKE_NOTHING = 21,
-+
-+       /* insert more here, increment KEYSTROKE_MAX accordingly.
-+        * then update string translation below */
-+
-+       KEYSTROKE_MAX=22,
-+};
-+
-+struct nxagentSpecialKeystrokeMap {
-+       enum nxagentSpecialKeystroke stroke;
-+       unsigned int modifierMask; /* everything except alt/meta */
-+       int modifierAltMeta; /* modifier combination should include alt/meta */
-+       KeySym keysym;
-+};
-+
- #endif /* __Keystroke_H__ */
---- /dev/null
-+++ b/README.keystrokes
-@@ -0,0 +1,83 @@
-+Configurable keybindings in nxagent
-+
-+Keybindings in the redistributed x2go version of nxagent can now be configured
-+by the user. This is done via a configuration file.
-+
-+File location
-+-------------
-+
-+nxagent searches for the configuration file in the following order:
-+- in the location given by the '-keystrokefile' command line parameter
-+- in the location given by the NXAGENT_KEYSTROKEFILE environment variable
-+- in ~/.nx/config/keystrokes.cfg
-+- in /etc/nxagent/keystrokes.cfg
-+
-+If none of those files is accessible, the default configuration is used which
-+is the same as the old, traditional nxagent keybindings.
-+
-+File format
-+-----------
-+
-+The configuration file is XML with the following format:
-+
-+<!DOCTYPE NXKeystroke>
-+<keystrokes>
-+<keystroke action="fullscreen" AltMeta="1" Control="1" key="b" />
-+<keystroke action="minimize" AltMeta="1" Control="1" key="space" />
-+<keystroke action="minimize" key="Escape" Shift="1" />
-+<keystroke action="close_session" key="F7" />
-+<keystroke action="fullscreen" key="F7" Mod1="1" />
-+<keystroke action="fullscreen" key="F6" Mod1="1" />
-+<keystroke action="force_synchronization" key="f" />
-+<keystroke action="fullscreen" key="space" Mod1="0" Mod2="0" Control="0" Shift="0" AltMeta="0" />
-+</keystrokes>
-+
-+Each 'action' defines an action to be executed when receiving that keystroke. A
-+list of possible actions is given below. Some of those actions are only
-+available with debug builds of nxagent.
-+
-+Keys are given as a combination of 'key' and (optionally) a number of
-+modifiers. The key attribute is evaluated into a X11 key via the usual
-+XStringToKeysym function. A list of possible keys can be found in
-+/usr/include/X11/keysymdef.h, the names are specified without the leading
-+'XK_'. Evaluation is case-sensitive, so, 'space' and 'Escape' will work while
-+'Space' and 'escape' won't.
-+
-+Modifiers are given as boolean attributes, possible modifiers are Mod1, Mod2,
-+Mod3, Mod4, Control, Shift, Lock. Sensible combinations strongly depend on your
-+keyboard configuration, but usually you will need Mod1 and Control. Boolean in
-+this context means '0', 'false' and an unspecified attribute are false, anything
-+else is considered true.
-+
-+Everything in this file is case-sensitive. Unknown lines are ignored.
-+Keybindings are evaluated from top to bottom, so if a keybinding matches, other
-+keybindings further down will be ignored. The contents of the file replaces the
-+default keybindings, and only one file is read, no merging between different
-+configuration files is done. This also means that an empty or invalid configuration
-+file deactivates all keybindings.
-+
-+List of possible 'action' attributes:
-+-------------------------------------
-+
-+close_session
-+switch_all_screens
-+minimize
-+left
-+up
-+right
-+down
-+resize
-+defer
-+ignore
-+fullscreen
-+viewport_move_left
-+viewport_move_up
-+viewport_move_right
-+viewport_move_down
-+
-+Only in builds with certain debugging options enabled, ignored otherwise:
-+force_synchronization
-+debug_tree
-+regions_on_screen
-+test_input
-+deactivate_input_devices_grab
---- a/nx-X11/programs/Xserver/hw/nxagent/Args.c
-+++ b/nx-X11/programs/Xserver/hw/nxagent/Args.c
-@@ -149,6 +149,8 @@
- 
- char nxagentVerbose = 0;
- 
-+char *nxagentKeystrokeFile = NULL;
-+
- int ddxProcessArgument(int argc, char *argv[], int i)
- {
-   /*
-@@ -1022,6 +1024,20 @@
-     return 1;
-   }
- 
-+  if (!strcmp(argv[i], "-keystrokefile"))
-+  {
-+    if (i + 1 < argc)
-+    {
-+      if (NULL != (nxagentKeystrokeFile = strdup(argv[i + 1])))
-+      {
-+        return 2;
-+      } else {
-+	FatalError("malloc failed");
-+      }
-+    }
-+    return 0;
-+  }
-+
-   return 0;
- }
- 
---- a/nx-X11/programs/Xserver/hw/nxagent/Args.h
-+++ b/nx-X11/programs/Xserver/hw/nxagent/Args.h
-@@ -83,4 +83,6 @@
- 
- extern int nxagentRemoteMajor;
- 
-+extern char *nxagentKeystrokeFile;
-+
- #endif /* __Args_H__ */
diff --git a/debian/patches/series b/debian/patches/series
index 5780128..764a6fe 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,4 +1,3 @@
-320_nxagent_configurable-keystrokes.full.patch
 321_nxagent_x2go-specific-keystroke-config.full.patch
 400_nxcomp-version.full+lite.patch
 #401_nxcomp_bigrequests-and-genericevent-extensions.full+lite.patch
diff --git a/nx-X11/programs/Xserver/Imakefile b/nx-X11/programs/Xserver/Imakefile
index c803a99..c31102b 100644
--- a/nx-X11/programs/Xserver/Imakefile
+++ b/nx-X11/programs/Xserver/Imakefile
@@ -1013,15 +1013,18 @@ $(NXAGENTOBJS) $(NXAGENTLIBS) $(NXAGENTSYSLIBS):: $(NXAGENTDIRS)
 #if defined(SunArchitecture)
 NXAGENTNXLIBS = -L ../../../nxcomp -L ../../../nxcompext -L ../../../nxcompshad \
                 -lXcomp -lXcompext -lXcompshad -lrt -L/usr/sfw/lib -lXrender -lXfixes \
-                -L../../../nx-X11/exports/lib -lXtst -lXdamage -lXrandr -lXcomposite -lXdmcp
+                -L../../../nx-X11/exports/lib -lXtst -lXdamage -lXrandr -lXcomposite -lXdmcp \
+`pkg-config --libs libxml-2.0`
 #elif defined(cygwinArchitecture)
 NXAGENTNXLIBS = -L ../../../nxcomp -L ../../../nxcompext \
                 -lXcomp -lXcompext -lXrender -lX11 -lXext -lXcomposite -lXfixes \
-                -L ../../../nxcompshad -lXcompshad -L../../../nx-X11/exports/lib -lXtst -lXdmcp
+                -L ../../../nxcompshad -lXcompshad -L../../../nx-X11/exports/lib -lXtst -lXdmcp \
+`pkg-config --libs libxml-2.0`
 #else
 NXAGENTNXLIBS = -L ../../../nxcomp -L ../../../nxcompext -L ../../../nxcompshad \
                 -lXcomp -lXcompext -lXcompshad -lXrender -lX11 -lXext -lXfixes \
-                -L../../../nx-X11/exports/lib -lXtst -lXdamage -lXrandr -lXcomposite -lXinerama -lXdmcp
+                -L../../../nx-X11/exports/lib -lXtst -lXdamage -lXrandr -lXcomposite -lXinerama -lXdmcp \
+`pkg-config --libs libxml-2.0`
 #endif
 
 #endif
diff --git a/nx-X11/programs/Xserver/hw/nxagent/Args.c b/nx-X11/programs/Xserver/hw/nxagent/Args.c
index 7074a18..07e9670 100644
--- a/nx-X11/programs/Xserver/hw/nxagent/Args.c
+++ b/nx-X11/programs/Xserver/hw/nxagent/Args.c
@@ -149,6 +149,8 @@ static int nxagentGetDialogName(void);
 
 char nxagentVerbose = 0;
 
+char *nxagentKeystrokeFile = NULL;
+
 int ddxProcessArgument(int argc, char *argv[], int i)
 {
   /*
@@ -1022,6 +1024,20 @@ int ddxProcessArgument(int argc, char *argv[], int i)
     return 1;
   }
 
+  if (!strcmp(argv[i], "-keystrokefile"))
+  {
+    if (i + 1 < argc)
+    {
+      if (NULL != (nxagentKeystrokeFile = strdup(argv[i + 1])))
+      {
+        return 2;
+      } else {
+	FatalError("malloc failed");
+      }
+    }
+    return 0;
+  }
+
   return 0;
 }
 
diff --git a/nx-X11/programs/Xserver/hw/nxagent/Args.h b/nx-X11/programs/Xserver/hw/nxagent/Args.h
index 8f4d05d..85734be 100644
--- a/nx-X11/programs/Xserver/hw/nxagent/Args.h
+++ b/nx-X11/programs/Xserver/hw/nxagent/Args.h
@@ -83,4 +83,6 @@ extern int nxagentUserDefinedFontPath;
 
 extern int nxagentRemoteMajor;
 
+extern char *nxagentKeystrokeFile;
+
 #endif /* __Args_H__ */
diff --git a/nx-X11/programs/Xserver/hw/nxagent/Imakefile b/nx-X11/programs/Xserver/hw/nxagent/Imakefile
index a8e1621..d812c7f 100644
--- a/nx-X11/programs/Xserver/hw/nxagent/Imakefile
+++ b/nx-X11/programs/Xserver/hw/nxagent/Imakefile
@@ -142,7 +142,8 @@ INCLUDES = -I. -I../../../../../nxcomp -I../../../../../nxcompext -I../../../../
            -I../../miext/damage -I../../miext/cw \
 	   -I../../GL/glx -I../../GL/include -I../../../../lib/GL/include -I../../Xext \
            -I$(EXTINCSRC) -I$(XINCLUDESRC) \
-	   $(VFBINCLUDES) $(NXFONTINCLUDES) $(LIBXRANDRINCLUDES)
+	   $(VFBINCLUDES) $(NXFONTINCLUDES) $(LIBXRANDRINCLUDES) \
+           `pkg-config --cflags-only-I libxml-2.0`
 #ifdef SunArchitecture
 INCLUDES = -I. -I../../../../../nxcomp -I../../../../../nxcompext -I../../../../../nxcompshad \
            -I../../../../extras/Mesa/include \
@@ -152,7 +153,8 @@ INCLUDES = -I. -I../../../../../nxcomp -I../../../../../nxcompext -I../../../../
 	   -I../../GL/glx -I../../GL/include -I../../../../lib/GL/include -I../../Xext \
            -I../../miext/damage -I../../miext/cw \
            -I$(EXTINCSRC) -I$(XINCLUDESRC) \
-	   $(VFBINCLUDES) $(NXFONTINCLUDES) $(LIBXRANDRINCLUDES)
+	   $(VFBINCLUDES) $(NXFONTINCLUDES) $(LIBXRANDRINCLUDES) \
+           `pkg-config --cflags-only-I libxml-2.0`
 #else
 #ifdef cygwinArchitecture
 INCLUDES = -I. -I$(XBUILDINCDIR) -I$(FONTINCSRC) \
@@ -162,7 +164,8 @@ INCLUDES = -I. -I$(XBUILDINCDIR) -I$(FONTINCSRC) \
            -I../../../../../nxcomp -I../../../../../nxcompext -I../../../../../nxcompshad \
            -I../../../../extras/Mesa/include \
            -I$(EXTINCSRC) -I$(XINCLUDESRC) \
-	   $(VFBINCLUDES) $(NXFONTINCLUDES) $(LIBXRANDRINCLUDES)
+	   $(VFBINCLUDES) $(NXFONTINCLUDES) $(LIBXRANDRINCLUDES) \
+           `pkg-config --cflags-only-I libxml-2.0`
 #endif
 #endif
 
diff --git a/nx-X11/programs/Xserver/hw/nxagent/Keystroke.c b/nx-X11/programs/Xserver/hw/nxagent/Keystroke.c
index 6c6e477..d61c65b 100644
--- a/nx-X11/programs/Xserver/hw/nxagent/Keystroke.c
+++ b/nx-X11/programs/Xserver/hw/nxagent/Keystroke.c
@@ -28,8 +28,15 @@
 #include "Keystroke.h"
 #include "Drawable.h"
 
+#include <unistd.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
 extern Bool nxagentWMIsRunning;
 extern Bool nxagentIpaq;
+extern char *nxagentKeystrokeFile;
+Bool nxagentKeystrokeFileParsed = False;
 
 #ifdef NX_DEBUG_INPUT
 int nxagentDebugInputDevices = 0;
@@ -47,10 +54,370 @@ extern void nxagentDeactivateInputDevicesGrabs();
 #undef  DEBUG
 #undef  DUMP
 
+
+/* this table is used to parse actions given on the command line or in the
+ * config file, therefore indices have to match the enum in Keystroke.h */
+char * nxagentSpecialKeystrokeNames[] = {
+       "end_marker",
+       "close_session",
+       "switch_all_screens",
+       "minimize",
+       "left",
+       "up",
+       "right",
+       "down",
+       "resize",
+       "defer",
+       "ignore",
+       "force_synchronization",
+
+       "debug_tree",
+       "regions_on_screen",
+       "test_input",
+       "deactivate_input_devices_grab",
+
+       "fullscreen",
+       "viewport_move_left",
+       "viewport_move_up",
+       "viewport_move_right",
+       "viewport_move_down",
+       NULL,
+};
+
+struct nxagentSpecialKeystrokeMap default_map[] = {
+  /* stroke, modifierMask, modifierAltMeta, keysym */
+  {KEYSTROKE_DEBUG_TREE, ControlMask, 1, XK_q},
+  {KEYSTROKE_DEBUG_TREE, ControlMask, 1, XK_Q},
+  {KEYSTROKE_CLOSE_SESSION, ControlMask, 1, XK_t},
+  {KEYSTROKE_CLOSE_SESSION, ControlMask, 1, XK_T},
+  {KEYSTROKE_SWITCH_ALL_SCREENS, ControlMask, 1, XK_f},
+  {KEYSTROKE_SWITCH_ALL_SCREENS, ControlMask, 1, XK_F},
+  {KEYSTROKE_MINIMIZE, ControlMask, 1, XK_m},
+  {KEYSTROKE_MINIMIZE, ControlMask, 1, XK_M},
+  {KEYSTROKE_LEFT, ControlMask, 1, XK_Left},
+  {KEYSTROKE_LEFT, ControlMask, 1, XK_KP_Left},
+  {KEYSTROKE_UP, ControlMask, 1, XK_Up},
+  {KEYSTROKE_UP, ControlMask, 1, XK_KP_Up},
+  {KEYSTROKE_RIGHT, ControlMask, 1, XK_Right},
+  {KEYSTROKE_RIGHT, ControlMask, 1, XK_KP_Right},
+  {KEYSTROKE_DOWN, ControlMask, 1, XK_Down},
+  {KEYSTROKE_DOWN, ControlMask, 1, XK_KP_Down},
+  {KEYSTROKE_RESIZE, ControlMask, 1, XK_r},
+  {KEYSTROKE_RESIZE, ControlMask, 1, XK_R},
+  {KEYSTROKE_DEFER, ControlMask, 1, XK_e},
+  {KEYSTROKE_DEFER, ControlMask, 1, XK_E},
+  {KEYSTROKE_IGNORE, ControlMask, 1, XK_BackSpace},
+  {KEYSTROKE_IGNORE, 0, 0, XK_Terminate_Server},
+  {KEYSTROKE_FORCE_SYNCHRONIZATION, ControlMask, 1, XK_j},
+  {KEYSTROKE_FORCE_SYNCHRONIZATION, ControlMask, 1, XK_J},
+  {KEYSTROKE_REGIONS_ON_SCREEN, ControlMask, 1, XK_a},
+  {KEYSTROKE_REGIONS_ON_SCREEN, ControlMask, 1, XK_A},
+  {KEYSTROKE_TEST_INPUT, ControlMask, 1, XK_x},
+  {KEYSTROKE_TEST_INPUT, ControlMask, 1, XK_X},
+  {KEYSTROKE_DEACTIVATE_INPUT_DEVICES_GRAB, ControlMask, 1, XK_y},
+  {KEYSTROKE_DEACTIVATE_INPUT_DEVICES_GRAB, ControlMask, 1, XK_Y},
+  {KEYSTROKE_FULLSCREEN, ControlMask | ShiftMask, 1, XK_f},
+  {KEYSTROKE_FULLSCREEN, ControlMask | ShiftMask, 1, XK_F},
+  {KEYSTROKE_VIEWPORT_MOVE_LEFT, ControlMask | ShiftMask, 1, XK_Left},
+  {KEYSTROKE_VIEWPORT_MOVE_LEFT, ControlMask | ShiftMask, 1, XK_KP_Left},
+  {KEYSTROKE_VIEWPORT_MOVE_UP, ControlMask | ShiftMask, 1, XK_Up},
+  {KEYSTROKE_VIEWPORT_MOVE_UP, ControlMask | ShiftMask, 1, XK_KP_Up},
+  {KEYSTROKE_VIEWPORT_MOVE_RIGHT, ControlMask | ShiftMask, 1, XK_Right},
+  {KEYSTROKE_VIEWPORT_MOVE_RIGHT, ControlMask | ShiftMask, 1, XK_KP_Right},
+  {KEYSTROKE_VIEWPORT_MOVE_DOWN, ControlMask | ShiftMask, 1, XK_Down},
+  {KEYSTROKE_VIEWPORT_MOVE_DOWN, ControlMask | ShiftMask, 1, XK_KP_Down},
+  {KEYSTROKE_END_MARKER, 0, 0, 0},
+};
+struct nxagentSpecialKeystrokeMap *map = default_map;
+
+static int modifier_matches(unsigned int mask, int compare_alt_meta, unsigned int state)
+{
+  /* nxagentAltMetaMask needs special handling
+   * it seems to me its an and-ed mask of all possible meta and alt keys
+   * somehow...
+   *
+   * otherwise this function would be just a simple bitop
+   */
+  int ret = 1;
+
+  if (compare_alt_meta) {
+    if (! (state & nxagentAltMetaMask)) {
+      ret = 0;
+    }
+
+    mask &= ~nxagentAltMetaMask;
+  }
+
+  /* all modifiers except meta/alt have to match exactly, extra bits are evil */
+  if ((mask & state) != mask) {
+    ret = 0;
+  }
+
+  return ret;
+}
+
+static int read_binding_from_xmlnode(xmlNode *node, struct nxagentSpecialKeystrokeMap *ret)
+{
+  int successful = 0;
+  struct nxagentSpecialKeystrokeMap new = {0, 0, 0, 0};
+  xmlAttr *attr;
+
+  for (attr = node->properties; attr; attr = attr->next)
+  {
+    /* ignore attributes without data (which should never happen anyways) */
+    if (attr->children->content == NULL)
+    {
+      char *aname = (attr->name)?(attr->name):"unknown";
+      fprintf(stderr, "attribute %s with NULL value", aname);
+      continue;
+    }
+    if (strcmp((char *)attr->name, "action") == 0)
+    {
+      int i;
+      for (i = 0; nxagentSpecialKeystrokeNames[i] != NULL; i++)
+      {
+        if (strcmp(nxagentSpecialKeystrokeNames[i],(char *)attr->children->content) == 0)
+        {
+          /* this relies on the values of enum nxagentSpecialKeystroke and the
+           * indices of nxagentSpecialKeystrokeNames being in sync */
+          new.stroke = i;
+          break;
+        }
+      }
+      continue;
+    }
+    else if (strcmp((char *)attr->name, "key") == 0)
+    {
+      new.keysym = XStringToKeysym((char *)attr->children->content);
+      /* NoSymbol is usually 0, but could there be weird implementations? */
+      if (new.keysym == NoSymbol)
+      {
+        new.keysym = 0;
+      }
+      continue;
+    }
+
+    /* ignore attributes with value="0" or "false", everything else is interpreted as true */
+    if (strcmp((char *)attr->children->content, "0") == 0 || strcmp((char *)attr->children->content, "false") == 0)
+      continue;
+
+    if (strcmp((char *)attr->name, "Mod1") == 0)
+    {
+      new.modifierMask |= Mod1Mask;
+    }
+    else if (strcmp((char *)attr->name, "Mod2") == 0)
+    {
+      new.modifierMask |= Mod2Mask;
+    }
+    else if (strcmp((char *)attr->name, "Mod3") == 0)
+    {
+      new.modifierMask |= Mod3Mask;
+    }
+    else if (strcmp((char *)attr->name, "Mod4") == 0)
+    {
+      new.modifierMask |= Mod4Mask;
+    }
+    else if (strcmp((char *)attr->name, "Control") == 0)
+    {
+      new.modifierMask |= ControlMask;
+    }
+    else if (strcmp((char *)attr->name, "Shift") == 0)
+    {
+      new.modifierMask |= ShiftMask;
+    }
+    else if (strcmp((char *)attr->name, "Lock") == 0)
+    {
+      new.modifierMask |= LockMask;
+    }
+    else if (strcmp((char *)attr->name, "AltMeta") == 0)
+    {
+      new.modifierAltMeta = 1;
+    }
+  }
+
+  if (new.stroke != 0 && new.keysym != 0)
+  {
+    /* keysym and stroke are required, everything else is optional */
+    successful = 1;
+    memcpy(ret, &new, sizeof(struct nxagentSpecialKeystrokeMap));
+  }
+  return successful;
+}
+
+/*
+ * searches a keystroke xml file
+ *
+ * search order:
+ *  - '-keystrokefile' commandline parameter
+ *  - $NXAGENT_KEYSTROKEFILE environment variable
+ *  - $HOME/.nx/config/keystrokes.cfg
+ *  - /etc/nxagent/keystrokes.cfg
+ *  - hardcoded traditional NX default settings
+ */
+static void parse_keystroke_file(void)
+{
+  char *filename = NULL;
+
+  char *homefile = "/.nx/config/keystrokes.cfg";
+  char *etcfile = "/etc/nxagent/keystrokes.cfg";
+
+  if (nxagentKeystrokeFile != NULL && access(nxagentKeystrokeFile, R_OK) == 0)
+  {
+    filename = strdup(nxagentKeystrokeFile);
+    if (filename == NULL)
+    {
+      fprintf(stderr, "malloc failed");
+      exit(EXIT_FAILURE);
+    }
+  }
+  else if ((filename = getenv("NXAGENT_KEYSTROKEFILE")) != NULL && access(filename, R_OK) == 0)
+  {
+    filename = strdup(filename);
+    if (filename == NULL)
+    {
+      fprintf(stderr, "malloc failed");
+      exit(EXIT_FAILURE);
+    }
+  }
+  else
+  {
+    char *homedir = getenv("HOME");
+    filename = NULL;
+    if (homedir != NULL)
+    {
+      homedir = strdup(homedir);
+      if (homedir == NULL)
+      {
+        fprintf(stderr, "malloc failed");
+exit(EXIT_FAILURE);
+      }
+      filename = calloc(1, strlen(homefile) + strlen(homedir) + 1);
+      if (filename == NULL)
+      {
+        fprintf(stderr, "malloc failed");
+        exit(EXIT_FAILURE);
+      }
+      strcpy(filename, homedir);
+      strcpy(filename + strlen(homedir), homefile);
+      if (homedir)
+      {
+        free(homedir);
+      }
+    }
+
+    if (access(filename, R_OK) == 0)
+    {
+      /* empty */
+    }
+    else if (access(etcfile, R_OK) == 0)
+    {
+      if (filename)
+        free(filename);
+      filename = strdup(etcfile);
+      if (filename == NULL)
+      {
+        fprintf(stderr, "malloc failed");
+        exit(EXIT_FAILURE);
+      }
+    }
+    else
+    {
+      if (filename)
+free(filename);
+      filename = NULL;
+    }
+  }
+
+  /* now we know which file to read, if any */
+  if (filename)
+  {
+    xmlDoc *doc = NULL;
+    xmlNode *root = NULL;
+    LIBXML_TEST_VERSION
+    doc = xmlReadFile(filename, NULL, 0);
+    if (doc != NULL)
+    {
+      xmlNode *cur = NULL;
+      root = xmlDocGetRootElement(doc);
+
+      for (cur = root; cur; cur = cur->next)
+      {
+        if (cur->type == XML_ELEMENT_NODE && strcmp((char *)cur->name, "keystrokes") == 0)
+{
+          xmlNode *bindings = NULL;
+          int num = 0;
+          int idx = 0;
+
+          for (bindings = cur->children; bindings; bindings = bindings->next)
+          {
+            if (bindings->type == XML_ELEMENT_NODE && strcmp((char *)bindings->name, "keystroke") == 0)
+            {
+              num++;
+            }
+          }
+          map = calloc((num + 1), sizeof(struct nxagentSpecialKeystrokeMap));
+          if (map == NULL)
+          {
+            fprintf(stderr, "malloc failed");
+            exit(EXIT_FAILURE);
+          }
+
+          for (bindings = cur->children; bindings; bindings = bindings->next)
+          {
+            if (bindings->type == XML_ELEMENT_NODE && strcmp((char *)bindings->name, "keystroke") == 0)
+            {
+              int res = 0;
+              res = read_binding_from_xmlnode(bindings, &(map[idx]));
+              if (res)
+                idx++;
+            }
+          }
+
+          map[idx].stroke = KEYSTROKE_END_MARKER;
+        }
+      }
+
+      xmlFreeDoc(doc);
+      xmlCleanupParser();
+    }
+    else
+    {
+      #ifdef DEBUG
+      fprintf("XML parsing for %s failed\n", filename);
+      #endif
+    }
+    free(filename);
+  }
+}
+
+static enum nxagentSpecialKeystroke find_keystroke(XKeyEvent *X)
+{
+  KeySym keysym = XKeycodeToKeysym(nxagentDisplay, X->keycode, 0);
+  struct nxagentSpecialKeystrokeMap *cur = map;
+
+  if (! nxagentKeystrokeFileParsed)
+  {
+    parse_keystroke_file();
+    nxagentKeystrokeFileParsed = True;
+  }
+
+  enum nxagentSpecialKeystroke ret = KEYSTROKE_NOTHING;
+
+  while (cur->stroke != KEYSTROKE_END_MARKER) {
+    if (cur->keysym == keysym && modifier_matches(cur->modifierMask, cur->modifierAltMeta, X->state)) {
+      return cur->stroke;
+    }
+    cur++;
+  }
+
+  return ret;
+}
+
 int nxagentCheckSpecialKeystroke(XKeyEvent *X, enum HandleEventResult *result)
 {
   KeySym sym;
   int index = 0;
+  enum nxagentSpecialKeystroke stroke = find_keystroke(X);
 
   *result = doNothing;
 
@@ -87,257 +454,128 @@ int nxagentCheckSpecialKeystroke(XKeyEvent *X, enum HandleEventResult *result)
     return 1;
   }
 
-  if ((X -> state & nxagentAltMetaMask) &&
-          ((X -> state & (ControlMask | ShiftMask)) == ControlMask))
-  {
-    switch (sym)
-    {
+  switch (stroke) {
+    case KEYSTROKE_DEBUG_TREE:
       #ifdef DEBUG_TREE
-
-      case XK_q:
-      case XK_Q:
-      {
-        *result = doDebugTree;
-
-        break;
-      }
-
-      #endif /* DEBUG_TREE */
-
-      case XK_t:
-      case XK_T:
-      {
-        *result = doCloseSession;
-
-        break;
-      }
-      case XK_f:
-      case XK_F:
-      {
-        if (nxagentOption(Rootless) == False)
-        {
-          *result = doSwitchAllScreens;
-        }
-
-        break;
-      }
-      case XK_m:
-      case XK_M:
-      {
-        if (nxagentOption(Rootless) == False)
-        {
-          *result = doMinimize;
-        }
-
-        break;
-      }
-      case XK_Left:
-      case XK_KP_Left:
-      {
-        if (nxagentOption(Rootless) == False &&
-                nxagentOption(DesktopResize) == False)
-        {
-          *result = doViewportLeft;
-        }
-
-        break;
-      }
-      case XK_Up:
-      case XK_KP_Up:
-      {
-        if (nxagentOption(Rootless) == False &&
-                nxagentOption(DesktopResize) == False)
-        {
-          *result = doViewportUp;
-        }
-
-        break;
+      *result = doDebugTree;
+      #endif
+      break;
+    case KEYSTROKE_CLOSE_SESSION:
+      *result = doCloseSession;
+      break;
+    case KEYSTROKE_SWITCH_ALL_SCREENS:
+      if (nxagentOption(Rootless) == False) {
+        *result = doSwitchAllScreens;
       }
-      case XK_Right:
-      case XK_KP_Right:
-      {
-        if (nxagentOption(Rootless) == False &&
-                nxagentOption(DesktopResize) == False)
-        {
-          *result = doViewportRight;
-        }
-
-        break;
+      break;
+    case KEYSTROKE_MINIMIZE:
+      if (nxagentOption(Rootless) == False) {
+        *result = doMinimize;
       }
-      case XK_Down:
-      case XK_KP_Down:
-      {
-        if (nxagentOption(Rootless) == 0 &&
-                nxagentOption(DesktopResize) == 0)
-        {
-          *result = doViewportDown;
-        }
-
-        break;
+      break;
+    case KEYSTROKE_LEFT:
+      if (nxagentOption(Rootless) == False &&
+          nxagentOption(DesktopResize) == False) {
+        *result = doViewportLeft;
       }
-      case XK_R:
-      case XK_r:
-      {
-        if (nxagentOption(Rootless) == 0)
-        {
-          *result = doSwitchResizeMode;
-        }
-
-        break;
+      break;
+    case KEYSTROKE_UP:
+      if (nxagentOption(Rootless) == False &&
+          nxagentOption(DesktopResize) == False) {
+        *result = doViewportUp;
       }
-      case XK_E:
-      case XK_e:
-      {
-        *result = doSwitchDeferMode;
-
-        break;
+      break;
+    case KEYSTROKE_RIGHT:
+      if (nxagentOption(Rootless) == False &&
+          nxagentOption(DesktopResize) == False) {
+        *result = doViewportRight;
       }
-      case XK_BackSpace:
-      case XK_Terminate_Server:
-      {
-        /*
-         * Discard Ctrl-Alt-BackSpace key.
-         */
-
-        return 1;
-
-        break;
+      break;
+    case KEYSTROKE_DOWN:
+      if (nxagentOption(Rootless) == False &&
+          nxagentOption(DesktopResize) == False) {
+        *result = doViewportDown;
       }
-
-      case XK_J:
-      case XK_j:
-      {
-        nxagentForceSynchronization = 1;
-
-        return 1;
+      break;
+    case KEYSTROKE_RESIZE:
+      if (nxagentOption(Rootless) == False) {
+        *result = doSwitchResizeMode;
       }
-
+      break;
+    case KEYSTROKE_DEFER:
+      *result = doSwitchDeferMode;
+      break;
+    case KEYSTROKE_IGNORE:
+      /* this is used e.g. to ignore C-A-Backspace aka XK_Terminate_Server */
+      return 1;
+      break;
+    case KEYSTROKE_FORCE_SYNCHRONIZATION:
+      nxagentForceSynchronization = 1;
+      break;
+    case KEYSTROKE_REGIONS_ON_SCREEN:
       #ifdef DUMP
-
-      case XK_A:
-      case XK_a:
-      {
-        /*
-         * Used to test the lazy encoding.
-         */
-
-        nxagentRegionsOnScreen();
-
-        return 1;
-      }
-
+      nxagentRegionsOnScreen();
       #endif
-
+      break;
+    case KEYSTROKE_TEST_INPUT:
+      /*
+       * Used to test the input devices state.
+       */
       #ifdef NX_DEBUG_INPUT
-
-      case XK_X:
-      case XK_x:
-      {
-        /*
-         * Used to test the input devices state.
-         */
-
-        if (X -> type == KeyPress)
-        {
-          if (nxagentDebugInputDevices == 0)
-          {
-            fprintf(stderr, "Info: Turning input devices debug ON.\n");
-    
-            nxagentDebugInputDevices = 1;
-          }
-          else
-          {
-            fprintf(stderr, "Info: Turning input devices debug OFF.\n");
-    
-            nxagentDebugInputDevices = 0;
-    
-            nxagentLastInputDevicesDumpTime = 0;
-          }
+      if (X -> type == KeyPress) {
+        if (nxagentDebugInputDevices == 0) {
+          fprintf(stderr, "Info: Turning input devices debug ON.\n");
+          nxagentDebugInputDevices = 1;
+        } else {
+          fprintf(stderr, "Info: Turning input devices debug OFF.\n");
+          nxagentDebugInputDevices = 0;
+          nxagentLastInputDevicesDumpTime = 0;
         }
-
-        return 1;
       }
-
-      case XK_Y:
-      case XK_y:
-      {
-        /*
-         * Used to deactivate input devices grab.
-         */
-
-        if (X -> type == KeyPress)
-        {
-          nxagentDeactivateInputDevicesGrabs();
-        }
-
-        return 1;
+      return 1;
+      #endif
+      break;
+    case KEYSTROKE_DEACTIVATE_INPUT_DEVICES_GRAB:
+      #ifdef NX_DEBUG_INPUT
+      if (X->type == KeyPress) {
+        nxagentDeactivateInputDevicesGrab();
       }
-
+      return 1;
       #endif
-    }
-  }
-  else if ((X -> state & nxagentAltMetaMask) &&
-               ((X -> state & (ControlMask | ShiftMask)) == (ControlMask |
-                   ShiftMask)))
-  {
-    switch (sym)
-    {
-      case XK_f:
-      case XK_F:
-      {
-        if (nxagentOption(Rootless) == 0)
-        {
-          *result = doSwitchFullscreen;
-        }
-
-        break;
+      break;
+    case KEYSTROKE_FULLSCREEN:
+      if (nxagentOption(Rootless) == 0) {
+        *result = doSwitchFullscreen;
       }
-      case XK_Left:
-      case XK_KP_Left:
-      {
-        if (nxagentOption(Rootless) == 0 &&
-                nxagentOption(DesktopResize) == 0)
-        {
-          *result = doViewportMoveLeft;
-        }
-
-        break;
+      break;
+    case KEYSTROKE_VIEWPORT_MOVE_LEFT:
+      if (nxagentOption(Rootless) == 0 &&
+          nxagentOption(DesktopResize) == 0) {
+        *result = doViewportMoveLeft;
       }
-      case XK_Up:
-      case XK_KP_Up:
-      {
-        if (nxagentOption(Rootless) == 0 &&
-                nxagentOption(DesktopResize) == 0)
-        {
-          *result = doViewportMoveUp;
-        }
-
-        break;
+      break;
+    case KEYSTROKE_VIEWPORT_MOVE_UP:
+      if (nxagentOption(Rootless) == 0 &&
+          nxagentOption(DesktopResize) == 0) {
+        *result = doViewportMoveUp;
       }
-      case XK_Right:
-      case XK_KP_Right:
-      {
-        if (nxagentOption(Rootless) == 0 &&
-                nxagentOption(DesktopResize) == 0)
-        {
-          *result = doViewportMoveRight;
-        }
-
-        break;
+      break;
+    case KEYSTROKE_VIEWPORT_MOVE_RIGHT:
+      if (nxagentOption(Rootless) == 0 &&
+          nxagentOption(DesktopResize) == 0) {
+        *result = doViewportMoveRight;
       }
-      case XK_Down:
-      case XK_KP_Down:
-      {
-        if (nxagentOption(Rootless) == 0 &&
-                nxagentOption(DesktopResize) == 0)
-        {
-          *result = doViewportMoveDown;
-        }
-
-        break;
+      break;
+    case KEYSTROKE_VIEWPORT_MOVE_DOWN:
+      if (nxagentOption(Rootless) == 0 &&
+          nxagentOption(DesktopResize) == 0) {
+        *result = doViewportMoveDown;
       }
-    }
+      break;
+    case KEYSTROKE_NOTHING: /* do nothing. difference to KEYSTROKE_IGNORE is the return value */
+    case KEYSTROKE_END_MARKER: /* just to make gcc STFU */
+    case KEYSTROKE_MAX:
+      break;
   }
-
   return (*result == doNothing) ? 0 : 1;
 }
diff --git a/nx-X11/programs/Xserver/hw/nxagent/Keystroke.h b/nx-X11/programs/Xserver/hw/nxagent/Keystroke.h
index ef71a88..d9575c8 100644
--- a/nx-X11/programs/Xserver/hw/nxagent/Keystroke.h
+++ b/nx-X11/programs/Xserver/hw/nxagent/Keystroke.h
@@ -24,4 +24,51 @@ extern int nxagentCheckSpecialKeystroke(XKeyEvent*, enum HandleEventResult*);
 
 unsigned int nxagentAltMetaMask;
 
+/* keep this sorted, do not rely on any numerical value in this enum, and be aware
+ * that KEYSTROKE_MAX may be used in a malloc */
+
+/* also be aware that if changing any numerical values, you also need to change values
+ * Keystroke.c nxagentSpecialKeystrokeNames */
+enum nxagentSpecialKeystroke {
+       /* 0 is used as end marker */
+       KEYSTROKE_END_MARKER = 0,
+       KEYSTROKE_CLOSE_SESSION = 1,
+       KEYSTROKE_SWITCH_ALL_SCREENS = 2,
+       KEYSTROKE_MINIMIZE = 3,
+       KEYSTROKE_LEFT = 4,
+       KEYSTROKE_UP = 5,
+       KEYSTROKE_RIGHT = 6,
+       KEYSTROKE_DOWN = 7,
+       KEYSTROKE_RESIZE = 8,
+       KEYSTROKE_DEFER = 9,
+       KEYSTROKE_IGNORE = 10,
+       KEYSTROKE_FORCE_SYNCHRONIZATION = 11,
+
+       /* stuff used for debugging, probably not useful for most people */
+       KEYSTROKE_DEBUG_TREE = 12,
+       KEYSTROKE_REGIONS_ON_SCREEN = 13,
+       KEYSTROKE_TEST_INPUT = 14,
+       KEYSTROKE_DEACTIVATE_INPUT_DEVICES_GRAB = 15,
+
+       KEYSTROKE_FULLSCREEN = 16,
+       KEYSTROKE_VIEWPORT_MOVE_LEFT = 17,
+       KEYSTROKE_VIEWPORT_MOVE_UP = 18,
+       KEYSTROKE_VIEWPORT_MOVE_RIGHT = 19,
+       KEYSTROKE_VIEWPORT_MOVE_DOWN = 20,
+
+       KEYSTROKE_NOTHING = 21,
+
+       /* insert more here, increment KEYSTROKE_MAX accordingly.
+        * then update string translation below */
+
+       KEYSTROKE_MAX=22,
+};
+
+struct nxagentSpecialKeystrokeMap {
+       enum nxagentSpecialKeystroke stroke;
+       unsigned int modifierMask; /* everything except alt/meta */
+       int modifierAltMeta; /* modifier combination should include alt/meta */
+       KeySym keysym;
+};
+
 #endif /* __Keystroke_H__ */

--
Alioth's /srv/git/_hooks_/post-receive-email on /srv/git/code.x2go.org/nx-libs.git


More information about the x2go-commits mailing list