Merge pull request #46 from mbattista/multiple-keys
pressing key with multiple pressed keys
This commit is contained in:
commit
6d38ea71b9
@ -245,14 +245,7 @@ func (manager *RemoteManager) GetScreenSize() *types.ScreenSize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (manager *RemoteManager) SetKeyboardLayout(layout string) {
|
func (manager *RemoteManager) SetKeyboardLayout(layout string) {
|
||||||
// Workaround for https://github.com/m1k1o/neko/issues/45
|
exec.Command("setxkbmap", layout).Run()
|
||||||
// When pressing `shift` + `,` instead of `<` comes `>`.
|
|
||||||
variant := ""
|
|
||||||
if layout == "us" {
|
|
||||||
variant = "mac" // TODO: Test all keys.
|
|
||||||
}
|
|
||||||
|
|
||||||
exec.Command("setxkbmap", layout, variant).Run()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (manager *RemoteManager) SetKeyboardModifiers(NumLock int, CapsLock int, ScrollLock int) {
|
func (manager *RemoteManager) SetKeyboardModifiers(NumLock int, CapsLock int, ScrollLock int) {
|
||||||
|
@ -6,6 +6,48 @@ static char *NAME = ":0.0";
|
|||||||
static int REGISTERED = 0;
|
static int REGISTERED = 0;
|
||||||
static int DIRTY = 0;
|
static int DIRTY = 0;
|
||||||
|
|
||||||
|
xkeys_t *xKeysHead = NULL;
|
||||||
|
|
||||||
|
void XKeysInsert(KeySym keysym, KeyCode keycode) {
|
||||||
|
xkeys_t *node = (xkeys_t *) malloc(sizeof(xkeys_t));
|
||||||
|
|
||||||
|
node->keysym = keysym;
|
||||||
|
node->keycode = keycode;
|
||||||
|
node->next = xKeysHead;
|
||||||
|
xKeysHead = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyCode XKeysPop(KeySym keysym) {
|
||||||
|
KeyCode keycode = 0;
|
||||||
|
xkeys_t *node = xKeysHead,
|
||||||
|
*previous = NULL;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
while (node) {
|
||||||
|
if (node->keysym == keysym) {
|
||||||
|
keycode = node->keycode;
|
||||||
|
|
||||||
|
if (!previous)
|
||||||
|
xKeysHead = node->next;
|
||||||
|
else
|
||||||
|
previous->next = node->next;
|
||||||
|
|
||||||
|
free(node);
|
||||||
|
return keycode;
|
||||||
|
}
|
||||||
|
|
||||||
|
previous = node;
|
||||||
|
node = node->next;
|
||||||
|
if (i++ > 120) {
|
||||||
|
// this should NEVER HAPPEN
|
||||||
|
fprintf(stderr, "[FATAL-ERROR] XKeysPop() in xorg.c: reached maximum loop limit! Something is wrong\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
Display *getXDisplay(void) {
|
Display *getXDisplay(void) {
|
||||||
/* Close the display if displayName has changed */
|
/* Close the display if displayName has changed */
|
||||||
if (DIRTY) {
|
if (DIRTY) {
|
||||||
@ -23,7 +65,7 @@ Display *getXDisplay(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (DISPLAY == NULL) {
|
if (DISPLAY == NULL) {
|
||||||
fputs("Could not open main display\n", stderr);
|
fprintf(stderr, "[FATAL-ERROR] XKeysPop() in xorg.c: Could not open main display!");
|
||||||
} else if (!REGISTERED) {
|
} else if (!REGISTERED) {
|
||||||
atexit(&XDisplayClose);
|
atexit(&XDisplayClose);
|
||||||
REGISTERED = 1;
|
REGISTERED = 1;
|
||||||
@ -96,26 +138,74 @@ void XButton(unsigned int button, int down) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void XKey(unsigned long key, int down) {
|
// From: https://github.com/TigerVNC/tigervnc/blob/0946e298075f8f7b6d63e552297a787c5f84d27c/unix/x0vncserver/XDesktop.cxx#L343-L379
|
||||||
if (key != 0) {
|
KeyCode XkbKeysymToKeycode(Display *dpy, KeySym keysym) {
|
||||||
Display *display = getXDisplay();
|
XkbDescPtr xkb;
|
||||||
KeyCode code = XKeysymToKeycode(display, key);
|
XkbStateRec state;
|
||||||
|
unsigned int mods;
|
||||||
|
unsigned keycode;
|
||||||
|
|
||||||
// Map non-existing keysyms to new keycodes
|
xkb = XkbGetMap(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
|
||||||
if(code == 0) {
|
if (!xkb)
|
||||||
int min, max, numcodes;
|
return 0;
|
||||||
XDisplayKeycodes(display, &min, &max);
|
|
||||||
XGetKeyboardMapping(display, min, max-min, &numcodes);
|
|
||||||
|
|
||||||
code = (max-min+1)*numcodes;
|
XkbGetState(dpy, XkbUseCoreKbd, &state);
|
||||||
KeySym keysym_list[numcodes];
|
// XkbStateFieldFromRec() doesn't work properly because
|
||||||
for(int i=0;i<numcodes;i++) keysym_list[i] = key;
|
// state.lookup_mods isn't properly updated, so we do this manually
|
||||||
XChangeKeyboardMapping(display, code, numcodes, keysym_list, 1);
|
mods = XkbBuildCoreState(XkbStateMods(&state), state.group);
|
||||||
}
|
|
||||||
|
|
||||||
XTestFakeKeyEvent(display, code, down, CurrentTime);
|
for (keycode = xkb->min_key_code;
|
||||||
XSync(display, 0);
|
keycode <= xkb->max_key_code;
|
||||||
|
keycode++) {
|
||||||
|
KeySym cursym;
|
||||||
|
unsigned int out_mods;
|
||||||
|
XkbTranslateKeyCode(xkb, keycode, mods, &out_mods, &cursym);
|
||||||
|
if (cursym == keysym)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (keycode > xkb->max_key_code)
|
||||||
|
keycode = 0;
|
||||||
|
|
||||||
|
XkbFreeKeyboard(xkb, XkbAllComponentsMask, True);
|
||||||
|
|
||||||
|
// Shift+Tab is usually ISO_Left_Tab, but RFB hides this fact. Do
|
||||||
|
// another attempt if we failed the initial lookup
|
||||||
|
if ((keycode == 0) && (keysym == XK_Tab) && (mods & ShiftMask))
|
||||||
|
return XkbKeysymToKeycode(dpy, XK_ISO_Left_Tab);
|
||||||
|
|
||||||
|
return keycode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XKey(KeySym key, int down) {
|
||||||
|
Display *display = getXDisplay();
|
||||||
|
KeyCode code = 0;
|
||||||
|
|
||||||
|
if (!down)
|
||||||
|
code = XKeysPop(key);
|
||||||
|
|
||||||
|
if (!code)
|
||||||
|
code = XkbKeysymToKeycode(display, key);
|
||||||
|
|
||||||
|
if (!code) {
|
||||||
|
int min, max, numcodes;
|
||||||
|
XDisplayKeycodes(display, &min, &max);
|
||||||
|
XGetKeyboardMapping(display, min, max-min, &numcodes);
|
||||||
|
|
||||||
|
code = (max-min+1)*numcodes;
|
||||||
|
KeySym keysym_list[numcodes];
|
||||||
|
for (int i=0;i<numcodes;i++) keysym_list[i] = key;
|
||||||
|
XChangeKeyboardMapping(display, code, numcodes, keysym_list, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!code)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (down)
|
||||||
|
XKeysInsert(key, code);
|
||||||
|
|
||||||
|
XTestFakeKeyEvent(display, code, down, CurrentTime);
|
||||||
|
XSync(display, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void XClipboardSet(char *src) {
|
void XClipboardSet(char *src) {
|
||||||
|
@ -75,7 +75,7 @@ func KeyDown(code uint64) error {
|
|||||||
|
|
||||||
debounce_key[code] = time.Now()
|
debounce_key[code] = time.Now()
|
||||||
|
|
||||||
C.XKey(C.ulong(code), C.int(1))
|
C.XKey(C.KeySym(code), C.int(1))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ func KeyUp(code uint64) error {
|
|||||||
|
|
||||||
delete(debounce_key, code)
|
delete(debounce_key, code)
|
||||||
|
|
||||||
C.XKey(C.ulong(code), C.int(0))
|
C.XKey(C.KeySym(code), C.int(0))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,12 @@
|
|||||||
extern void goCreateScreenSize(int index, int width, int height, int mwidth, int mheight);
|
extern void goCreateScreenSize(int index, int width, int height, int mwidth, int mheight);
|
||||||
extern void goSetScreenRates(int index, int rate_index, short rate);
|
extern void goSetScreenRates(int index, int rate_index, short rate);
|
||||||
|
|
||||||
|
typedef struct xkeys_t {
|
||||||
|
KeySym keysym;
|
||||||
|
KeyCode keycode;
|
||||||
|
struct xkeys_t *next;
|
||||||
|
} xkeys_t;
|
||||||
|
|
||||||
/* Returns the main display, closed either on exit or when closeMainDisplay()
|
/* Returns the main display, closed either on exit or when closeMainDisplay()
|
||||||
* is invoked. This removes a bit of the overhead of calling XOpenDisplay() &
|
* is invoked. This removes a bit of the overhead of calling XOpenDisplay() &
|
||||||
* XCloseDisplay() everytime the main display needs to be used.
|
* XCloseDisplay() everytime the main display needs to be used.
|
||||||
|
Reference in New Issue
Block a user