ephyr: Add -host-grab to set custom grab shortcut
Allows for calling Xephyr with `-host-grab [keys]` to customize the keyboard shortcut for grabbing/releasing keyboard and mouse input. Fully backwards compatible: Omitting `-host-grab` defaults to ctrl+shift. `-no-host-grab` acts the same as before. Closes: #134 Signed-off-by: Steven Van Dorp <steven@vandorp.lu>
This commit is contained in:
parent
61accf16e2
commit
b393d5fc02
|
@ -61,7 +61,12 @@ typedef struct _EphyrInputPrivate {
|
||||||
|
|
||||||
Bool EphyrWantGrayScale = 0;
|
Bool EphyrWantGrayScale = 0;
|
||||||
Bool EphyrWantResize = 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
|
Bool
|
||||||
ephyrInitialize(KdCardInfo * card, EphyrPriv * priv)
|
ephyrInitialize(KdCardInfo * card, EphyrPriv * priv)
|
||||||
|
@ -649,6 +654,114 @@ ephyrCreateColormap(ColormapPtr pmap)
|
||||||
return fbInitializeColormap(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
|
Bool
|
||||||
ephyrInitScreen(ScreenPtr pScreen)
|
ephyrInitScreen(ScreenPtr pScreen)
|
||||||
{
|
{
|
||||||
|
@ -657,11 +770,10 @@ ephyrInitScreen(ScreenPtr pScreen)
|
||||||
|
|
||||||
EPHYR_LOG("pScreen->myNum:%d\n", pScreen->myNum);
|
EPHYR_LOG("pScreen->myNum:%d\n", pScreen->myNum);
|
||||||
hostx_set_screen_number(screen, pScreen->myNum);
|
hostx_set_screen_number(screen, pScreen->myNum);
|
||||||
if (EphyrWantNoHostGrab) {
|
if (!EphyrHostGrabSet) {
|
||||||
hostx_set_win_title(screen, "xephyr");
|
ephyrSetGrabShortcut("ctrl+shift");
|
||||||
} else {
|
|
||||||
hostx_set_win_title(screen, "(ctrl+shift grabs mouse and keyboard)");
|
|
||||||
}
|
}
|
||||||
|
ephyrUpdateWindowTitle(screen, FALSE);
|
||||||
pScreen->CreateColormap = ephyrCreateColormap;
|
pScreen->CreateColormap = ephyrCreateColormap;
|
||||||
|
|
||||||
#ifdef XV
|
#ifdef XV
|
||||||
|
@ -1019,71 +1131,78 @@ ephyrProcessKeyPress(xcb_generic_event_t *xev)
|
||||||
static void
|
static void
|
||||||
ephyrProcessKeyRelease(xcb_generic_event_t *xev)
|
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;
|
xcb_key_release_event_t *key = (xcb_key_release_event_t *)xev;
|
||||||
static xcb_key_symbols_t *keysyms;
|
if (EphyrKeybindToggleHostGrabModMask != 0 ||
|
||||||
static int grabbed_screen = -1;
|
EphyrKeybindToggleHostGrabKey != 0) {
|
||||||
int mod1_down = ephyrUpdateGrabModifierState(key->state);
|
|
||||||
|
|
||||||
if (!keysyms)
|
xcb_connection_t *conn = hostx_get_xcbconn();
|
||||||
keysyms = xcb_key_symbols_alloc(conn);
|
static xcb_key_symbols_t *keysyms;
|
||||||
|
static int grabbed_screen = -1;
|
||||||
|
|
||||||
if (!EphyrWantNoHostGrab &&
|
if (!keysyms)
|
||||||
(((xcb_key_symbols_get_keysym(keysyms, key->detail, 0) == XK_Shift_L
|
keysyms = xcb_key_symbols_alloc(conn);
|
||||||
|| 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 (grabbed_screen != -1) {
|
int const keysym =
|
||||||
xcb_ungrab_keyboard(conn, XCB_TIME_CURRENT_TIME);
|
xcb_key_symbols_get_keysym(keysyms, key->detail, 0);
|
||||||
xcb_ungrab_pointer(conn, XCB_TIME_CURRENT_TIME);
|
|
||||||
grabbed_screen = -1;
|
if (
|
||||||
hostx_set_win_title(screen,
|
(
|
||||||
"(ctrl+shift grabs mouse and keyboard)");
|
(key->state & EphyrKeybindToggleHostGrabModMask) ==
|
||||||
}
|
EphyrKeybindToggleHostGrabModMask
|
||||||
else if (!mod1_down) {
|
) && (
|
||||||
/* Attempt grab */
|
/* NOTE: mod-key keysyms are > 0xfe00. We do this so when the
|
||||||
xcb_grab_keyboard_cookie_t kbgrabc =
|
shortcut is only mod-keys (e.g. ctrl+shift) and the user
|
||||||
xcb_grab_keyboard(conn,
|
releases any other key, input doesn't get grabbed */
|
||||||
TRUE,
|
(EphyrKeybindToggleHostGrabKey == 0 && keysym > 0xfe00) ||
|
||||||
scrpriv->win,
|
keysym == EphyrKeybindToggleHostGrabKey
|
||||||
XCB_TIME_CURRENT_TIME,
|
)
|
||||||
XCB_GRAB_MODE_ASYNC,
|
) {
|
||||||
XCB_GRAB_MODE_ASYNC);
|
KdScreenInfo *screen = screen_from_window(key->event);
|
||||||
xcb_grab_keyboard_reply_t *kbgrabr;
|
assert(screen);
|
||||||
xcb_grab_pointer_cookie_t pgrabc =
|
EphyrScrPriv *scrpriv = screen->driver;
|
||||||
xcb_grab_pointer(conn,
|
|
||||||
TRUE,
|
if (grabbed_screen != -1) {
|
||||||
scrpriv->win,
|
xcb_ungrab_keyboard(conn, XCB_TIME_CURRENT_TIME);
|
||||||
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);
|
xcb_ungrab_pointer(conn, XCB_TIME_CURRENT_TIME);
|
||||||
} else {
|
grabbed_screen = -1;
|
||||||
pgrabr = xcb_grab_pointer_reply(conn, pgrabc, NULL);
|
}
|
||||||
if (!pgrabr || pgrabr->status != XCB_GRAB_STATUS_SUCCESS)
|
else {
|
||||||
{
|
/* Attempt grab */
|
||||||
xcb_ungrab_keyboard(conn,
|
xcb_grab_keyboard_cookie_t kbgrabc =
|
||||||
XCB_TIME_CURRENT_TIME);
|
xcb_grab_keyboard(conn,
|
||||||
} else {
|
TRUE,
|
||||||
grabbed_screen = scrpriv->mynum;
|
scrpriv->win,
|
||||||
hostx_set_win_title
|
XCB_TIME_CURRENT_TIME,
|
||||||
(screen,
|
XCB_GRAB_MODE_ASYNC,
|
||||||
"(ctrl+shift releases mouse and keyboard)");
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -167,6 +167,14 @@ Bool
|
||||||
Bool
|
Bool
|
||||||
ephyrCreateColormap(ColormapPtr pmap);
|
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
|
#ifdef RANDR
|
||||||
Bool
|
Bool
|
||||||
ephyrRandRGetInfo(ScreenPtr pScreen, Rotation * rotations);
|
ephyrRandRGetInfo(ScreenPtr pScreen, Rotation * rotations);
|
||||||
|
|
|
@ -38,7 +38,6 @@
|
||||||
extern Window EphyrPreExistingHostWin;
|
extern Window EphyrPreExistingHostWin;
|
||||||
extern Bool EphyrWantGrayScale;
|
extern Bool EphyrWantGrayScale;
|
||||||
extern Bool EphyrWantResize;
|
extern Bool EphyrWantResize;
|
||||||
extern Bool EphyrWantNoHostGrab;
|
|
||||||
extern Bool kdHasPointer;
|
extern Bool kdHasPointer;
|
||||||
extern Bool kdHasKbd;
|
extern Bool kdHasKbd;
|
||||||
extern Bool ephyr_glamor, ephyr_glamor_gles2, ephyr_glamor_skip_present;
|
extern Bool ephyr_glamor, ephyr_glamor_gles2, ephyr_glamor_skip_present;
|
||||||
|
@ -144,6 +143,8 @@ ddxUseMsg(void)
|
||||||
ErrorF
|
ErrorF
|
||||||
("-title [title] set the window title in the WM_NAME property\n");
|
("-title [title] set the window title in the WM_NAME property\n");
|
||||||
ErrorF("-no-host-grab Disable grabbing the keyboard and mouse.\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");
|
ErrorF("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,9 +344,21 @@ ddxProcessArgument(int argc, char **argv, int i)
|
||||||
}
|
}
|
||||||
/* end Xnest compat */
|
/* end Xnest compat */
|
||||||
else if (!strcmp(argv[i], "-no-host-grab")) {
|
else if (!strcmp(argv[i], "-no-host-grab")) {
|
||||||
EphyrWantNoHostGrab = 1;
|
ephyrSetGrabShortcut(NULL);
|
||||||
return 1;
|
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") ||
|
else if (!strcmp(argv[i], "-sharevts") ||
|
||||||
!strcmp(argv[i], "-novtswitch")) {
|
!strcmp(argv[i], "-novtswitch")) {
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -67,6 +67,31 @@ window. By default, the Xephyr window has a fixed size.
|
||||||
.TP 8
|
.TP 8
|
||||||
.B \-no\-host\-grab
|
.B \-no\-host\-grab
|
||||||
Disable grabbing the keyboard and mouse.
|
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"
|
.SH "SIGNALS"
|
||||||
Send a SIGUSR1 to the server (e.g. pkill \-USR1 Xephyr) to
|
Send a SIGUSR1 to the server (e.g. pkill \-USR1 Xephyr) to
|
||||||
toggle the debugging mode.
|
toggle the debugging mode.
|
||||||
|
|
Loading…
Reference in New Issue