xkeymap.c revision a180a41bba1d50822df23fff0099e90b86638b89
/* -*- c-basic-offset: 8 -*-
rdesktop: A Remote Desktop Protocol client.
User interface services - X keyboard mapping
Copyright (C) Matthew Chapman <matthewc.unsw.edu.au> 1999-2008
Copyright 2003-2008 Peter Astrand <astrand@cendio.se> for Cendio AB
Copyright 2014 Henrik Andersson <hean01@cendio.se> for Cendio AB
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef RDP2VNC
#include "vnc/x11stubs.h"
#else
#endif
#include <ctype.h>
#include <limits.h>
#include <time.h>
#include <string.h>
#include <assert.h>
#include "rdesktop.h"
#include "scancodes.h"
#define KEYMAP_MASK 0x7f
#define KEYMAP_MAX_LINE_LENGTH 80
extern char g_keymapname[16];
extern unsigned int g_keylayout;
extern int g_keyboard_type;
extern int g_keyboard_subtype;
extern int g_keyboard_functionkeys;
extern int g_win_button_size;
extern RD_BOOL g_enable_compose;
extern RDP_VERSION g_rdp_version;
extern RD_BOOL g_numlock_sync;
static RD_BOOL keymap_loaded;
static int min_keycode;
static uint16 remote_modifier_state = 0;
static uint16 saved_remote_modifier_state = 0;
/* Free key_translation structure, including linked list */
static void
{
while (ptr)
{
}
}
/* Free the key_translation_entry for a given keysym and remove from the table */
static void
{
/* Faking a prev node allows us to keep the algorithm simple */
while (ptr)
{
{
}
else
{
}
}
/* Copy pointer back from our fake node */
}
/* Allocate and return a new entry in the translation table */
static key_translation_entry *
{
/* Clear out any existing entry */
/* Allocate the new one */
/* And insert it at head of list */
return entry;
}
/* Retrieve the key_translation_entry for a given keysym */
static key_translation_entry *
{
while (ptr)
{
return ptr;
}
/* Not found */
return NULL;
}
static void
{
{
return;
}
DEBUG_KBD(("Adding translation, keysym=0x%x, scancode=0x%x, "
/* Make a new entry in the table */
/* And add the new translation to it */
return;
}
static void
{
char keyname[KEYMAP_MAX_LINE_LENGTH];
/* Skip over whitespace after the sequence keyword */
/* Fetch the keysym name */
{
return;
}
while (*rest)
{
/* Skip whitespace */
/* Fetch the keysym name */
/* Handle trailing whitespace */
if (*keyname == 0)
break;
if (seq_keysym == NoSymbol)
{
mapname));
return;
}
/* Allocate space for key_translation structure */
/* Do this straight away so the key_translation won't get orphaned on error */
}
DEBUG_KBD(("\n"));
}
xkeymap_from_locale(const char *locale)
{
/* Create a working copy */
/* Truncate at dot and at */
if (ptr)
*ptr = '\0';
if (ptr)
*ptr = '\0';
/* Replace _ with - */
if (ptr)
*ptr = '-';
/* Convert to lowercase */
while (*ptr)
{
ptr++;
}
/* Try to open this keymap (da-dk) */
{
/* Truncate at dash */
if (ptr)
*ptr = '\0';
/* Try the short name (da) */
}
if (fp)
{
return True;
}
return False;
}
/* Joins two path components. The result should be freed with
xfree(). */
static char *
pathjoin(const char *a, const char *b)
{
char *result;
if (b[0] == '/')
{
}
else
{
}
return result;
}
/* Try to open a keymap with fopen() */
FILE *
xkeymap_open(const char *filename)
{
char *home;
if (home)
{
if (fp)
return fp;
}
/* Try KEYMAP_PATH */
if (fp)
return fp;
/* Try current directory, in case we are running from the source
tree */
if (fp)
return fp;
return NULL;
}
static RD_BOOL
xkeymap_read(char *mapname)
{
char line[KEYMAP_MAX_LINE_LENGTH];
unsigned int line_num = 0;
unsigned int line_length = 0;
char *keyname, *p;
char *line_rest;
{
return False;
}
/* FIXME: More tolerant on white space */
{
line_num++;
/* Replace the \n with \0 */
if (p != NULL)
*p = 0;
/* Completely empty line */
{
continue;
}
/* Include */
{
return False;
continue;
}
/* map */
{
continue;
}
/* compose */
{
DEBUG_KBD(("Enabling compose handling\n"));
continue;
}
/* sequence */
{
continue;
}
/* keyboard_type */
{
continue;
}
/* keyboard_subtype */
{
continue;
}
/* keyboard_functionkeys */
{
continue;
}
/* Comment */
if (line[0] == '#')
{
continue;
}
/* Normal line */
if (p == NULL)
{
continue;
}
else
{
*p = 0;
}
/* scancode */
p++;
/* flags */
/* FIXME: Should allow case-insensitive flag names.
Fix by using lex+yacc... */
modifiers = 0;
{
}
{
}
{
}
{
}
{
}
{
/* Automatically add uppercase key, with same modifiers
plus shift */
for (p = keyname; *p; p++)
*p = toupper((int) *p);
}
}
return True;
}
/* Before connecting and creating UI */
void
xkeymap_init(void)
{
unsigned int max_keycode;
{
if (xkeymap_read(g_keymapname))
}
}
static void
{
if (leftkey)
else
if (pressed)
{
if (g_rdp_version >= RDP_V5)
{
}
else
{
/* RDP4 doesn't support winkey. Fake with Ctrl-Esc */
}
}
else
{
/* key released */
if (g_rdp_version >= RDP_V5)
{
}
else
{
}
}
}
static void
{
if (g_rdp_version >= RDP_V5)
{
/* For some reason, it seems to suffice to release
*either* the left or right winkey. */
}
}
void
{
return;
}
{
return keysym;
if (ks != 0)
{
keypress_keysyms[keycode] = 0;
}
else
{
}
return ks;
}
/* Handle special key combinations */
{
switch (keysym)
{
case XK_Return:
{
/* Ctrl-Alt-Enter: toggle full screen */
if (pressed)
return True;
}
break;
case XK_Break:
/* Send Break sequence E0 46 E0 C6 */
if (pressed)
{
(SCANCODE_EXTENDED | 0x46));
(SCANCODE_EXTENDED | 0xc6));
}
/* No release sequence */
return True;
break;
case XK_Pause:
/* According to MS Keyboard Scan Code
Specification, pressing Pause should result
in E1 1D 45 E1 9D C5. I'm not exactly sure
of how this is supposed to be sent via
RDP. The code below seems to work, but with
the side effect that Left Ctrl stays
down. Therefore, we release it when Pause
is released. */
if (pressed)
{
}
else
{
/* Release Left Ctrl */
0x1d, 0);
}
return True;
break;
case XK_Meta_L: /* Windows keys */
case XK_Super_L:
case XK_Hyper_L:
return True;
break;
case XK_Meta_R:
case XK_Super_R:
case XK_Hyper_R:
return True;
break;
case XK_space:
/* Prevent access to the Windows system menu in single app mode */
return True;
break;
case XK_Num_Lock:
/* Synchronize on key release */
if (g_numlock_sync && !pressed)
/* Inhibit */
return True;
break;
case XK_Overlay1_Enable:
/* Toggle SeamlessRDP */
if (pressed)
break;
}
return False;
}
{
key_translation tr = { 0, 0, 0, 0 };
if (ptr)
{
{
{
DEBUG_KBD(("Inhibiting key\n"));
return tr;
}
{
/* The modifiers to send for this key should be obtained
from the local state. Currently, only shift is implemented. */
{
}
}
/* Windows interprets CapsLock+Ctrl+key
differently from Shift+Ctrl+key. Since we
are simulating CapsLock with Shifts, things
like Ctrl+f with CapsLock on breaks. To
solve this, we are releasing Shift if Ctrl
is on, but only if Shift isn't physically pressed. */
{
DEBUG_KBD(("Non-physical Shift + Ctrl pressed, releasing Shift\n"));
}
DEBUG_KBD(("Found scancode translation, scancode=0x%x, modifiers=0x%x\n",
}
}
else
{
if (keymap_loaded)
get_ksname(keysym));
/* not in keymap, try to interpret the raw scancode */
{
/* The modifiers to send for this key should be
obtained from the local state. Currently, only
shift is implemented. */
{
}
}
else
{
}
}
return tr;
}
static RD_BOOL
{
switch (scancode)
{
case SCANCODE_CHAR_LSHIFT:
case SCANCODE_CHAR_RSHIFT:
case SCANCODE_CHAR_LCTRL:
case SCANCODE_CHAR_RCTRL:
case SCANCODE_CHAR_LALT:
case SCANCODE_CHAR_RALT:
case SCANCODE_CHAR_LWIN:
case SCANCODE_CHAR_RWIN:
case SCANCODE_CHAR_NUMLOCK:
return True;
default:
break;
}
return False;
}
void
{
if (tr.seq_keysym == 0)
{
/* Scancode translation */
return;
return;
}
/* Sequence, only on key down */
if (pressed)
{
do
{
DEBUG_KBD(("Handling sequence element, keysym=0x%x\n",
(unsigned int) ptr->seq_keysym));
if (nesting++ > 32)
{
error("Sequence nesting too deep\n");
return;
}
}
while (ptr);
}
}
xkeymap_translate_button(unsigned int button)
{
switch (button)
{
case Button1: /* left */
return MOUSE_FLAG_BUTTON1;
case Button2: /* middle */
return MOUSE_FLAG_BUTTON3;
case Button3: /* right */
return MOUSE_FLAG_BUTTON2;
case Button4: /* wheel up */
return MOUSE_FLAG_BUTTON4;
case Button5: /* wheel down */
return MOUSE_FLAG_BUTTON5;
}
return 0;
}
char *
{
ksname = "NoSymbol";
ksname = "(no name)";
return ksname;
}
void
{
if (is_modifier(scancode))
return;
}
void
{
key_translation dummy = { };
if (is_modifier(scancode))
return;
}
void
{
/* If this key is a modifier, do nothing */
return;
if (!g_numlock_sync)
{
/* NumLock */
{
/* The remote modifier state is not correct */
{
DEBUG_KBD(("Remote NumLock state is incorrect, activating NumLock.\n"));
}
else
{
DEBUG_KBD(("Remote NumLock state is incorrect, deactivating NumLock.\n"));
new_remote_state = 0;
}
}
}
/* Shift. Left shift and right shift are treated as equal; either is fine. */
{
/* The remote modifier state is not correct */
{
/* Needs left shift. Send down. */
}
{
/* Needs right shift. Send down. */
}
else
{
/* Should not use this modifier. Send up for shift currently pressed. */
/* Left shift is down */
else
/* Right shift is down */
}
}
/* AltGr */
{
/* The remote modifier state is not correct */
{
/* Needs this modifier. Send down. */
}
else
{
/* Should not use this modifier. Send up. */
}
}
}
unsigned int
{
#ifdef RDP2VNC
return 0;
#else
unsigned int state;
int dummy;
return state;
#endif
}
ui_get_numlock_state(unsigned int state)
{
uint16 numlock_state = 0;
return numlock_state;
}
void
{
unsigned int state = read_keyboard_state();
/* reset keys */
if (g_numlock_sync)
}
static void
{
#ifdef WITH_DEBUG_KBD
#endif
switch (scancode)
{
case SCANCODE_CHAR_LSHIFT:
break;
case SCANCODE_CHAR_RSHIFT:
break;
case SCANCODE_CHAR_LCTRL:
break;
case SCANCODE_CHAR_RCTRL:
break;
case SCANCODE_CHAR_LALT:
break;
case SCANCODE_CHAR_RALT:
break;
case SCANCODE_CHAR_LWIN:
break;
case SCANCODE_CHAR_RWIN:
break;
case SCANCODE_CHAR_NUMLOCK:
/* KeyReleases for NumLocks are sent immediately. Toggle the
modifier state only on Keypress */
if (pressed && !g_numlock_sync)
{
}
}
#ifdef WITH_DEBUG_KBD
{
DEBUG_KBD(("Before updating modifier_state:0x%x, pressed=0x%x\n",
}
#endif
}
/* Send keyboard input */
void
{
if (scancode & SCANCODE_EXTENDED)
{
DEBUG_KBD(("Sending extended scancode=0x%x, flags=0x%x\n",
scancode & ~SCANCODE_EXTENDED, 0);
}
else
{
}
}