diff --git a/hw/kdrive/ephyr/ephyr.c b/hw/kdrive/ephyr/ephyr.c index 668322f8c..d94eca8b6 100644 --- a/hw/kdrive/ephyr/ephyr.c +++ b/hw/kdrive/ephyr/ephyr.c @@ -61,7 +61,12 @@ typedef struct _EphyrInputPrivate { Bool EphyrWantGrayScale = 0; Bool EphyrWantResize = 0; -Bool EphyrWantNoHostGrab = 0; + +static xcb_mod_mask_t EphyrKeybindToggleHostGrabModMask; +static uint32_t EphyrKeybindToggleHostGrabKey; +static char const* EphyrTitleHostGrabKeyComboHint; +static uint8_t EphyrTitleHostGrabKeyComboHintLen; +static Bool EphyrHostGrabSet = FALSE; Bool ephyrInitialize(KdCardInfo * card, EphyrPriv * priv) @@ -649,6 +654,114 @@ ephyrCreateColormap(ColormapPtr pmap) return fbInitializeColormap(pmap); } +Bool +ephyrSetGrabShortcut(char const* const desc) +{ + if (desc == NULL || !strcmp(desc, "NULL")) { + EphyrKeybindToggleHostGrabModMask = 0; + EphyrKeybindToggleHostGrabKey = 0; + EphyrTitleHostGrabKeyComboHint = NULL; + EphyrTitleHostGrabKeyComboHintLen = 0; + } + else { + const uint8_t fixed_bound = 255; + char buf[16]; + uint8_t j = 0; + for (uint8_t i = 0;; ++i) { + assert(i < fixed_bound); + char const c = desc[i]; + if (c == 0 || (j != 0 && c == '+')) { + buf[j] = 0; + if (j == 1) { + EphyrKeybindToggleHostGrabKey = buf[0]; + } + else if (!strcmp(buf, "ctrl")) { + EphyrKeybindToggleHostGrabModMask |= XCB_MOD_MASK_CONTROL; + } + else if (!strcmp(buf, "shift")) { + EphyrKeybindToggleHostGrabModMask |= XCB_MOD_MASK_SHIFT; + } + else if (!strcmp(buf, "lock")) { + EphyrKeybindToggleHostGrabModMask |= XCB_MOD_MASK_LOCK; + } + else if (!strcmp(buf, "mod1")) { + EphyrKeybindToggleHostGrabModMask |= XCB_MOD_MASK_1; + } + else if (!strcmp(buf, "mod2")) { + EphyrKeybindToggleHostGrabModMask |= XCB_MOD_MASK_2; + } + else if (!strcmp(buf, "mod3")) { + EphyrKeybindToggleHostGrabModMask |= XCB_MOD_MASK_3; + } + else if (!strcmp(buf, "mod4")) { + EphyrKeybindToggleHostGrabModMask |= XCB_MOD_MASK_4; + } + else if (!strcmp(buf, "mod5")) { + EphyrKeybindToggleHostGrabModMask |= XCB_MOD_MASK_5; + } + else { + ErrorF("ephyr: -host-grab: " + "Unrecognized key: '%s'\n", buf); + return FALSE; + } + if (c == 0) break; + j = 0; + } + else { + buf[j] = c; + ++j; + assert(j < sizeof(buf)); + } + } + + EphyrTitleHostGrabKeyComboHint = desc; + EphyrTitleHostGrabKeyComboHintLen = strlen(desc); + } + + EphyrHostGrabSet = TRUE; + return TRUE; +} + +static void +ephyrPrintGrabShortcut(char* const out, size_t const out_size, + Bool const currently_grabbed) +{ + if ( + ( + EphyrKeybindToggleHostGrabModMask == 0 && + EphyrKeybindToggleHostGrabKey == 0 + ) || ( + EphyrTitleHostGrabKeyComboHint == 0 || + EphyrTitleHostGrabKeyComboHint == 0 + ) + ) { + /* grabbing disabled */ + out[0] = '\0'; + return; + } + + char const* const suffix = currently_grabbed + ? " releases mouse and keyboard)" + : " grabs mouse and keyboard)"; + size_t const suffix_len = strlen(suffix); + + assert(out_size > 1 + EphyrTitleHostGrabKeyComboHintLen + suffix_len + 1); + assert(out != NULL); + + out[0] = '('; + memcpy(out + 1, EphyrTitleHostGrabKeyComboHint, EphyrTitleHostGrabKeyComboHintLen); + + memcpy(out + EphyrTitleHostGrabKeyComboHintLen + 1, suffix, suffix_len + 1); +} + +static void +ephyrUpdateWindowTitle(KdScreenInfo* const screen, Bool const currently_grabbed) +{ + char title_buf[128]; + ephyrPrintGrabShortcut(title_buf, sizeof(title_buf), currently_grabbed); + hostx_set_win_title(screen, title_buf); +} + Bool ephyrInitScreen(ScreenPtr pScreen) { @@ -657,11 +770,10 @@ ephyrInitScreen(ScreenPtr pScreen) EPHYR_LOG("pScreen->myNum:%d\n", pScreen->myNum); hostx_set_screen_number(screen, pScreen->myNum); - if (EphyrWantNoHostGrab) { - hostx_set_win_title(screen, "xephyr"); - } else { - hostx_set_win_title(screen, "(ctrl+shift grabs mouse and keyboard)"); + if (!EphyrHostGrabSet) { + ephyrSetGrabShortcut("ctrl+shift"); } + ephyrUpdateWindowTitle(screen, FALSE); pScreen->CreateColormap = ephyrCreateColormap; #ifdef XV @@ -1019,71 +1131,78 @@ ephyrProcessKeyPress(xcb_generic_event_t *xev) static void ephyrProcessKeyRelease(xcb_generic_event_t *xev) { - xcb_connection_t *conn = hostx_get_xcbconn(); xcb_key_release_event_t *key = (xcb_key_release_event_t *)xev; - static xcb_key_symbols_t *keysyms; - static int grabbed_screen = -1; - int mod1_down = ephyrUpdateGrabModifierState(key->state); + if (EphyrKeybindToggleHostGrabModMask != 0 || + EphyrKeybindToggleHostGrabKey != 0) { - if (!keysyms) - keysyms = xcb_key_symbols_alloc(conn); + xcb_connection_t *conn = hostx_get_xcbconn(); + static xcb_key_symbols_t *keysyms; + static int grabbed_screen = -1; - if (!EphyrWantNoHostGrab && - (((xcb_key_symbols_get_keysym(keysyms, key->detail, 0) == XK_Shift_L - || xcb_key_symbols_get_keysym(keysyms, key->detail, 0) == XK_Shift_R) - && (key->state & XCB_MOD_MASK_CONTROL)) || - ((xcb_key_symbols_get_keysym(keysyms, key->detail, 0) == XK_Control_L - || xcb_key_symbols_get_keysym(keysyms, key->detail, 0) == XK_Control_R) - && (key->state & XCB_MOD_MASK_SHIFT)))) { - KdScreenInfo *screen = screen_from_window(key->event); - assert(screen); - EphyrScrPriv *scrpriv = screen->driver; + if (!keysyms) + keysyms = xcb_key_symbols_alloc(conn); - if (grabbed_screen != -1) { - xcb_ungrab_keyboard(conn, XCB_TIME_CURRENT_TIME); - xcb_ungrab_pointer(conn, XCB_TIME_CURRENT_TIME); - grabbed_screen = -1; - hostx_set_win_title(screen, - "(ctrl+shift grabs mouse and keyboard)"); - } - else if (!mod1_down) { - /* Attempt grab */ - xcb_grab_keyboard_cookie_t kbgrabc = - xcb_grab_keyboard(conn, - TRUE, - scrpriv->win, - XCB_TIME_CURRENT_TIME, - XCB_GRAB_MODE_ASYNC, - XCB_GRAB_MODE_ASYNC); - xcb_grab_keyboard_reply_t *kbgrabr; - xcb_grab_pointer_cookie_t pgrabc = - xcb_grab_pointer(conn, - TRUE, - scrpriv->win, - 0, - XCB_GRAB_MODE_ASYNC, - XCB_GRAB_MODE_ASYNC, - scrpriv->win, - XCB_NONE, - XCB_TIME_CURRENT_TIME); - xcb_grab_pointer_reply_t *pgrabr; - kbgrabr = xcb_grab_keyboard_reply(conn, kbgrabc, NULL); - if (!kbgrabr || kbgrabr->status != XCB_GRAB_STATUS_SUCCESS) { - xcb_discard_reply(conn, pgrabc.sequence); + int const keysym = + xcb_key_symbols_get_keysym(keysyms, key->detail, 0); + + if ( + ( + (key->state & EphyrKeybindToggleHostGrabModMask) == + EphyrKeybindToggleHostGrabModMask + ) && ( + /* NOTE: mod-key keysyms are > 0xfe00. We do this so when the + shortcut is only mod-keys (e.g. ctrl+shift) and the user + releases any other key, input doesn't get grabbed */ + (EphyrKeybindToggleHostGrabKey == 0 && keysym > 0xfe00) || + keysym == EphyrKeybindToggleHostGrabKey + ) + ) { + KdScreenInfo *screen = screen_from_window(key->event); + assert(screen); + EphyrScrPriv *scrpriv = screen->driver; + + if (grabbed_screen != -1) { + xcb_ungrab_keyboard(conn, XCB_TIME_CURRENT_TIME); xcb_ungrab_pointer(conn, XCB_TIME_CURRENT_TIME); - } else { - pgrabr = xcb_grab_pointer_reply(conn, pgrabc, NULL); - if (!pgrabr || pgrabr->status != XCB_GRAB_STATUS_SUCCESS) - { - xcb_ungrab_keyboard(conn, - XCB_TIME_CURRENT_TIME); - } else { - grabbed_screen = scrpriv->mynum; - hostx_set_win_title - (screen, - "(ctrl+shift releases mouse and keyboard)"); + grabbed_screen = -1; + } + else { + /* Attempt grab */ + xcb_grab_keyboard_cookie_t kbgrabc = + xcb_grab_keyboard(conn, + TRUE, + scrpriv->win, + XCB_TIME_CURRENT_TIME, + XCB_GRAB_MODE_ASYNC, + XCB_GRAB_MODE_ASYNC); + xcb_grab_keyboard_reply_t *kbgrabr; + xcb_grab_pointer_cookie_t pgrabc = + xcb_grab_pointer(conn, + TRUE, + scrpriv->win, + 0, + XCB_GRAB_MODE_ASYNC, + XCB_GRAB_MODE_ASYNC, + scrpriv->win, + XCB_NONE, + XCB_TIME_CURRENT_TIME); + xcb_grab_pointer_reply_t *pgrabr; + kbgrabr = xcb_grab_keyboard_reply(conn, kbgrabc, NULL); + if (!kbgrabr || kbgrabr->status != XCB_GRAB_STATUS_SUCCESS) { + xcb_discard_reply(conn, pgrabc.sequence); + xcb_ungrab_pointer(conn, XCB_TIME_CURRENT_TIME); + } else { + pgrabr = xcb_grab_pointer_reply(conn, pgrabc, NULL); + if (!pgrabr || pgrabr->status != XCB_GRAB_STATUS_SUCCESS) + { + xcb_ungrab_keyboard(conn, + XCB_TIME_CURRENT_TIME); + } else { + grabbed_screen = scrpriv->mynum; + } } } + ephyrUpdateWindowTitle(screen, grabbed_screen != -1); } } diff --git a/hw/kdrive/ephyr/ephyr.h b/hw/kdrive/ephyr/ephyr.h index 8833de8a9..3633de7ca 100644 --- a/hw/kdrive/ephyr/ephyr.h +++ b/hw/kdrive/ephyr/ephyr.h @@ -167,6 +167,14 @@ Bool Bool ephyrCreateColormap(ColormapPtr pmap); +/** + * @param desc examples: "ctrl+shift", "ctrl+mod1+a", "a", + * NULL (disables host grab) + * @return TRUE if success, otherwise FALSE + */ +Bool + ephyrSetGrabShortcut(char const* const desc); + #ifdef RANDR Bool ephyrRandRGetInfo(ScreenPtr pScreen, Rotation * rotations); diff --git a/hw/kdrive/ephyr/ephyrinit.c b/hw/kdrive/ephyr/ephyrinit.c index 767fccd0f..ff0d5d0cc 100644 --- a/hw/kdrive/ephyr/ephyrinit.c +++ b/hw/kdrive/ephyr/ephyrinit.c @@ -38,7 +38,6 @@ extern Window EphyrPreExistingHostWin; extern Bool EphyrWantGrayScale; extern Bool EphyrWantResize; -extern Bool EphyrWantNoHostGrab; extern Bool kdHasPointer; extern Bool kdHasKbd; extern Bool ephyr_glamor, ephyr_glamor_gles2, ephyr_glamor_skip_present; @@ -144,6 +143,8 @@ ddxUseMsg(void) ErrorF ("-title [title] set the window title in the WM_NAME property\n"); ErrorF("-no-host-grab Disable grabbing the keyboard and mouse.\n"); + ErrorF + ("-host-grab [keys] set shortcut to grab the keyboard and mouse (default: ctrl+shift)\n"); ErrorF("\n"); } @@ -343,9 +344,21 @@ ddxProcessArgument(int argc, char **argv, int i) } /* end Xnest compat */ else if (!strcmp(argv[i], "-no-host-grab")) { - EphyrWantNoHostGrab = 1; + ephyrSetGrabShortcut(NULL); return 1; } + else if (!strcmp(argv[i], "-host-grab")) { + if (i + 1 >= argc) { + ErrorF( + "ephyr: -host-grab requires an argument e.g. ctrl+shift+x\n"); + exit(1); + } + else if (!ephyrSetGrabShortcut(argv[i + 1])) { + /* specific error message is printed in ephyrSetGrabShortcut */ + exit(1); + } + return 2; + } else if (!strcmp(argv[i], "-sharevts") || !strcmp(argv[i], "-novtswitch")) { return 1; diff --git a/hw/kdrive/ephyr/man/Xephyr.man b/hw/kdrive/ephyr/man/Xephyr.man index 765e43f2d..d9c7b19c0 100644 --- a/hw/kdrive/ephyr/man/Xephyr.man +++ b/hw/kdrive/ephyr/man/Xephyr.man @@ -67,6 +67,31 @@ window. By default, the Xephyr window has a fixed size. .TP 8 .B \-no\-host\-grab Disable grabbing the keyboard and mouse. +.TP 8 +.BI \-host\-grab " keys" +Set the keyboard shortcut for Xephyr to grab keyboard and mouse +input. Possible values for mod-keys are: ctrl, shift, lock, +mod1, mod2, mod3, mod4, mod5. Up to one ascii character (lower-case) can +be used by itself or in conjunction with mod-keys. Keys are concatenated +with +.I +\fP. If omitted, defaults to +.I ctrl+shift\fP. + +Examples: +.RS +.IP \[bu] 2 +.I ctrl+mod1 +.IP \[bu] 2 +.I ctrl+shift++ +(note that the +.I + +at the end is interpreted as the ascii character '+') +.IP \[bu] 2 +.I a +(mod-keys are optional, this will grab/release whenever the +.I a +key is pressed) +.RE .SH "SIGNALS" Send a SIGUSR1 to the server (e.g. pkill \-USR1 Xephyr) to toggle the debugging mode.