Author: Alexander Wuerstlein 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 + +#include +#include + 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: + + + + + + + + + + + + + +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__ */