GUACAMOLE-518: Track and update modifier states based on overall flags, not keysyms.

This commit is contained in:
Michael Jumper 2020-06-21 20:59:11 -07:00
parent 120be65dbc
commit ce0982fefd
4 changed files with 169 additions and 177 deletions

View File

@ -28,6 +28,39 @@
#include <stdlib.h> #include <stdlib.h>
/**
* Translates the given keysym into the corresponding modifier flag, as defined
* by keymap.h. If the given keysym does not represent a modifier key, zero is
* returned.
*
* @param keysym
* The keysym to translate into a modifier flag.
*
* @return
* The modifier flag which corresponds to the given keysym, or zero if the
* given keysym does not represent a modifier key.
*/
static int guac_rdp_keyboard_modifier_flag(int keysym) {
/* Translate keysym into corresponding modifier flag */
switch (keysym) {
/* Shift */
case 0xFFE1:
case 0xFFE2:
return GUAC_RDP_KEYMAP_MODIFIER_SHIFT;
/* AltGr */
case 0xFE03:
return GUAC_RDP_KEYMAP_MODIFIER_ALTGR;
}
/* Not a modifier */
return 0;
}
/** /**
* Translates the given keysym into the corresponding lock flag, as would be * Translates the given keysym into the corresponding lock flag, as would be
* required by the RDP synchronize event. If the given keysym does not * required by the RDP synchronize event. If the given keysym does not
@ -328,38 +361,15 @@ int guac_rdp_keyboard_send_event(guac_rdp_keyboard* keyboard,
keysym_desc->set_locks, keysym_desc->set_locks,
keysym_desc->clear_locks); keysym_desc->clear_locks);
/* If defined, send any prerequesite keys that must be set */ /* Update remote modifier states as necessary */
if (keysym_desc->set_keysyms != NULL) guac_rdp_keyboard_update_modifiers(keyboard,
guac_rdp_keyboard_send_events(keyboard, keysym_desc->set_modifiers,
keysym_desc->set_keysyms, keysym_desc->clear_modifiers);
GUAC_RDP_KEY_RELEASED,
GUAC_RDP_KEY_PRESSED);
/* If defined, release any keys that must be cleared */
if (keysym_desc->clear_keysyms != NULL)
guac_rdp_keyboard_send_events(keyboard,
keysym_desc->clear_keysyms,
GUAC_RDP_KEY_PRESSED,
GUAC_RDP_KEY_RELEASED);
/* Fire actual key event for target key */ /* Fire actual key event for target key */
guac_rdp_send_key_event(rdp_client, keysym_desc->scancode, guac_rdp_send_key_event(rdp_client, keysym_desc->scancode,
keysym_desc->flags, pressed); keysym_desc->flags, pressed);
/* If defined, release any keys that were originally released */
if (keysym_desc->set_keysyms != NULL)
guac_rdp_keyboard_send_events(keyboard,
keysym_desc->set_keysyms,
GUAC_RDP_KEY_RELEASED,
GUAC_RDP_KEY_RELEASED);
/* If defined, send any keys that were originally set */
if (keysym_desc->clear_keysyms != NULL)
guac_rdp_keyboard_send_events(keyboard,
keysym_desc->clear_keysyms,
GUAC_RDP_KEY_PRESSED,
GUAC_RDP_KEY_PRESSED);
return 0; return 0;
} }
@ -398,28 +408,6 @@ int guac_rdp_keyboard_send_event(guac_rdp_keyboard* keyboard,
return 0; return 0;
} }
void guac_rdp_keyboard_send_events(guac_rdp_keyboard* keyboard,
const int* keysym_string, guac_rdp_key_state from,
guac_rdp_key_state to) {
int keysym;
/* Send all keysyms in string, NULL terminated */
while ((keysym = *keysym_string) != 0) {
/* If key is currently in given state, send event for changing it to
* specified "to" state */
guac_rdp_key* key = guac_rdp_keyboard_get_key(keyboard, keysym);
if (key != NULL && key->state == from)
guac_rdp_keyboard_send_event(keyboard, *keysym_string, to);
/* Next keysym */
keysym_string++;
}
}
void guac_rdp_keyboard_update_locks(guac_rdp_keyboard* keyboard, void guac_rdp_keyboard_update_locks(guac_rdp_keyboard* keyboard,
int set_flags, int clear_flags) { int set_flags, int clear_flags) {
@ -437,9 +425,50 @@ void guac_rdp_keyboard_update_locks(guac_rdp_keyboard* keyboard,
} }
void guac_rdp_keyboard_update_modifiers(guac_rdp_keyboard* keyboard,
int set_flags, int clear_flags) {
/* Only clear modifiers that are set */
clear_flags &= keyboard->modifier_flags;
/* Only set modifiers that are currently cleared */
set_flags &= ~keyboard->modifier_flags;
/* Press/release Shift as needed */
if (set_flags & GUAC_RDP_KEYMAP_MODIFIER_SHIFT) {
guac_rdp_keyboard_update_keysym(keyboard, 0xFFE1, 1);
}
else if (clear_flags & GUAC_RDP_KEYMAP_MODIFIER_SHIFT) {
guac_rdp_keyboard_update_keysym(keyboard, 0xFFE1, 0);
guac_rdp_keyboard_update_keysym(keyboard, 0xFFE2, 0);
}
/* Press/release AltGr as needed */
if (set_flags & GUAC_RDP_KEYMAP_MODIFIER_ALTGR) {
guac_rdp_keyboard_update_keysym(keyboard, 0xFE03, 1);
}
else if (clear_flags & GUAC_RDP_KEYMAP_MODIFIER_ALTGR) {
guac_rdp_keyboard_update_keysym(keyboard, 0xFE03, 0);
}
}
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) {
guac_rdp_key* key = guac_rdp_keyboard_get_key(keyboard, keysym);
if (key != NULL) {
/* Ignore event if state is not changing */
guac_rdp_key_state new_state = pressed ? GUAC_RDP_KEY_PRESSED : GUAC_RDP_KEY_RELEASED;
if (key->state == new_state)
return 0;
/* Update keysym state */
key->state = new_state;
}
/* Synchronize lock keys states, if this has not yet been done */ /* Synchronize lock keys states, if this has not yet been done */
if (!keyboard->synchronized) { if (!keyboard->synchronized) {
@ -452,14 +481,15 @@ int guac_rdp_keyboard_update_keysym(guac_rdp_keyboard* keyboard,
} }
/* Toggle lock flag, if any */ /* Toggle locks and set modifiers on keydown */
if (pressed) if (pressed) {
keyboard->lock_flags ^= guac_rdp_keyboard_lock_flag(keysym); keyboard->lock_flags ^= guac_rdp_keyboard_lock_flag(keysym);
keyboard->modifier_flags |= guac_rdp_keyboard_modifier_flag(keysym);
}
/* Update keysym state */ /* Clear modifiers on keyup */
guac_rdp_key* key = guac_rdp_keyboard_get_key(keyboard, keysym); else
if (key != NULL) keyboard->modifier_flags &= ~guac_rdp_keyboard_modifier_flag(keysym);
key->state = pressed ? GUAC_RDP_KEY_PRESSED : GUAC_RDP_KEY_RELEASED;
return guac_rdp_keyboard_send_event(keyboard, keysym, pressed); return guac_rdp_keyboard_send_event(keyboard, keysym, pressed);

View File

@ -79,6 +79,15 @@ typedef struct guac_rdp_keyboard {
*/ */
guac_client* client; guac_client* client;
/**
* The local state of all known modifier keys, as a bitwise OR of the
* modified flags used by the keymaps.
*
* @see GUAC_RDP_KEYMAP_MODIFIER_SHIFT
* @see GUAC_RDP_KEYMAP_MODIFIER_ALTGR
*/
int modifier_flags;
/** /**
* The local state of all known lock keys, as a bitwise OR of all RDP lock * 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, * key flags. Legal flags are KBD_SYNC_SCROLL_LOCK, KBD_SYNC_NUM_LOCK,
@ -177,33 +186,6 @@ int guac_rdp_keyboard_is_defined(guac_rdp_keyboard* keyboard, int keysym);
int guac_rdp_keyboard_send_event(guac_rdp_keyboard* keyboard, int guac_rdp_keyboard_send_event(guac_rdp_keyboard* keyboard,
int keysym, int pressed); int keysym, int pressed);
/**
* For every keysym in the given NULL-terminated array of keysyms, send the RDP
* key events required to update the remote state of those keys as specified,
* depending on the current local state of those keysyms. For each key in the
* "from" state, that key will be updated to the "to" state. The locally-stored
* state of each key is remains untouched.
*
* @param keyboard
* The guac_rdp_keyboard associated with the current RDP session.
*
* @param keysym_string
* A NULL-terminated array of keysyms, each of which will be updated.
*
* @param from
* GUAC_RDP_KEY_RELEASED if the state of currently-released keys should be
* updated, or GUAC_RDP_KEY_PRESSED if the state of currently-pressed keys
* should be updated.
*
* @param to
* GUAC_RDP_KEY_RELEASED if the keys being updated should be marked as
* released, or GUAC_RDP_KEY_PRESSED if the keys being updated should be
* marked as pressed.
*/
void guac_rdp_keyboard_send_events(guac_rdp_keyboard* keyboard,
const int* keysym_string, guac_rdp_key_state from,
guac_rdp_key_state to);
/** /**
* Updates the local state of the lock keys (such as Caps lock or Num lock), * 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. * synchronizing the remote state of those keys if it is expected to differ.
@ -224,6 +206,26 @@ void guac_rdp_keyboard_send_events(guac_rdp_keyboard* keyboard,
void guac_rdp_keyboard_update_locks(guac_rdp_keyboard* keyboard, void guac_rdp_keyboard_update_locks(guac_rdp_keyboard* keyboard,
int set_flags, int clear_flags); int set_flags, int clear_flags);
/**
* Updates the local state of the modifier keys (such as Shift or AltGr),
* synchronizing the remote state of those keys if it is expected to differ.
* Valid modifier flags are defined by keymap.h.
*
* @see GUAC_RDP_KEYMAP_MODIFIER_SHIFT
* @see GUAC_RDP_KEYMAP_MODIFIER_ALTGR
*
* @param keyboard
* The guac_rdp_keyboard associated with the current RDP session.
*
* @param set_flags
* The modifier key flags which should be set.
*
* @param clear_flags
* The modifier key flags which should be cleared.
*/
void guac_rdp_keyboard_update_modifiers(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

@ -22,6 +22,16 @@
#include <winpr/wtypes.h> #include <winpr/wtypes.h>
/**
* Bitwise flag value representing the Shift modifier.
*/
#define GUAC_RDP_KEYMAP_MODIFIER_SHIFT 1
/**
* Bitwise flag value representing the AltGr modifier.
*/
#define GUAC_RDP_KEYMAP_MODIFIER_ALTGR 2
/** /**
* Represents a keysym-to-scancode mapping for RDP, with extra information * Represents a keysym-to-scancode mapping for RDP, with extra information
* about the state of prerequisite keysyms. * about the state of prerequisite keysyms.
@ -39,29 +49,44 @@ typedef struct guac_rdp_keysym_desc {
int scancode; int scancode;
/** /**
* Required RDP-specific flags. * Required RDP-specific flags that must be sent along with the scancode.
*/ */
int flags; int flags;
/** /**
* Null-terminated list of keysyms which must be down for this keysym * Bitwise-OR of the flags of any modifiers that must be active for the
* to be properly typed. * associated scancode to be interpreted as this keysym.
*
* If the associated keysym is pressed, and any of these modifiers are not
* currently active, Guacamole's RDP support must send additional events to
* activate these modifiers prior to sending the scancode for this keysym.
*
* @see GUAC_RDP_KEYMAP_MODIFIER_SHIFT
* @see GUAC_RDP_KEYMAP_MODIFIER_ALTGR
*/ */
const int* set_keysyms; const int set_modifiers;
/** /**
* Null-terminated list of keysyms which must be up for this keysym * Bitwise-OR of the flags of any modifiers that must NOT be active for the
* to be properly typed. * associated scancode to be interpreted as this keysym.
*
* If the associated keysym is pressed, and any of these modifiers are
* currently active, Guacamole's RDP support must send additional events to
* deactivate these modifiers prior to sending the scancode for this
* keysym.
*
* @see GUAC_RDP_KEYMAP_MODIFIER_SHIFT
* @see GUAC_RDP_KEYMAP_MODIFIER_ALTGR
*/ */
const int* clear_keysyms; const int clear_modifiers;
/** /**
* Bitwise OR of the flags of all lock keys (ie: Caps lock, Num lock, etc.) * 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 * 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 * are KBD_SYNC_SCROLL_LOCK, KBD_SYNC_NUM_LOCK, KBD_SYNC_CAPS_LOCK, and
* KBD_SYNC_KANA_LOCK. * KBD_SYNC_KANA_LOCK.
*/ */
int set_locks; const int set_locks;
/** /**
* Bitwise OR of the flags of all lock keys (ie: Caps lock, Num lock, etc.) * Bitwise OR of the flags of all lock keys (ie: Caps lock, Num lock, etc.)
@ -69,7 +94,7 @@ typedef struct guac_rdp_keysym_desc {
* are KBD_SYNC_SCROLL_LOCK, KBD_SYNC_NUM_LOCK, KBD_SYNC_CAPS_LOCK, and * are KBD_SYNC_SCROLL_LOCK, KBD_SYNC_NUM_LOCK, KBD_SYNC_CAPS_LOCK, and
* KBD_SYNC_KANA_LOCK. * KBD_SYNC_KANA_LOCK.
*/ */
int clear_locks; const int clear_locks;
} guac_rdp_keysym_desc; } guac_rdp_keysym_desc;
@ -110,63 +135,6 @@ struct guac_rdp_keymap {
*/ */
#define GUAC_DEFAULT_KEYMAP "en-us-qwerty" #define GUAC_DEFAULT_KEYMAP "en-us-qwerty"
/**
* Keysym string containing only the left "shift" key.
*/
extern const int GUAC_KEYSYMS_SHIFT[];
/**
* Keysym string containing both "shift" keys.
*/
extern const int GUAC_KEYSYMS_ALL_SHIFT[];
/**
* Keysym string containing only the right "alt" key (AltGr).
*/
extern const int GUAC_KEYSYMS_ALTGR[];
/**
* Keysym string containing the right "alt" key (AltGr) and
* left shift.
*/
extern const int GUAC_KEYSYMS_SHIFT_ALTGR[];
/**
* Keysym string containing the right "alt" key (AltGr) and
* both shift keys.
*/
extern const int GUAC_KEYSYMS_ALL_SHIFT_ALTGR[];
/**
* Keysym string containing only the left "ctrl" key.
*/
extern const int GUAC_KEYSYMS_CTRL[];
/**
* Keysym string containing both "ctrl" keys.
*/
extern const int GUAC_KEYSYMS_ALL_CTRL[];
/**
* Keysym string containing only the left "alt" key.
*/
extern const int GUAC_KEYSYMS_ALT[];
/**
* Keysym string containing both "alt" keys.
*/
extern const int GUAC_KEYSYMS_ALL_ALT[];
/**
* Keysym string containing the left "alt" and left "ctrl" keys
*/
extern const int GUAC_KEYSYMS_CTRL_ALT[];
/**
* Keysym string containing all modifier keys.
*/
extern const int GUAC_KEYSYMS_ALL_MODIFIERS[];
/** /**
* NULL-terminated array of all keymaps. * NULL-terminated array of all keymaps.
*/ */

View File

@ -92,10 +92,12 @@ for my $filename (@ARGV) {
my $ext_flags = 0; my $ext_flags = 0;
my $set_shift = 0; my $set_shift = 0;
my $set_altgr = 0; my $set_altgr = 0;
my $set_caps = 0;
my $set_num = 0; my $set_num = 0;
my $clear_shift = 0; my $clear_shift = 0;
my $clear_altgr = 0; my $clear_altgr = 0;
my $clear_caps = 0;
my $clear_num = 0; my $clear_num = 0;
# Parse ranges and options # Parse ranges and options
@ -105,6 +107,7 @@ for my $filename (@ARGV) {
if ((my $opt) = m/^\+([a-z]+)$/) { if ((my $opt) = m/^\+([a-z]+)$/) {
if ($opt eq "shift") { $set_shift = 1; } if ($opt eq "shift") { $set_shift = 1; }
elsif ($opt eq "altgr") { $set_altgr = 1; } elsif ($opt eq "altgr") { $set_altgr = 1; }
elsif ($opt eq "caps") { $set_caps = 1; }
elsif ($opt eq "num") { $set_num = 1; } elsif ($opt eq "num") { $set_num = 1; }
elsif ($opt eq "ext") { $ext_flags = 1; } elsif ($opt eq "ext") { $ext_flags = 1; }
else { else {
@ -117,6 +120,7 @@ for my $filename (@ARGV) {
elsif ((my $opt) = m/^-([a-z]+)$/) { elsif ((my $opt) = m/^-([a-z]+)$/) {
if ($opt eq "shift") { $clear_shift = 1; } if ($opt eq "shift") { $clear_shift = 1; }
elsif ($opt eq "altgr") { $clear_altgr = 1; } elsif ($opt eq "altgr") { $clear_altgr = 1; }
elsif ($opt eq "caps") { $clear_caps = 1; }
elsif ($opt eq "num") { $clear_num = 1; } elsif ($opt eq "num") { $clear_num = 1; }
else { else {
die "$filename: $.: ERROR: " die "$filename: $.: ERROR: "
@ -175,37 +179,25 @@ for my $filename (@ARGV) {
. " .keysym = " . $keysyms[$i] . "," . " .keysym = " . $keysyms[$i] . ","
. " .scancode = " . $scancodes[$i]; . " .scancode = " . $scancodes[$i];
# Set requirements # Modifiers that must be active
if ($set_shift && !$set_altgr) { $content .= ", .set_modifiers = 0";
$content .= ", .set_keysyms = GUAC_KEYSYMS_SHIFT"; $content .= " | GUAC_RDP_KEYMAP_MODIFIER_SHIFT" if $set_shift;
} $content .= " | GUAC_RDP_KEYMAP_MODIFIER_ALTGR" if $set_altgr;
elsif (!$set_shift && $set_altgr) {
$content .= ", .set_keysyms = GUAC_KEYSYMS_ALTGR";
}
elsif ($set_shift && $set_altgr) {
$content .= ", .set_keysyms = GUAC_KEYSYMS_SHIFT_ALTGR";
}
# Clear requirements # Modifiers that must be inactive
if ($clear_shift && !$clear_altgr) { $content .= ", .clear_modifiers = 0";
$content .= ", .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT"; $content .= " | GUAC_RDP_KEYMAP_MODIFIER_SHIFT" if $clear_shift;
} $content .= " | GUAC_RDP_KEYMAP_MODIFIER_ALTGR" if $clear_altgr;
elsif (!$clear_shift && $clear_altgr) {
$content .= ", .clear_keysyms = GUAC_KEYSYMS_ALTGR";
}
elsif ($clear_shift && $clear_altgr) {
$content .= ", .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR";
}
# Set locks # Locks that must be set
if ($set_num) { $content .= ", .set_locks = 0";
$content .= ", .set_locks = KBD_SYNC_NUM_LOCK"; $content .= " | KBD_SYNC_NUM_LOCK" if $set_num;
} $content .= " | KBD_SYNC_CAPS_LOCK" if $set_caps;
# Clear locks # Locks that must NOT be set
if ($clear_num) { $content .= ", .clear_locks = 0";
$content .= ", .clear_locks = KBD_SYNC_NUM_LOCK"; $content .= " | KBD_SYNC_NUM_LOCK" if $clear_num;
} $content .= " | KBD_SYNC_CAPS_LOCK" if $clear_caps;
# Flags # Flags
if ($ext_flags) { if ($ext_flags) {