aboutsummaryrefslogtreecommitdiff
path: root/debian/patches/0320_nxagent_configurable-keystrokes.full.patch
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches/0320_nxagent_configurable-keystrokes.full.patch')
-rw-r--r--debian/patches/0320_nxagent_configurable-keystrokes.full.patch1031
1 files changed, 1031 insertions, 0 deletions
diff --git a/debian/patches/0320_nxagent_configurable-keystrokes.full.patch b/debian/patches/0320_nxagent_configurable-keystrokes.full.patch
new file mode 100644
index 000000000..c799c8b93
--- /dev/null
+++ b/debian/patches/0320_nxagent_configurable-keystrokes.full.patch
@@ -0,0 +1,1031 @@
+Author: Alexander Wuerstlein <arw@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__ */