2025-08-08 20:00:36 +02:00

735 lines
23 KiB
C

/**************************************************************************/
/* */
/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
/* Copyright (c) 2008-2017 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
/* Copyright (c) 2011-2022 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
/* Copyright (c) 2014-2019 Mihai Moldovan <ionic@ionic.de> */
/* Copyright (c) 2014-2022 Ulrich Sibiller <uli42@gmx.de> */
/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
/* */
/* NXAGENT, NX protocol compression and NX extensions to this software */
/* are copyright of the aforementioned persons and companies. */
/* */
/* Redistribution and use of the present software is allowed according */
/* to terms specified in the file LICENSE which comes in the source */
/* distribution. */
/* */
/* All rights reserved. */
/* */
/* NOTE: This software has received contributions from various other */
/* contributors, only the core maintainers and supporters are listed as */
/* copyright holders. Please contact us, if you feel you should be listed */
/* as copyright holder, as well. */
/* */
/**************************************************************************/
#include "X.h"
#include "keysym.h"
#include "screenint.h"
#include "scrnintstr.h"
#include "Agent.h"
#include "Display.h"
#include "Events.h"
#include "Options.h"
#include "Keyboard.h"
#include "Drawable.h"
#include "Init.h" /* extern Bool nxagentX2go */
#include "Utils.h"
#include <unistd.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
extern Bool nxagentWMIsRunning;
extern char *nxagentKeystrokeFile;
#ifdef NX_DEBUG_INPUT
int nxagentDebugInputDevices = False;
unsigned long nxagentLastInputDevicesDumpTime = 0;
extern void nxagentDeactivateInputDevicesGrabs();
#endif
/*
* Set here the required log level.
*/
#define PANIC
#define WARNING
#undef TEST
#undef DEBUG
#undef DUMP
/* must be included _after_ DUMP */
#include "Keystroke.h"
/* 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",
"fullscreen",
"minimize",
"defer",
"ignore",
"force_synchronization",
#ifdef DEBUG_TREE
"debug_tree",
#endif
#ifdef DUMP
"regions_on_screen",
#endif
#ifdef NX_DEBUG_INPUT
"test_input",
"deactivate_input_devices_grab",
#endif
"resize",
"viewport_move_left",
"viewport_move_up",
"viewport_move_right",
"viewport_move_down",
"viewport_scroll_left",
"viewport_scroll_up",
"viewport_scroll_right",
"viewport_scroll_down",
"reread_keystrokes",
"autograb",
"dump_clipboard",
NULL,
};
struct nxagentSpecialKeystrokeMap default_map[] = {
/* stroke, modifierMask, modifierAltMeta, keysym */
#ifdef DEBUG_TREE
{KEYSTROKE_DEBUG_TREE, ControlMask, True, XK_q},
#endif
{KEYSTROKE_CLOSE_SESSION, ControlMask, True, XK_t},
{KEYSTROKE_SWITCH_ALL_SCREENS, ControlMask, True, XK_f},
{KEYSTROKE_FULLSCREEN, ControlMask | ShiftMask, True, XK_f},
{KEYSTROKE_MINIMIZE, ControlMask, True, XK_m},
{KEYSTROKE_DEFER, ControlMask, True, XK_e},
{KEYSTROKE_FORCE_SYNCHRONIZATION, ControlMask, True, XK_j},
#ifdef DUMP
{KEYSTROKE_REGIONS_ON_SCREEN, ControlMask, True, XK_a},
#endif
#ifdef NX_DEBUG_INPUT
{KEYSTROKE_TEST_INPUT, ControlMask, True, XK_x},
{KEYSTROKE_DEACTIVATE_INPUT_DEVICES_GRAB, ControlMask, True, XK_y},
#endif
{KEYSTROKE_RESIZE, ControlMask, True, XK_r},
{KEYSTROKE_VIEWPORT_MOVE_LEFT, ControlMask | ShiftMask, True, XK_Left},
{KEYSTROKE_VIEWPORT_MOVE_LEFT, ControlMask | ShiftMask, True, XK_KP_Left},
{KEYSTROKE_VIEWPORT_MOVE_UP, ControlMask | ShiftMask, True, XK_Up},
{KEYSTROKE_VIEWPORT_MOVE_UP, ControlMask | ShiftMask, True, XK_KP_Up},
{KEYSTROKE_VIEWPORT_MOVE_RIGHT, ControlMask | ShiftMask, True, XK_Right},
{KEYSTROKE_VIEWPORT_MOVE_RIGHT, ControlMask | ShiftMask, True, XK_KP_Right},
{KEYSTROKE_VIEWPORT_MOVE_DOWN, ControlMask | ShiftMask, True, XK_Down},
{KEYSTROKE_VIEWPORT_MOVE_DOWN, ControlMask | ShiftMask, True, XK_KP_Down},
{KEYSTROKE_VIEWPORT_SCROLL_LEFT, ControlMask, True, XK_Left},
{KEYSTROKE_VIEWPORT_SCROLL_LEFT, ControlMask, True, XK_KP_Left},
{KEYSTROKE_VIEWPORT_SCROLL_UP, ControlMask, True, XK_Up},
{KEYSTROKE_VIEWPORT_SCROLL_UP, ControlMask, True, XK_KP_Up},
{KEYSTROKE_VIEWPORT_SCROLL_RIGHT, ControlMask, True, XK_Right},
{KEYSTROKE_VIEWPORT_SCROLL_RIGHT, ControlMask, True, XK_KP_Right},
{KEYSTROKE_VIEWPORT_SCROLL_DOWN, ControlMask, True, XK_Down},
{KEYSTROKE_VIEWPORT_SCROLL_DOWN, ControlMask, True, XK_KP_Down},
{KEYSTROKE_REREAD_KEYSTROKES, ControlMask, True, XK_k},
{KEYSTROKE_AUTOGRAB, ControlMask, True, XK_g},
{KEYSTROKE_DUMP_CLIPBOARD, ControlMask | ShiftMask, True, XK_c},
{KEYSTROKE_END_MARKER, 0, False, NoSymbol},
};
struct nxagentSpecialKeystrokeMap *map = default_map;
static Bool 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
*/
Bool ret = True;
if (compare_alt_meta) {
if (! (state & nxagentAltMetaMask)) {
ret = False;
}
mask &= ~nxagentAltMetaMask;
state &= ~nxagentAltMetaMask;
}
/* ignore CapsLock and/or Numlock if the keystroke does not
explicitly require them */
if ( !(mask & nxagentCapsMask) )
state &= ~nxagentCapsMask;
if ( !(mask & nxagentNumlockMask) )
state &= ~nxagentNumlockMask;
/* all modifiers except meta/alt have to match exactly, extra bits are evil */
if (mask != state) {
ret = False;
}
return ret;
}
static Bool read_binding_from_xmlnode(xmlNode *node, struct nxagentSpecialKeystrokeMap *ret)
{
/* init the struct to have proper values in case not all attributes are found */
struct nxagentSpecialKeystrokeMap newkm = {
.stroke = KEYSTROKE_NOTHING,
.modifierMask = 0,
.modifierAltMeta = False,
.keysym = NoSymbol
};
for (xmlAttr *attr = node->properties; attr; attr = attr->next)
{
/* ignore attributes without data (which should never happen anyways) */
if (attr->children->content == NULL)
{
#ifdef DEBUG
char *aname = (attr->name)?((char *)attr->name):"unknown";
fprintf(stderr, "attribute %s with NULL value", aname);
#endif
continue;
}
if (strcmp((char *)attr->name, "action") == 0)
{
newkm.stroke = KEYSTROKE_NOTHING;
for (int 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 */
newkm.stroke = i;
break;
}
}
if (newkm.stroke == KEYSTROKE_NOTHING)
fprintf(stderr, "Info: ignoring unknown keystroke action '%s'.\n", (char *)attr->children->content);
continue;
}
else if (strcmp((char *)attr->name, "key") == 0)
{
newkm.keysym = XStringToKeysym((char *)attr->children->content);
continue;
}
else
{
/* 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) { newkm.modifierMask |= Mod1Mask; }
else if (strcmp((char *)attr->name, "Mod2") == 0) { newkm.modifierMask |= Mod2Mask; }
else if (strcmp((char *)attr->name, "Mod3") == 0) { newkm.modifierMask |= Mod3Mask; }
else if (strcmp((char *)attr->name, "Mod4") == 0) { newkm.modifierMask |= Mod4Mask; }
else if (strcmp((char *)attr->name, "Mod5") == 0) { newkm.modifierMask |= Mod5Mask; }
else if (strcmp((char *)attr->name, "Control") == 0) { newkm.modifierMask |= ControlMask; }
else if (strcmp((char *)attr->name, "Shift") == 0) { newkm.modifierMask |= ShiftMask; }
else if (strcmp((char *)attr->name, "Lock") == 0) { newkm.modifierMask |= LockMask; }
else if (strcmp((char *)attr->name, "AltMeta") == 0) { newkm.modifierAltMeta = True; }
}
}
if (newkm.stroke != KEYSTROKE_NOTHING && newkm.keysym != NoSymbol)
{
/* keysym and stroke are required, everything else is optional */
memcpy(ret, &newkm, sizeof(struct nxagentSpecialKeystrokeMap));
return True;
}
else
return False;
}
char *checkKeystrokeFile(char *filename)
{
if (!filename)
return NULL;
if (access(filename, R_OK) == 0)
{
return filename;
}
else
{
#ifdef WARNING
fprintf(stderr, "Warning: Cannot read keystroke file '%s'.\n", filename);
#endif
return NULL;
}
}
/*
* 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
* If run in x2go flavour different filenames and varnames are used.
*/
void nxagentInitKeystrokes(Bool force)
{
char *filename = NULL;
char *homefile;
char *etcfile;
char *envvar;
/* used for tracking if the config file parsing has already been
done (regardless of the result) */
static Bool done = False;
if (force) {
if (map != default_map)
{
SAFE_free(map);
map = default_map;
}
fprintf(stderr, "Info: re-reading keystrokes configuration\n");
}
else
{
if (done)
return;
}
done = True;
#ifdef X2GO
if (nxagentX2go) {
homefile = "/.x2go/config/keystrokes.cfg";
etcfile = "/etc/x2go/keystrokes.cfg";
envvar = "X2GO_KEYSTROKEFILE";
}
else
#endif
{
homefile = "/.nx/config/keystrokes.cfg";
etcfile = "/etc/nxagent/keystrokes.cfg";
envvar = "NXAGENT_KEYSTROKEFILE";
}
char *homepath = NULL;
char *homedir = getenv("HOME");
if (homedir)
{
if (-1 == asprintf(&homepath, "%s%s", homedir, homefile))
{
fprintf(stderr, "malloc failed");
exit(EXIT_FAILURE);
}
}
/* if any of the files can be read we have our candidate */
if ((filename = checkKeystrokeFile(nxagentKeystrokeFile)) ||
(filename = checkKeystrokeFile(getenv(envvar))) ||
(filename = checkKeystrokeFile(homepath)) ||
(filename = checkKeystrokeFile(etcfile)))
{
LIBXML_TEST_VERSION
xmlDoc *doc = xmlReadFile(filename, NULL, 0);
if (doc)
{
fprintf(stderr, "Info: using keystrokes file '%s'\n", filename);
for (xmlNode *cur = xmlDocGetRootElement(doc); cur; cur = cur->next)
{
if (cur->type == XML_ELEMENT_NODE && strcmp((char *)cur->name, "keystrokes") == 0)
{
xmlNode *bindings;
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++;
}
}
#ifdef DEBUG
fprintf(stderr, "%s: found %d keystrokes in %s\n", __func__, num, filename);
#endif
if (!(map = calloc(num+1, sizeof(struct nxagentSpecialKeystrokeMap))))
{
fprintf(stderr, "calloc failed");
exit(EXIT_FAILURE);
}
for (bindings = cur->children; bindings; bindings = bindings->next)
{
map[idx].stroke = KEYSTROKE_NOTHING;
if (bindings->type == XML_ELEMENT_NODE &&
strcmp((char *)bindings->name, "keystroke") == 0 &&
read_binding_from_xmlnode(bindings, &(map[idx])))
{
Bool store = True;
for (int j = 0; j < idx; j++)
{
if (map[j].stroke != KEYSTROKE_NOTHING &&
map[idx].keysym != NoSymbol &&
map[j].keysym == map[idx].keysym &&
map[j].modifierMask == map[idx].modifierMask &&
map[j].modifierAltMeta == map[idx].modifierAltMeta)
{
#ifdef WARNING
fprintf(stderr, "Warning: ignoring keystroke '%s' (already in use by '%s')\n",
nxagentSpecialKeystrokeNames[map[idx].stroke],
nxagentSpecialKeystrokeNames[map[j].stroke]);
#endif
store = False;
break;
}
}
if (store)
idx++;
else
map[idx].stroke = KEYSTROKE_NOTHING;
}
}
#ifdef DEBUG
fprintf(stderr, "%s: read %d keystrokes", __func__, idx);
#endif
map[idx].stroke = KEYSTROKE_END_MARKER;
}
}
xmlFreeDoc(doc);
xmlCleanupParser();
}
#ifdef WARNING
else /* if (doc) */
{
fprintf(stderr, "Warning: could not read/parse keystrokes file '%s'\n", filename);
}
#endif
filename = NULL;
}
SAFE_free(homepath);
if (map == default_map)
{
fprintf(stderr, "Info: Using builtin keystrokes.\n");
}
nxagentDumpKeystrokes();
}
static char *nxagentGetSingleKeystrokeString(struct nxagentSpecialKeystrokeMap *cur)
{
if (!cur)
return strdup(""); /* caller is expected to free the returned string */
char *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9, *s10, *s11;
s1 = s2 = s3 = s4 = s5 = s6 = s7 = s8 = s9 = s10 = s11 = "";
unsigned int mask = cur->modifierMask;
if (mask & ControlMask) {s1 = "Ctrl+"; mask &= ~ControlMask;}
if (mask & ShiftMask) {s2 = "Shift+"; mask &= ~ShiftMask;}
/* these are only here for better readable modifier names. Normally
they are covered by the Mod<n> and Lock lines below */
if (cur->modifierAltMeta) {s3 = "Alt+"; mask &= ~(cur->modifierAltMeta);}
if (mask & nxagentCapsMask) {s4 = "CapsLock+"; mask &= ~nxagentCapsMask;}
if (mask & nxagentNumlockMask) {s5 = "NumLock+"; mask &= ~nxagentNumlockMask;}
if (mask & Mod1Mask) {s6 = "Mod1+"; mask &= ~Mod1Mask;}
if (mask & Mod2Mask) {s7 = "Mod2+"; mask &= ~Mod2Mask;}
if (mask & Mod3Mask) {s8 = "Mod3+"; mask &= ~Mod3Mask;}
if (mask & Mod4Mask) {s9 = "Mod4+"; mask &= ~Mod4Mask;}
if (mask & Mod5Mask) {s10 = "Mod5+"; mask &= ~Mod5Mask;}
if (mask & LockMask) {s11 = "Lock+"; mask &= ~LockMask;}
char *ret = NULL;
asprintf(&ret, "%s%s%s%s%s%s%s%s%s%s%s%s", s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, XKeysymToString(cur->keysym));
return ret;
}
/*
* return the _first_ keystroke for the passed keystroke name
*
* e.g. nxagentFindFirstKeystroke("resize") -> "Ctrl+Alt+r"
*
* result must be free()d after use.
*/
char *nxagentFindFirstKeystroke(char *name)
{
for (struct nxagentSpecialKeystrokeMap *cur = map; cur->stroke != KEYSTROKE_END_MARKER; cur++)
{
if (nxagentSpecialKeystrokeNames[cur->stroke] &&
strcmp(nxagentSpecialKeystrokeNames[cur->stroke], name) == 0)
{
return nxagentGetSingleKeystrokeString(cur);
}
}
return NULL;
}
/*
* return a string with linefeeds of all keystrokes who's name starts
* with the the passed string,
*
* e.g. nxagentFindKeystrokeString("viewport_scroll_")
* ->
* " viewport_scroll_left : Ctrl+Alt+Left
* viewport_scroll_left : Ctrl+Alt+KP_Left
* viewport_scroll_up : Ctrl+Alt+Up
* viewport_scroll_up : Ctrl+Alt+KP_Up
* viewport_scroll_right : Ctrl+Alt+Right
* viewport_scroll_right : Ctrl+Alt+KP_Right
* viewport_scroll_down : Ctrl+Alt+Down
* viewport_scroll_down : Ctrl+Alt+KP_Down
* "
* result must be free()d after use.
*/
char *nxagentFindMatchingKeystrokes(char *name)
{
int maxlen = 0;
for (int i = 0; nxagentSpecialKeystrokeNames[i]; i++)
maxlen = max(maxlen, strlen(nxagentSpecialKeystrokeNames[i]));
char * res = strdup(""); /* let the caller free the string */
for (struct nxagentSpecialKeystrokeMap *cur = map; cur->stroke != KEYSTROKE_END_MARKER; cur++)
{
if (nxagentSpecialKeystrokeNames[cur->stroke] &&
strncmp(nxagentSpecialKeystrokeNames[cur->stroke], name, strlen(name)) == 0)
{
char *tmp;
char *tmp1 = nxagentGetSingleKeystrokeString(cur);
if (-1 == asprintf(&tmp, "%s\n %-*s : %s", res, maxlen,
nxagentSpecialKeystrokeNames[cur->stroke],
tmp1))
{
SAFE_free(tmp1);
#ifdef TEST
fprintf(stderr, "%s: returning incomplete result:%s\n", __func__, res);
#endif
return res;
}
else
{
SAFE_free(tmp1);
free(res);
res = tmp;
}
}
}
#ifdef TEST
fprintf(stderr, "%s: returning result:\n%s", __func__, res);
#endif
return res;
}
void nxagentDumpKeystrokes(void)
{
char *s = nxagentFindMatchingKeystrokes("");
fprintf(stderr, "Currently known keystrokes:\n%s", s);
SAFE_free(s);
}
static enum nxagentSpecialKeystroke find_keystroke(XKeyEvent *X)
{
enum nxagentSpecialKeystroke ret = KEYSTROKE_NOTHING;
KeySym keysym = XKeycodeToKeysym(nxagentDisplay, X->keycode, 0);
#ifdef DEBUG
fprintf(stderr, "%s: got keysym '%c' (%d)\n", __func__, keysym, keysym);
#endif
for (struct nxagentSpecialKeystrokeMap *cur = map; cur->stroke != KEYSTROKE_END_MARKER; cur++) {
#ifdef DEBUG
fprintf(stderr,"%s: keysym %d stroke %d, type %d\n", __func__, cur->keysym, cur->stroke, X->type);
#endif
if (cur->keysym == keysym && modifier_matches(cur->modifierMask, cur->modifierAltMeta, X->state)) {
#ifdef DEBUG
fprintf(stderr, "%s: match including modifiers for keysym '%c' (%d), stroke %d (%s)\n", __func__, cur->keysym, cur->keysym, cur->stroke, nxagentSpecialKeystrokeNames[cur->stroke]);
#endif
return cur->stroke;
}
}
return ret;
}
/*
* returns True if a special keystroke has been pressed. *result will contain the action.
*/
Bool nxagentCheckSpecialKeystroke(XKeyEvent *X, enum HandleEventResult *result)
{
enum nxagentSpecialKeystroke stroke = find_keystroke(X);
*result = doNothing;
#ifdef TEST
if (stroke != KEYSTROKE_NOTHING && stroke != KEYSTROKE_END_MARKER)
fprintf(stderr, "nxagentCheckSpecialKeystroke: got code %x - state %x - stroke %d (%s)\n",
X -> keycode, X -> state, stroke, nxagentSpecialKeystrokeNames[stroke]);
else
fprintf(stderr, "nxagentCheckSpecialKeystroke: got code %x - state %x - stroke %d (unused)\n",
X -> keycode, X -> state, stroke);
#endif
if (stroke == KEYSTROKE_NOTHING)
return False;
/*
* Check special keys.
*/
/*
* FIXME: We should use the keysym instead that the keycode
* here.
*/
switch (stroke) {
#ifdef DEBUG_TREE
case KEYSTROKE_DEBUG_TREE:
*result = doDebugTree;
break;
#endif
case KEYSTROKE_CLOSE_SESSION:
*result = doCloseSession;
break;
case KEYSTROKE_SWITCH_ALL_SCREENS:
if (!nxagentOption(Rootless)) {
*result = doSwitchAllScreens;
}
break;
case KEYSTROKE_MINIMIZE:
if (!nxagentOption(Rootless)) {
*result = doMinimize;
}
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 True;
break;
case KEYSTROKE_FORCE_SYNCHRONIZATION:
nxagentForceSynchronization = True;
break;
#ifdef DUMP
case KEYSTROKE_REGIONS_ON_SCREEN:
nxagentRegionsOnScreen();
break;
#endif
#ifdef NX_DEBUG_INPUT
case KEYSTROKE_TEST_INPUT:
/*
* Used to test the input devices state.
*/
if (X -> type == KeyPress) {
if (!nxagentDebugInputDevices) {
fprintf(stderr, "Info: Turning input devices debug ON.\n");
nxagentDebugInputDevices = True;
} else {
fprintf(stderr, "Info: Turning input devices debug OFF.\n");
nxagentDebugInputDevices = False;
nxagentLastInputDevicesDumpTime = 0;
}
}
return True;
break;
case KEYSTROKE_DEACTIVATE_INPUT_DEVICES_GRAB:
if (X->type == KeyPress) {
nxagentDeactivateInputDevicesGrabs();
}
return True;
break;
#endif
case KEYSTROKE_FULLSCREEN:
if (!nxagentOption(Rootless)) {
*result = doSwitchFullscreen;
}
break;
case KEYSTROKE_RESIZE:
if (!nxagentOption(Rootless)) {
*result = doSwitchResizeMode;
}
break;
case KEYSTROKE_VIEWPORT_MOVE_LEFT:
if (!nxagentOption(Rootless) && !nxagentOption(DesktopResize)) {
*result = doViewportMoveLeft;
}
break;
case KEYSTROKE_VIEWPORT_MOVE_UP:
if (!nxagentOption(Rootless) && !nxagentOption(DesktopResize)) {
*result = doViewportMoveUp;
}
break;
case KEYSTROKE_VIEWPORT_MOVE_RIGHT:
if (!nxagentOption(Rootless) && !nxagentOption(DesktopResize)) {
*result = doViewportMoveRight;
}
break;
case KEYSTROKE_VIEWPORT_MOVE_DOWN:
if (!nxagentOption(Rootless) && !nxagentOption(DesktopResize)) {
*result = doViewportMoveDown;
}
break;
case KEYSTROKE_VIEWPORT_SCROLL_LEFT:
if (!nxagentOption(Rootless) && !nxagentOption(DesktopResize)) {
*result = doViewportLeft;
}
break;
case KEYSTROKE_VIEWPORT_SCROLL_UP:
if (!nxagentOption(Rootless) && !nxagentOption(DesktopResize)) {
*result = doViewportUp;
}
break;
case KEYSTROKE_VIEWPORT_SCROLL_RIGHT:
if (!nxagentOption(Rootless) && !nxagentOption(DesktopResize)) {
*result = doViewportRight;
}
break;
case KEYSTROKE_VIEWPORT_SCROLL_DOWN:
if (!nxagentOption(Rootless) && !nxagentOption(DesktopResize)) {
*result = doViewportDown;
}
break;
case KEYSTROKE_REREAD_KEYSTROKES:
/* two reasons to check on KeyRelease:
- this code is called for KeyPress and KeyRelease, so we
would read the keystroke file twice
- if the keystroke file changes settings for this key this
might lead to unexpected behaviour
*/
if (X->type == KeyRelease)
nxagentInitKeystrokes(True);
break;
case KEYSTROKE_AUTOGRAB:
*result = doAutoGrab;
break;
case KEYSTROKE_DUMP_CLIPBOARD:
*result = doDumpClipboard;
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);
}