GUACAMOLE-51: Track and synchronize remote lock key state.

This commit is contained in:
Michael Jumper 2016-08-14 17:19:09 -07:00
parent 4f4f83f9a7
commit 0c48096147
3 changed files with 160 additions and 0 deletions

View File

@ -31,6 +31,46 @@
#include <pthread.h> #include <pthread.h>
#include <stdlib.h> #include <stdlib.h>
/**
* Translates the given keysym into the corresponding lock flag, as would be
* required by the RDP synchronize event. If the given keysym does not
* represent a lock key, zero is returned.
*
* @param keysym
* The keysym to translate into a RDP lock flag.
*
* @return
* The RDP lock flag which corresponds to the given keysym, or zero if the
* given keysym does not represent a lock key.
*/
static int guac_rdp_keyboard_lock_flag(int keysym) {
/* Translate keysym into corresponding lock flag */
switch (keysym) {
/* Scroll lock */
case 0xFF14:
return KBD_SYNC_SCROLL_LOCK;
/* Kana lock */
case 0xFF2D:
return KBD_SYNC_KANA_LOCK;
/* Num lock */
case 0xFF7F:
return KBD_SYNC_NUM_LOCK;
/* Caps lock */
case 0xFFE5:
return KBD_SYNC_CAPS_LOCK;
}
/* Not a lock key */
return 0;
}
/** /**
* Immediately sends an RDP key event having the given scancode and flags. * Immediately sends an RDP key event having the given scancode and flags.
* *
@ -113,6 +153,39 @@ static void guac_rdp_send_unicode_event(guac_rdp_client* rdp_client,
} }
/**
* Immediately sends an RDP synchonize event having the given flags. An RDP
* synchronize event sets the state of remote lock keys absolutely, where a
* lock key will be active only if its corresponding flag is set in the event.
*
* @param rdp_client
* The RDP client instance associated with the RDP session along which the
* synchronize event should be sent.
*
* @param flags
* Bitwise OR of the flags representing the lock keys which should be set,
* if any, as dictated by the RDP protocol. If no flags are set, then no
* lock keys will be active.
*/
static void guac_rdp_send_synchronize_event(guac_rdp_client* rdp_client,
int flags) {
pthread_mutex_lock(&(rdp_client->rdp_lock));
/* Skip if not yet connected */
freerdp* rdp_inst = rdp_client->rdp_inst;
if (rdp_inst == NULL) {
pthread_mutex_unlock(&(rdp_client->rdp_lock));
return;
}
/* Synchronize lock key states */
rdp_inst->input->SynchronizeEvent(rdp_inst->input, flags);
pthread_mutex_unlock(&(rdp_client->rdp_lock));
}
/** /**
* Loads all keysym/scancode mappings declared within the given keymap and its * Loads all keysym/scancode mappings declared within the given keymap and its
* parent keymap, if any. These mappings are stored within the given * parent keymap, if any. These mappings are stored within the given
@ -187,6 +260,11 @@ int guac_rdp_keyboard_send_event(guac_rdp_keyboard* keyboard,
/* If defined, send event */ /* If defined, send event */
if (keysym_desc->scancode != 0) { if (keysym_desc->scancode != 0) {
/* Update remote lock state as necessary */
guac_rdp_keyboard_update_locks(keyboard,
keysym_desc->set_locks,
keysym_desc->clear_locks);
/* If defined, send any prerequesite keys that must be set */ /* If defined, send any prerequesite keys that must be set */
if (keysym_desc->set_keysyms != NULL) if (keysym_desc->set_keysyms != NULL)
guac_rdp_keyboard_send_events(keyboard, guac_rdp_keyboard_send_events(keyboard,
@ -270,9 +348,42 @@ void guac_rdp_keyboard_send_events(guac_rdp_keyboard* keyboard,
} }
void guac_rdp_keyboard_update_locks(guac_rdp_keyboard* keyboard,
int set_flags, int clear_flags) {
guac_client* client = keyboard->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
/* Calculate updated lock flags */
int lock_flags = (keyboard->lock_flags | set_flags) & ~clear_flags;
/* Synchronize remote side only if lock flags have changed */
if (lock_flags != keyboard->lock_flags) {
guac_rdp_send_synchronize_event(rdp_client, lock_flags);
keyboard->lock_flags = lock_flags;
}
}
int guac_rdp_keyboard_update_keysym(guac_rdp_keyboard* keyboard, int guac_rdp_keyboard_update_keysym(guac_rdp_keyboard* keyboard,
int keysym, int pressed) { int keysym, int pressed) {
/* Synchronize lock keys states, if this has not yet been done */
if (!keyboard->synchronized) {
guac_client* client = keyboard->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
/* Synchronize remote lock key states with local state */
guac_rdp_send_synchronize_event(rdp_client, keyboard->lock_flags);
keyboard->synchronized = 1;
}
/* Toggle lock flag, if any */
if (pressed)
keyboard->lock_flags ^= guac_rdp_keyboard_lock_flag(keysym);
/* Update keysym state */ /* Update keysym state */
if (GUAC_RDP_KEYSYM_STORABLE(keysym)) if (GUAC_RDP_KEYSYM_STORABLE(keysym))
GUAC_RDP_KEYSYM_LOOKUP(keyboard->keysym_state, keysym) = pressed; GUAC_RDP_KEYSYM_LOOKUP(keyboard->keysym_state, keysym) = pressed;

View File

@ -35,6 +35,19 @@ typedef struct guac_rdp_keyboard {
*/ */
guac_client* client; guac_client* client;
/**
* The local state of all known lock keys, as a bitwise OR of all RDP lock
* key flags. Legal flags are KBD_SYNC_SCROLL_LOCK, KBD_SYNC_NUM_LOCK,
* KBD_SYNC_CAPS_LOCK, and KBD_SYNC_KANA_LOCK.
*/
int lock_flags;
/**
* Whether the states of remote lock keys (Caps lock, Num lock, etc.) have
* been synchronized with local lock key states.
*/
int synchronized;
/** /**
* The keymap to use when translating keysyms into scancodes or sequences * The keymap to use when translating keysyms into scancodes or sequences
* of scancodes for RDP. * of scancodes for RDP.
@ -127,6 +140,26 @@ int guac_rdp_keyboard_send_event(guac_rdp_keyboard* keyboard,
void guac_rdp_keyboard_send_events(guac_rdp_keyboard* keyboard, void guac_rdp_keyboard_send_events(guac_rdp_keyboard* keyboard,
const int* keysym_string, int from, int to); const int* keysym_string, int from, int to);
/**
* Updates the local state of the lock keys (such as Caps lock or Num lock),
* synchronizing the remote state of those keys if it is expected to differ.
*
* @param keyboard
* The guac_rdp_keyboard associated with the current RDP session.
*
* @param set_flags
* The lock key flags which should be set. Legal flags are
* KBD_SYNC_SCROLL_LOCK, KBD_SYNC_NUM_LOCK, KBD_SYNC_CAPS_LOCK, and
* KBD_SYNC_KANA_LOCK.
*
* @param clear_flags
* The lock key flags which should be cleared. Legal flags are
* KBD_SYNC_SCROLL_LOCK, KBD_SYNC_NUM_LOCK, KBD_SYNC_CAPS_LOCK, and
* KBD_SYNC_KANA_LOCK.
*/
void guac_rdp_keyboard_update_locks(guac_rdp_keyboard* keyboard,
int set_flags, int clear_flags);
/** /**
* Updates the local state of the given keysym, sending the key events required * Updates the local state of the given keysym, sending the key events required
* to replicate that state remotely (on the RDP server). The key events sent * to replicate that state remotely (on the RDP server). The key events sent

View File

@ -62,6 +62,22 @@ typedef struct guac_rdp_keysym_desc {
*/ */
const int* clear_keysyms; const int* clear_keysyms;
/**
* Bitwise OR of the flags of all lock keys (ie: Caps lock, Num lock, etc.)
* which must be active for this keysym to be properly typed. Legal flags
* are KBD_SYNC_SCROLL_LOCK, KBD_SYNC_NUM_LOCK, KBD_SYNC_CAPS_LOCK, and
* KBD_SYNC_KANA_LOCK.
*/
int set_locks;
/**
* Bitwise OR of the flags of all lock keys (ie: Caps lock, Num lock, etc.)
* which must be inactive for this keysym to be properly typed. Legal flags
* are KBD_SYNC_SCROLL_LOCK, KBD_SYNC_NUM_LOCK, KBD_SYNC_CAPS_LOCK, and
* KBD_SYNC_KANA_LOCK.
*/
int clear_locks;
} guac_rdp_keysym_desc; } guac_rdp_keysym_desc;
/** /**