blob: 6068c29c59a8c5e0abd7670479f1280e986acaa0 [file] [log] [blame]
/*
* 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 "keydef.h"
#include "log.h"
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
* All known keys.
*/
const guaclog_keydef known_keys[] = {
{ 0xFE03, "AltGr", "", true },
{ 0xFF08, "Backspace" },
{ 0xFF09, "Tab" },
{ 0xFF0B, "Clear" },
{ 0xFF0D, "Return", "\n" },
{ 0xFF13, "Pause" },
{ 0xFF14, "Scroll" },
{ 0xFF15, "SysReq" },
{ 0xFF1B, "Escape" },
{ 0xFF50, "Home" },
{ 0xFF51, "Left" },
{ 0xFF52, "Up" },
{ 0xFF53, "Right" },
{ 0xFF54, "Down" },
{ 0xFF55, "Page Up" },
{ 0xFF56, "Page Down" },
{ 0xFF57, "End" },
{ 0xFF63, "Insert" },
{ 0xFF65, "Undo" },
{ 0xFF6A, "Help" },
{ 0xFF7F, "Num" },
{ 0xFF80, "Space", " " },
{ 0xFF8D, "Enter", "\n" },
{ 0xFF95, "Home" },
{ 0xFF96, "Left" },
{ 0xFF97, "Up" },
{ 0xFF98, "Right" },
{ 0xFF99, "Down" },
{ 0xFF9A, "Page Up" },
{ 0xFF9B, "Page Down" },
{ 0xFF9C, "End" },
{ 0xFF9E, "Insert" },
{ 0xFFAA, "*", "*" },
{ 0xFFAB, "+", "+" },
{ 0xFFAD, "-", "-" },
{ 0xFFAE, ".", "." },
{ 0xFFAF, "/", "/" },
{ 0xFFB0, "0", "0" },
{ 0xFFB1, "1", "1" },
{ 0xFFB2, "2", "2" },
{ 0xFFB3, "3", "3" },
{ 0xFFB4, "4", "4" },
{ 0xFFB5, "5", "5" },
{ 0xFFB6, "6", "6" },
{ 0xFFB7, "7", "7" },
{ 0xFFB8, "8", "8" },
{ 0xFFB9, "9", "9" },
{ 0xFFBE, "F1" },
{ 0xFFBF, "F2" },
{ 0xFFC0, "F3" },
{ 0xFFC1, "F4" },
{ 0xFFC2, "F5" },
{ 0xFFC3, "F6" },
{ 0xFFC4, "F7" },
{ 0xFFC5, "F8" },
{ 0xFFC6, "F9" },
{ 0xFFC7, "F10" },
{ 0xFFC8, "F11" },
{ 0xFFC9, "F12" },
{ 0xFFCA, "F13" },
{ 0xFFCB, "F14" },
{ 0xFFCC, "F15" },
{ 0xFFCD, "F16" },
{ 0xFFCE, "F17" },
{ 0xFFCF, "F18" },
{ 0xFFD0, "F19" },
{ 0xFFD1, "F20" },
{ 0xFFD2, "F21" },
{ 0xFFD3, "F22" },
{ 0xFFD4, "F23" },
{ 0xFFD5, "F24" },
{ 0xFFE1, "Shift", "", true },
{ 0xFFE2, "Shift", "", true },
{ 0xFFE3, "Ctrl", NULL, true },
{ 0xFFE4, "Ctrl", NULL, true },
{ 0xFFE5, "Caps" },
{ 0xFFE7, "Meta", NULL, true },
{ 0xFFE8, "Meta", NULL, true },
{ 0xFFE9, "Alt", NULL, true },
{ 0xFFEA, "Alt", NULL, true },
{ 0xFFEB, "Super", NULL, true },
{ 0xFFEC, "Super", NULL, true },
{ 0xFFED, "Hyper", NULL, true },
{ 0xFFEE, "Hyper", NULL, true },
{ 0xFFFF, "Delete" }
};
/**
* Comparator for the standard bsearch() function which compares an integer
* keysym against the keysym associated with a guaclog_keydef.
*
* @param key
* The key value being compared against the member. This MUST be the
* keysym value, passed through typecasting to an intptr_t (NOT a pointer
* to the int itself).
*
* @param member
* The member within the known_keys array being compared against the given
* key.
*
* @return
* Zero if the given keysym is equal to that of the given member, a
* positive value if the given keysym is greater than that of the given
* member, or a negative value if the given keysym is less than that of the
* given member.
*/
static int guaclog_keydef_bsearch_compare(const void* key,
const void* member) {
int keysym = (int) ((intptr_t) key);
guaclog_keydef* current = (guaclog_keydef*) member;
/* Compare given keysym to keysym of current member */
return keysym - current->keysym;
}
/**
* Searches through the known_keys array of known keys for the name of the key
* having the given keysym, returning a pointer to the static guaclog_keydef
* within the array if found.
*
* @param keysym
* The X11 keysym of the key.
*
* @return
* A pointer to the static guaclog_keydef associated with the given keysym,
* or NULL if the key could not be found.
*/
static guaclog_keydef* guaclog_get_known_key(int keysym) {
/* Search through known keys for given keysym */
return bsearch((void*) ((intptr_t) keysym),
known_keys, sizeof(known_keys) / sizeof(known_keys[0]),
sizeof(known_keys[0]), guaclog_keydef_bsearch_compare);
}
/**
* Returns a statically-allocated guaclog_keydef representing an unknown key,
* deriving the name of the key from the hexadecimal value of the keysym.
*
* @param keysym
* The X11 keysym of the key.
*
* @return
* A statically-allocated guaclog_keydef representing the key associated
* with the given keysym.
*/
static guaclog_keydef* guaclog_get_unknown_key(int keysym) {
static char unknown_keydef_name[64];
static guaclog_keydef unknown_keydef;
/* Write keysym as hex */
int size = snprintf(unknown_keydef_name, sizeof(unknown_keydef_name),
"0x%X", keysym);
/* Hex string is guaranteed to fit within the provided 64 bytes */
assert(size < sizeof(unknown_keydef_name));
/* Return static key definition */
unknown_keydef.keysym = keysym;
unknown_keydef.name = unknown_keydef_name;
return &unknown_keydef;
}
/**
* Returns a statically-allocated guaclog_keydef representing the key
* associated with the given keysym, deriving the name and value of the key
* using its corresponding Unicode character.
*
* @param keysym
* The X11 keysym of the key.
*
* @return
* A statically-allocated guaclog_keydef representing the key associated
* with the given keysym, or NULL if the given keysym has no corresponding
* Unicode character.
*/
static guaclog_keydef* guaclog_get_unicode_key(int keysym) {
static char unicode_keydef_name[8];
static guaclog_keydef unicode_keydef;
int i;
int mask, bytes;
/* Translate only if keysym maps to Unicode */
if (keysym < 0x00 || (keysym > 0xFF && (keysym | 0xFFFF) != 0x0100FFFF))
return NULL;
int codepoint = keysym & 0xFFFF;
/* Determine size and initial byte mask */
if (codepoint <= 0x007F) {
mask = 0x00;
bytes = 1;
}
else if (codepoint <= 0x7FF) {
mask = 0xC0;
bytes = 2;
}
else {
assert(codepoint <= 0xFFFF);
mask = 0xE0;
bytes = 3;
}
/* Offset buffer by size */
char* key_name = unicode_keydef_name + bytes;
/* Add null terminator */
*(key_name--) = '\0';
/* Add trailing bytes, if any */
for (i=1; i<bytes; i++) {
*(key_name--) = 0x80 | (codepoint & 0x3F);
codepoint >>= 6;
}
/* Set initial byte */
*key_name = mask | codepoint;
/* Return static key definition */
unicode_keydef.keysym = keysym;
unicode_keydef.name = unicode_keydef.value = unicode_keydef_name;
unicode_keydef.modifier = false;
return &unicode_keydef;
}
/**
* Copies the given guaclog_keydef into a newly-allocated guaclog_keydef
* structure. The resulting guaclog_keydef must eventually be freed through a
* call to guaclog_keydef_free().
*
* @param keydef
* The guaclog_keydef to copy.
*
* @return
* A newly-allocated guaclog_keydef structure copied from the given
* guaclog_keydef.
*/
static guaclog_keydef* guaclog_copy_key(guaclog_keydef* keydef) {
guaclog_keydef* copy = malloc(sizeof(guaclog_keydef));
/* Always copy keysym and name */
copy->keysym = keydef->keysym;
copy->name = strdup(keydef->name);
copy->modifier = keydef->modifier;
/* Copy value only if defined */
if (keydef->value != NULL)
copy->value = strdup(keydef->value);
else
copy->value = NULL;
return copy;
}
guaclog_keydef* guaclog_keydef_alloc(int keysym) {
guaclog_keydef* keydef;
/* Check list of known keys first */
keydef = guaclog_get_known_key(keysym);
if (keydef != NULL)
return guaclog_copy_key(keydef);
/* Failing that, attempt to translate straight into a Unicode character */
keydef = guaclog_get_unicode_key(keysym);
if (keydef != NULL)
return guaclog_copy_key(keydef);
/* Key not known */
guaclog_log(GUAC_LOG_DEBUG, "Definition not found for key 0x%X.", keysym);
return guaclog_copy_key(guaclog_get_unknown_key(keysym));
}
void guaclog_keydef_free(guaclog_keydef* keydef) {
/* Ignore NULL keydef */
if (keydef == NULL)
return;
free(keydef->name);
free(keydef->value);
free(keydef);
}