| /* |
| * 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); |
| |
| } |
| |