diff --git a/doc/nxagent/README.keystrokes b/doc/nxagent/README.keystrokes index 750a460ebd..32974cbca6 100644 --- a/doc/nxagent/README.keystrokes +++ b/doc/nxagent/README.keystrokes @@ -126,6 +126,12 @@ reread_keystrokes Forces nxagent to re-read the keystroke configuration. Useful to add/change keystrokes to a running session. +autograb + Toggles autograb mode + +lockinput + Locks/unlocsk pointer within nxagent window + force_synchronization Forces immediate drawing of elements to be synchronized which can fix some visual bugs. diff --git a/etc/keystrokes.cfg b/etc/keystrokes.cfg index 8563698392..2cb242641d 100644 --- a/etc/keystrokes.cfg +++ b/etc/keystrokes.cfg @@ -24,4 +24,6 @@ + + diff --git a/nx-X11/programs/Xserver/hw/nxagent/Args.c b/nx-X11/programs/Xserver/hw/nxagent/Args.c index 161403cd0a..7ecd363402 100644 --- a/nx-X11/programs/Xserver/hw/nxagent/Args.c +++ b/nx-X11/programs/Xserver/hw/nxagent/Args.c @@ -1041,6 +1041,21 @@ int ddxProcessArgument(int argc, char *argv[], int i) return 0; } + if (!strcmp(argv[i], "-autograb")) + { + nxagentChangeOption(AutoGrab, True); + + return 1; + } + + if (!strcmp(argv[i], "-inputlock")) + { + nxagentChangeOption(InputLock, True); + + return 1; + } + + /* * Disable Xinerama (i.e. fake it in Screen.c) if somehow Xinerama support * has been disabled on the cmdline. @@ -1541,6 +1556,32 @@ static void nxagentParseSingleOption(char *name, char *value) return; } + else if (!strcmp(name, "autograb")) + { + if (!strcmp(value, "0")) + { + nxagentChangeOption(AutoGrab, False); + } + else + { + nxagentChangeOption(AutoGrab, True); + } + + return; + } + else if (!strcmp(name, "inputlock")) + { + if (!strcmp(value, "0")) + { + nxagentChangeOption(InputLock, False); + } + else + { + nxagentChangeOption(InputLock, True); + } + + return; + } else { #ifdef DEBUG @@ -2197,6 +2238,8 @@ void ddxUseMsg(void) ErrorF("-noignore don't ignore pointer and keyboard configuration changes mandated by clients\n"); ErrorF("-nokbreset don't reset keyboard device if the session is resumed\n"); ErrorF("-noxkblock always allow applications to change layout through XKEYBOARD\n"); + ErrorF("-autograb enable autograb\n"); + ErrorF("-inputlock enable inputlock\n"); ErrorF("-tile WxH size of image tiles (minimum allowed: 32x32)\n"); ErrorF("-keystrokefile file file with keyboard shortcut definitions\n"); ErrorF("-verbose print more warning and error messages\n"); diff --git a/nx-X11/programs/Xserver/hw/nxagent/Events.c b/nx-X11/programs/Xserver/hw/nxagent/Events.c index d8bf4df60e..608b0e12e2 100644 --- a/nx-X11/programs/Xserver/hw/nxagent/Events.c +++ b/nx-X11/programs/Xserver/hw/nxagent/Events.c @@ -78,6 +78,8 @@ #undef Atom #undef Time +#include + #ifdef NXAGENT_FIXKEYS #include "inputstr.h" #include "input.h" @@ -293,6 +295,36 @@ void ProcessInputEvents(void) mieqProcessInputEvents(); } +#ifdef DEBUG +char * nxagentGetNotifyMode(int mode) +{ + switch (mode) + { + case NotifyNormal: + { + return "NotifyNormal"; + break; + } + case NotifyGrab: + { + return "NotifyGrab"; + break; + } + case NotifyUngrab: + { + return "NotifyUngrab"; + break; + } + case NotifyWhileGrabbed: + { + return "NotifyWhileGrabbed"; + break; + } + } + return "Unknown"; +} +#endif + #ifdef DEBUG_TREE /* @@ -678,6 +710,157 @@ static void nxagentSwitchDeferMode(void) } } +enum {WINDOWSUFFIX_AUTOGRAB = 0, WINDOWSUFFIX_INPUTLOCK, WINDOWSUFFIX_MAX}; + +static char * nxagentWindowSuffixes[WINDOWSUFFIX_MAX] = {NULL}; + +void setWinNameSuffix(int index, const char *suffix) +{ + if (index < 0 || index >= WINDOWSUFFIX_MAX) + return; + + if (nxagentWindowSuffixes[index]) + { + free(nxagentWindowSuffixes[index]); + nxagentWindowSuffixes[index] = NULL; + } + + if (suffix) + nxagentWindowSuffixes[index] = strdup(suffix); +} + +void updateWinName() +{ + static const char * pre = " ("; + static const char * post = ")"; + static const char * comma = ", "; + char * newname; + + int len = strlen(nxagentWindowName) + 1; + int count = 0; + for (int i = 0; i < WINDOWSUFFIX_MAX; i++) + { + if (nxagentWindowSuffixes[i]) + { + if (count) + len += strlen(comma); + len += strlen(nxagentWindowSuffixes[i]); + count++; + } + } + + if (count) + len += strlen(pre) + strlen(post); + + if (!(newname = calloc(len, sizeof(char)))) + { + fprintf(stderr, "%s: malloc failed", __func__); + return; + } + + strncpy(newname, nxagentWindowName, len); + + if (count) + strncat(newname, pre, len); + + int first = True; + for (int i = 0; i < WINDOWSUFFIX_MAX; i++) + { + if (nxagentWindowSuffixes[i]) + { + if (!first) + strncat(newname, comma, len); + strncat(newname, nxagentWindowSuffixes[i], len); + first = False; + } + } + + if (count) + strncat(newname, post, len); + + XStoreName(nxagentDisplay, nxagentDefaultWindows[0], newname); + free(newname); +} + +static void nxagentEnableAutoGrab(void) +{ +#ifdef DEBUG + fprintf(stderr, "enabling autograb\n"); +#endif + + nxagentGrabPointerAndKeyboard(NULL); + setWinNameSuffix(WINDOWSUFFIX_AUTOGRAB, "autograb on"); + updateWinName(); + nxagentChangeOption(AutoGrab, True); +} + +static void nxagentDisableAutoGrab(void) +{ +#ifdef DEBUG + fprintf(stderr, "disabling autograb\n"); +#endif + + nxagentUngrabPointerAndKeyboard(NULL); + setWinNameSuffix(WINDOWSUFFIX_AUTOGRAB, NULL); + updateWinName(); + nxagentChangeOption(AutoGrab, False); +} + +static void nxagentToggleAutoGrab(void) +{ + /* autograb only works in windowed mode */ + if (nxagentOption(Rootless) || nxagentOption(Fullscreen)) + return; + + if (!nxagentOption(InputLock)) + { + if (!nxagentOption(AutoGrab)) + nxagentEnableAutoGrab(); + else + nxagentDisableAutoGrab(); + } +} + +static void nxagentEnableInputlock(void) +{ +#ifdef DEBUG + fprintf(stderr, "activating inputlock\n"); +#endif + setWinNameSuffix(WINDOWSUFFIX_INPUTLOCK, "inputlock on"); + updateWinName(); + XGrabPointer(nxagentDisplay,nxagentDefaultWindows[0], True, + ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask | EnterWindowMask | LeaveWindowMask, + GrabModeAsync, GrabModeAsync, nxagentDefaultWindows[0], None, CurrentTime); + nxagentChangeOption(InputLock, True); +} + +static void nxagentDisableInputlock(void) +{ +#ifdef DEBUG + fprintf(stderr, "deactivating inputlock\n"); +#endif + nxagentUngrabPointerAndKeyboard(NULL); + /* XTextProperty name = { + .value = (unsigned char *)nxagentWindowName, + .encoding = XA_STRING, + .format = 8, + .nitems = strlen((char *) name.value) + }; + */ + setWinNameSuffix(WINDOWSUFFIX_INPUTLOCK, NULL); + updateWinName(); + nxagentChangeOption(InputLock, False); +} + +/* TODO: drop inputlock when switching to Fullscreen */ +static void nxagentToggleInputLock(void) +{ + if (!nxagentOption(InputLock)) + nxagentEnableInputlock(); + else + nxagentDisableInputlock(); +} + static Bool nxagentExposurePredicate(Display *display, XEvent *event, XPointer window) { /* @@ -1064,6 +1247,18 @@ void nxagentDispatchEvents(PredicateFuncPtr predicate) break; } + case doAutoGrab: + { + nxagentToggleAutoGrab(); + + break; + } + case doInputLock: + { + nxagentToggleInputLock(); + + break; + } default: { FatalError("nxagentDispatchEvent: handleKeyPress returned unknown value\n"); @@ -1481,8 +1676,18 @@ FIXME: Don't enqueue the KeyRelease event if the key was { WindowPtr pWin; - #ifdef TEST - fprintf(stderr, "nxagentDispatchEvents: Going to handle new FocusIn event.\n"); + #ifdef DEBUG + fprintf(stderr, "%s: Going to handle new FocusIn event [0x%x] mode: [%s]\n", __func__, X.xfocus.window, nxagentGetNotifyMode(X.xfocus.mode)); + { + XlibWindow w; + int revert_to; + XGetInputFocus(nxagentDisplay, &w, &revert_to); + fprintf(stderr, "%s: (FocusIn): Event win [0x%x] Focus owner [0x%x] nxagentDefaultWindows[0] [0x%x]\n", __func__, X.xfocus.window, w, nxagentDefaultWindows[0]); + } + #else + #ifdef TEST + fprintf(stderr, "%s: Going to handle new FocusIn event\n", __func__); + #endif #endif /* @@ -1508,12 +1713,34 @@ FIXME: Don't enqueue the KeyRelease event if the key was } } + if (nxagentOption(AutoGrab) && !(nxagentOption(AllScreens) || nxagentOption(Fullscreen) || nxagentOption(Rootless))) + { + if (X.xfocus.window == nxagentDefaultWindows[0] && X.xfocus.mode == NotifyNormal) + { + #ifdef DEBUG + fprintf(stderr, "%s: (FocusIn): grabbing\n", __func__); + #endif + nxagentGrabPointerAndKeyboard(NULL); + } + /* else + { + #ifdef DEBUG + fprintf(stderr, "%s: (FocusIn): ungrabbing\n", __func__); + #endif + nxagentUngrabPointerAndKeyboard(NULL); + } + */ + } break; } case FocusOut: { - #ifdef TEST - fprintf(stderr, "nxagentDispatchEvents: Going to handle new FocusOut event.\n"); + #ifdef DEBUG + fprintf(stderr, "%s: Going to handle new FocusOut event [0x%x] mode: [%s]\n", __func__, X.xfocus.window, nxagentGetNotifyMode(X.xfocus.mode)); + #else + #ifdef TEST + fprintf(stderr, "%s: Going to handle new FocusOut event.\n", __func__); + #endif #endif if (X.xfocus.detail != NotifyInferior) @@ -1586,6 +1813,19 @@ FIXME: Don't enqueue the KeyRelease event if the key was #endif /* NXAGENT_FIXKEYS */ + if (nxagentOption(AutoGrab)) + { + XlibWindow w; + int revert_to; + XGetInputFocus(nxagentDisplay, &w, &revert_to); + if (w != nxagentDefaultWindows[0] && X.xfocus.mode == NotifyWhileGrabbed) + { + #ifdef DEBUG + fprintf(stderr, "%s: (FocusOut): ungrabbing\n", __func__); + #endif + nxagentUngrabPointerAndKeyboard(NULL); + } + } break; } case KeymapNotify: @@ -1721,11 +1961,14 @@ FIXME: Don't enqueue the KeyRelease event if the key was nxagentLastEnteredWindow = NULL; } - if (X.xcrossing.window == nxagentDefaultWindows[0] && - X.xcrossing.detail != NotifyInferior && - X.xcrossing.mode == NotifyNormal) + if (!nxagentOption(AutoGrab)) { - nxagentUngrabPointerAndKeyboard(&X); + if (X.xcrossing.window == nxagentDefaultWindows[0] && + X.xcrossing.detail != NotifyInferior && + X.xcrossing.mode == NotifyNormal) + { + nxagentUngrabPointerAndKeyboard(&X); + } } if (X.xcrossing.detail != NotifyInferior) @@ -3844,13 +4087,26 @@ void nxagentGrabPointerAndKeyboard(XEvent *X) fprintf(stderr, "nxagentGrabPointerAndKeyboard: Going to grab the keyboard in context [B1].\n"); #endif - result = XGrabKeyboard(nxagentDisplay, nxagentFullscreenWindow, - True, GrabModeAsync, GrabModeAsync, now); + if (nxagentFullscreenWindow) + result = XGrabKeyboard(nxagentDisplay, nxagentFullscreenWindow, + True, GrabModeAsync, GrabModeAsync, now); + else + result = XGrabKeyboard(nxagentDisplay, RootWindow(nxagentDisplay, DefaultScreen(nxagentDisplay)), + True, GrabModeAsync, GrabModeAsync, now); if (result != GrabSuccess) { + #ifdef DEBUG + fprintf(stderr, "%s: keyboard grab failed.\n", __func__); + #endif return; } + #ifdef DEBUG + else + { + fprintf(stderr, "%s: keyboard grab successful.\n", __func__); + } + #endif /* * The smart scheduler could be stopped while @@ -3868,7 +4124,8 @@ void nxagentGrabPointerAndKeyboard(XEvent *X) resource = nxagentWaitForResource(NXGetCollectGrabPointerResource, nxagentCollectGrabPointerPredicate); - NXCollectGrabPointer(nxagentDisplay, resource, + if (nxagentFullscreenWindow) + NXCollectGrabPointer(nxagentDisplay, resource, nxagentFullscreenWindow, True, NXAGENT_POINTER_EVENT_MASK, GrabModeAsync, GrabModeAsync, None, None, now); diff --git a/nx-X11/programs/Xserver/hw/nxagent/Events.h b/nx-X11/programs/Xserver/hw/nxagent/Events.h index 5df0e1f05c..3c5623ca16 100644 --- a/nx-X11/programs/Xserver/hw/nxagent/Events.h +++ b/nx-X11/programs/Xserver/hw/nxagent/Events.h @@ -50,7 +50,9 @@ enum HandleEventResult doViewportRight, doViewportDown, doSwitchResizeMode, - doSwitchDeferMode + doSwitchDeferMode, + doAutoGrab, + doInputLock }; extern CARD32 nxagentLastEventTime; diff --git a/nx-X11/programs/Xserver/hw/nxagent/Keystroke.c b/nx-X11/programs/Xserver/hw/nxagent/Keystroke.c index 662da6b04d..19f829c8e0 100644 --- a/nx-X11/programs/Xserver/hw/nxagent/Keystroke.c +++ b/nx-X11/programs/Xserver/hw/nxagent/Keystroke.c @@ -99,6 +99,10 @@ char * nxagentSpecialKeystrokeNames[] = { "viewport_scroll_down", "reread_keystrokes", + + "autograb", + "inputlock", + NULL, }; @@ -138,6 +142,8 @@ struct nxagentSpecialKeystrokeMap default_map[] = { {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_INPUTLOCK, ControlMask, True, XK_c}, {KEYSTROKE_END_MARKER, 0, False, NoSymbol}, }; struct nxagentSpecialKeystrokeMap *map = default_map; @@ -467,7 +473,7 @@ static enum nxagentSpecialKeystroke find_keystroke(XKeyEvent *X) #endif for (struct nxagentSpecialKeystrokeMap *cur = map; cur->stroke != KEYSTROKE_END_MARKER; cur++) { #ifdef DEBUG - fprintf(stderr, "%s: checking keysym '%c' (%d)\n", __func__, cur->keysym, cur->keysym); + 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 @@ -624,6 +630,12 @@ Bool nxagentCheckSpecialKeystroke(XKeyEvent *X, enum HandleEventResult *result) if (X->type == KeyRelease) nxagentInitKeystrokes(True); break; + case KEYSTROKE_AUTOGRAB: + *result = doAutoGrab; + break; + case KEYSTROKE_INPUTLOCK: + *result = doInputLock; + 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: diff --git a/nx-X11/programs/Xserver/hw/nxagent/Keystroke.h b/nx-X11/programs/Xserver/hw/nxagent/Keystroke.h index 13a83d0fea..73d29bbda2 100644 --- a/nx-X11/programs/Xserver/hw/nxagent/Keystroke.h +++ b/nx-X11/programs/Xserver/hw/nxagent/Keystroke.h @@ -73,6 +73,9 @@ enum nxagentSpecialKeystroke { KEYSTROKE_REREAD_KEYSTROKES, + KEYSTROKE_AUTOGRAB, + KEYSTROKE_INPUTLOCK, + KEYSTROKE_NOTHING, /* insert more here and in the string translation */ diff --git a/nx-X11/programs/Xserver/hw/nxagent/Options.c b/nx-X11/programs/Xserver/hw/nxagent/Options.c index c06967c76e..ad229e042f 100644 --- a/nx-X11/programs/Xserver/hw/nxagent/Options.c +++ b/nx-X11/programs/Xserver/hw/nxagent/Options.c @@ -172,6 +172,10 @@ void nxagentInitOptions(void) nxagentOptions.ReconnectTolerance = DEFAULT_TOLERANCE; nxagentOptions.KeycodeConversion = DEFAULT_KEYCODE_CONVERSION; + + nxagentOptions.AutoGrab = False; + + nxagentOptions.InputLock = False; } /* diff --git a/nx-X11/programs/Xserver/hw/nxagent/Options.h b/nx-X11/programs/Xserver/hw/nxagent/Options.h index b5394ee016..764a28b82d 100644 --- a/nx-X11/programs/Xserver/hw/nxagent/Options.h +++ b/nx-X11/programs/Xserver/hw/nxagent/Options.h @@ -450,6 +450,18 @@ typedef struct _AgentOptions * Convert evdev keycodes to pc105. */ KeycodeConversionMode KeycodeConversion; + + /* + * True if agent should grab the input in windowed mode whenever the + * agent window gets the focus + */ + int AutoGrab; /* Should be Bool but I do not want to include Xlib.h here */ + + /* + * True if the pointer is locked within the agent window (in windowed mode) + */ + int InputLock; /* Should be Bool but I do not want to include Xlib.h here */ + } AgentOptionsRec; typedef AgentOptionsRec *AgentOptionsPtr; diff --git a/nx-X11/programs/Xserver/hw/nxagent/man/nxagent.1 b/nx-X11/programs/Xserver/hw/nxagent/man/nxagent.1 index 6804032a22..2ee5c1bfa0 100644 --- a/nx-X11/programs/Xserver/hw/nxagent/man/nxagent.1 +++ b/nx-X11/programs/Xserver/hw/nxagent/man/nxagent.1 @@ -751,6 +751,13 @@ format must be included in both. This is potentially unsafe. means that all of these checks are essentially deactivated. This is a very bad idea. .RE +.TP 8 +.B autograb= +enable or disable autograb (default: disabled) +.TP 8 +.B inputlock= +enable or disable inputlock (default: disabled) +.RE If you want to use \fBnxagent\fR as a replacement for Xnest or Xephyr you can pass options like this: