GUACAMOLE-352: Merge add support for typing using dead keys within RDP.
This commit is contained in:
commit
4e5a7e97ad
@ -28,6 +28,7 @@ nodist_libguac_client_rdp_la_SOURCES = \
|
|||||||
libguac_client_rdp_la_SOURCES = \
|
libguac_client_rdp_la_SOURCES = \
|
||||||
audio_input.c \
|
audio_input.c \
|
||||||
client.c \
|
client.c \
|
||||||
|
decompose.c \
|
||||||
dvc.c \
|
dvc.c \
|
||||||
error.c \
|
error.c \
|
||||||
input.c \
|
input.c \
|
||||||
@ -98,6 +99,7 @@ noinst_HEADERS = \
|
|||||||
guac_svc/svc_service.h \
|
guac_svc/svc_service.h \
|
||||||
audio_input.h \
|
audio_input.h \
|
||||||
client.h \
|
client.h \
|
||||||
|
decompose.h \
|
||||||
dvc.h \
|
dvc.h \
|
||||||
error.h \
|
error.h \
|
||||||
input.h \
|
input.h \
|
||||||
|
177
src/protocols/rdp/decompose.c
Normal file
177
src/protocols/rdp/decompose.c
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "keyboard.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The X11 keysym for the dead key which types a grave (`).
|
||||||
|
*/
|
||||||
|
#define DEAD_GRAVE 0xFE50
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The X11 keysym for the dead key which types an acute (´). Note that this is
|
||||||
|
* NOT equivalent to an apostrophe or single quote.
|
||||||
|
*/
|
||||||
|
#define DEAD_ACUTE 0xFE51
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The X11 keysym for the dead key which types a circumflex/caret (^).
|
||||||
|
*/
|
||||||
|
#define DEAD_CIRCUMFLEX 0xFE52
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The X11 keysym for the dead key which types a tilde (~).
|
||||||
|
*/
|
||||||
|
#define DEAD_TILDE 0xFE53
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The X11 keysym for the dead key which types a dieresis/umlaut (¨).
|
||||||
|
*/
|
||||||
|
#define DEAD_DIERESIS 0xFE57
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The X11 keysym for the dead key which types an abovering (˚). Note that this
|
||||||
|
* is NOT equivalent to the degree symbol.
|
||||||
|
*/
|
||||||
|
#define DEAD_ABOVERING 0xFE58
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The decomposed form of a key that can be typed using two keypresses: a dead
|
||||||
|
* key followed by a base key. For example, on a keyboard which lacks a single
|
||||||
|
* dedicated key for doing the same, "ó" would be typed using the dead acute
|
||||||
|
* key followed by the "o" key. The dead key and base key are pressed and
|
||||||
|
* released in sequence; they are not held down.
|
||||||
|
*/
|
||||||
|
typedef struct guac_rdp_decomposed_key {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The keysym of the dead key which must first be pressed and released to
|
||||||
|
* begin typing the desired character. The dead key defines the diacritic
|
||||||
|
* which will be applied to the character typed by the base key.
|
||||||
|
*/
|
||||||
|
int dead_keysym;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The keysym of the base key which must be pressed and released to finish
|
||||||
|
* typing the desired character. The base key defines the normal form of
|
||||||
|
* the character (the form which lacks any diacritic) to which the
|
||||||
|
* diacritic defined by the previously-pressed dead key will be applied.
|
||||||
|
*/
|
||||||
|
int base_keysym;
|
||||||
|
|
||||||
|
} guac_rdp_decomposed_key;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A lookup table of all known decomposed forms of various keysyms. Keysyms map
|
||||||
|
* directly to entries within this table. A keysym which has no entry within
|
||||||
|
* this table does not have a defined decomposed form (or at least does not
|
||||||
|
* have a decomposed form relevant to RDP).
|
||||||
|
*/
|
||||||
|
guac_rdp_decomposed_key guac_rdp_decomposed_keys[256] = {
|
||||||
|
|
||||||
|
/* ^ */ [0x005E] = { DEAD_CIRCUMFLEX, ' ' },
|
||||||
|
/* ` */ [0x0060] = { DEAD_GRAVE, ' ' },
|
||||||
|
/* ~ */ [0x007E] = { DEAD_TILDE, ' ' },
|
||||||
|
/* ¨ */ [0x00A8] = { DEAD_DIERESIS, ' ' },
|
||||||
|
/* ´ */ [0x00B4] = { DEAD_ACUTE, ' ' },
|
||||||
|
/* À */ [0x00C0] = { DEAD_GRAVE, 'A' },
|
||||||
|
/* Á */ [0x00C1] = { DEAD_ACUTE, 'A' },
|
||||||
|
/* Â */ [0x00C2] = { DEAD_CIRCUMFLEX, 'A' },
|
||||||
|
/* Ã */ [0x00C3] = { DEAD_TILDE, 'A' },
|
||||||
|
/* Ä */ [0x00C4] = { DEAD_DIERESIS, 'A' },
|
||||||
|
/* Å */ [0x00C5] = { DEAD_ABOVERING, 'A' },
|
||||||
|
/* È */ [0x00C8] = { DEAD_GRAVE, 'E' },
|
||||||
|
/* É */ [0x00C9] = { DEAD_ACUTE, 'E' },
|
||||||
|
/* Ê */ [0x00CA] = { DEAD_CIRCUMFLEX, 'E' },
|
||||||
|
/* Ë */ [0x00CB] = { DEAD_DIERESIS, 'E' },
|
||||||
|
/* Ì */ [0x00CC] = { DEAD_GRAVE, 'I' },
|
||||||
|
/* Í */ [0x00CD] = { DEAD_ACUTE, 'I' },
|
||||||
|
/* Î */ [0x00CE] = { DEAD_CIRCUMFLEX, 'I' },
|
||||||
|
/* Ï */ [0x00CF] = { DEAD_DIERESIS, 'I' },
|
||||||
|
/* Ñ */ [0x00D1] = { DEAD_TILDE, 'N' },
|
||||||
|
/* Ò */ [0x00D2] = { DEAD_GRAVE, 'O' },
|
||||||
|
/* Ó */ [0x00D3] = { DEAD_ACUTE, 'O' },
|
||||||
|
/* Ô */ [0x00D4] = { DEAD_CIRCUMFLEX, 'O' },
|
||||||
|
/* Õ */ [0x00D5] = { DEAD_TILDE, 'O' },
|
||||||
|
/* Ö */ [0x00D6] = { DEAD_DIERESIS, 'O' },
|
||||||
|
/* Ù */ [0x00D9] = { DEAD_GRAVE, 'U' },
|
||||||
|
/* Ú */ [0x00DA] = { DEAD_ACUTE, 'U' },
|
||||||
|
/* Û */ [0x00DB] = { DEAD_CIRCUMFLEX, 'U' },
|
||||||
|
/* Ü */ [0x00DC] = { DEAD_DIERESIS, 'U' },
|
||||||
|
/* Ý */ [0x00DD] = { DEAD_ACUTE, 'Y' },
|
||||||
|
/* à */ [0x00E0] = { DEAD_GRAVE, 'a' },
|
||||||
|
/* á */ [0x00E1] = { DEAD_ACUTE, 'a' },
|
||||||
|
/* â */ [0x00E2] = { DEAD_CIRCUMFLEX, 'a' },
|
||||||
|
/* ã */ [0x00E3] = { DEAD_TILDE, 'a' },
|
||||||
|
/* ä */ [0x00E4] = { DEAD_DIERESIS, 'a' },
|
||||||
|
/* å */ [0x00E5] = { DEAD_ABOVERING, 'a' },
|
||||||
|
/* è */ [0x00E8] = { DEAD_GRAVE, 'e' },
|
||||||
|
/* é */ [0x00E9] = { DEAD_ACUTE, 'e' },
|
||||||
|
/* ê */ [0x00EA] = { DEAD_CIRCUMFLEX, 'e' },
|
||||||
|
/* ë */ [0x00EB] = { DEAD_DIERESIS, 'e' },
|
||||||
|
/* ì */ [0x00EC] = { DEAD_GRAVE, 'i' },
|
||||||
|
/* í */ [0x00ED] = { DEAD_ACUTE, 'i' },
|
||||||
|
/* î */ [0x00EE] = { DEAD_CIRCUMFLEX, 'i' },
|
||||||
|
/* ï */ [0x00EF] = { DEAD_DIERESIS, 'i' },
|
||||||
|
/* ñ */ [0x00F1] = { DEAD_TILDE, 'n' },
|
||||||
|
/* ò */ [0x00F2] = { DEAD_GRAVE, 'o' },
|
||||||
|
/* ó */ [0x00F3] = { DEAD_ACUTE, 'o' },
|
||||||
|
/* ô */ [0x00F4] = { DEAD_CIRCUMFLEX, 'o' },
|
||||||
|
/* õ */ [0x00F5] = { DEAD_TILDE, 'o' },
|
||||||
|
/* ö */ [0x00F6] = { DEAD_DIERESIS, 'o' },
|
||||||
|
/* ù */ [0x00F9] = { DEAD_GRAVE, 'u' },
|
||||||
|
/* ú */ [0x00FA] = { DEAD_ACUTE, 'u' },
|
||||||
|
/* û */ [0x00FB] = { DEAD_CIRCUMFLEX, 'u' },
|
||||||
|
/* ü */ [0x00FC] = { DEAD_DIERESIS, 'u' },
|
||||||
|
/* ý */ [0x00FD] = { DEAD_ACUTE, 'y' },
|
||||||
|
/* ÿ */ [0x00FF] = { DEAD_DIERESIS, 'y' }
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
int guac_rdp_decompose_keysym(guac_rdp_keyboard* keyboard, int keysym) {
|
||||||
|
|
||||||
|
/* Verify keysym is within range of lookup table */
|
||||||
|
if (keysym < 0x00 || keysym > 0xFF)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* Verify keysym is actually defined within lookup table */
|
||||||
|
guac_rdp_decomposed_key* key = &guac_rdp_decomposed_keys[keysym];
|
||||||
|
if (!key->dead_keysym)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* Cannot type using decomposed keys if those keys are not defined within
|
||||||
|
* the current layout */
|
||||||
|
if (!guac_rdp_keyboard_is_defined(keyboard, key->dead_keysym)
|
||||||
|
|| !guac_rdp_keyboard_is_defined(keyboard, key->base_keysym))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* Press dead key */
|
||||||
|
guac_rdp_keyboard_send_event(keyboard, key->dead_keysym, 1);
|
||||||
|
guac_rdp_keyboard_send_event(keyboard, key->dead_keysym, 0);
|
||||||
|
|
||||||
|
/* Press base key */
|
||||||
|
guac_rdp_keyboard_send_event(keyboard, key->base_keysym, 1);
|
||||||
|
guac_rdp_keyboard_send_event(keyboard, key->base_keysym, 0);
|
||||||
|
|
||||||
|
/* Decomposed key successfully typed */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
50
src/protocols/rdp/decompose.h
Normal file
50
src/protocols/rdp/decompose.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GUAC_RDP_DECOMPOSE_H
|
||||||
|
#define GUAC_RDP_DECOMPOSE_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "keyboard.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to type the given keysym by decomposing the associated character
|
||||||
|
* into the dead key and base key pair which would be used to type that
|
||||||
|
* character on a keyboard which lacks the necessary dedicated key. The key
|
||||||
|
* events for the dead key and base key are sent only if the keyboard layout of
|
||||||
|
* the given keyboard defines those keys.
|
||||||
|
*
|
||||||
|
* For example, the keysym for "ò" (0x00F2) would decompose into a dead grave
|
||||||
|
* (`) and the base key "o". May it rest in peace.
|
||||||
|
*
|
||||||
|
* @param keyboard
|
||||||
|
* The guac_rdp_keyboard associated with the current RDP session.
|
||||||
|
*
|
||||||
|
* @param keysym
|
||||||
|
* The keysym being pressed.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* Zero if the keysym was successfully decomposed and sent to the RDP
|
||||||
|
* server as a pair of key events (the dead key and base key), non-zero
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
int guac_rdp_decompose_keysym(guac_rdp_keyboard* keyboard, int keysym);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -20,6 +20,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
|
#include "decompose.h"
|
||||||
#include "keyboard.h"
|
#include "keyboard.h"
|
||||||
#include "rdp.h"
|
#include "rdp.h"
|
||||||
#include "rdp_keymap.h"
|
#include "rdp_keymap.h"
|
||||||
@ -244,6 +245,21 @@ void guac_rdp_keyboard_free(guac_rdp_keyboard* keyboard) {
|
|||||||
free(keyboard);
|
free(keyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int guac_rdp_keyboard_is_defined(guac_rdp_keyboard* keyboard, int keysym) {
|
||||||
|
|
||||||
|
/* Verify keysym can actually be stored within keymap */
|
||||||
|
if (!GUAC_RDP_KEYSYM_STORABLE(keysym))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Look up scancode mapping */
|
||||||
|
const guac_rdp_keysym_desc* keysym_desc =
|
||||||
|
&GUAC_RDP_KEYSYM_LOOKUP(keyboard->keymap, keysym);
|
||||||
|
|
||||||
|
/* Return whether the mapping actually exists */
|
||||||
|
return keysym_desc->scancode != 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
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) {
|
||||||
|
|
||||||
@ -294,12 +310,15 @@ int guac_rdp_keyboard_send_event(guac_rdp_keyboard* keyboard,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fall back to unicode events if undefined inside current keymap */
|
/* Fall back to dead keys or Unicode events if otherwise undefined inside
|
||||||
|
* current keymap (note that we only handle "pressed" here, as neither
|
||||||
/* Only send when key pressed - Unicode events do not have
|
* Unicode events nor dead keys can have a pressed/released state) */
|
||||||
* DOWN/RELEASE flags */
|
|
||||||
if (pressed) {
|
if (pressed) {
|
||||||
|
|
||||||
|
/* Attempt to type using dead keys */
|
||||||
|
if (!guac_rdp_decompose_keysym(keyboard, keysym))
|
||||||
|
return 0;
|
||||||
|
|
||||||
guac_client_log(client, GUAC_LOG_DEBUG,
|
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||||
"Sending keysym 0x%x as Unicode", keysym);
|
"Sending keysym 0x%x as Unicode", keysym);
|
||||||
|
|
||||||
|
@ -95,6 +95,23 @@ guac_rdp_keyboard* guac_rdp_keyboard_alloc(guac_client* client,
|
|||||||
*/
|
*/
|
||||||
void guac_rdp_keyboard_free(guac_rdp_keyboard* keyboard);
|
void guac_rdp_keyboard_free(guac_rdp_keyboard* keyboard);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the given keysym is defined for the keyboard layout
|
||||||
|
* associated with the given keyboard.
|
||||||
|
*
|
||||||
|
* @param keyboard
|
||||||
|
* The guac_rdp_keyboard instance to check.
|
||||||
|
*
|
||||||
|
* @param keysym
|
||||||
|
* The keysym of the key being checked against the keyboard layout of the
|
||||||
|
* given keyboard.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* Non-zero if the key is explicitly defined within the keyboard layout of
|
||||||
|
* the given keyboard, zero otherwise.
|
||||||
|
*/
|
||||||
|
int guac_rdp_keyboard_is_defined(guac_rdp_keyboard* keyboard, int keysym);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends one or more RDP key events, effectively pressing or releasing the
|
* Sends one or more RDP key events, effectively pressing or releasing the
|
||||||
* given keysym on the remote side. The key events sent will depend on the
|
* given keysym on the remote side. The key events sent will depend on the
|
||||||
|
Loading…
Reference in New Issue
Block a user