From 4fcbbe406aa48688091166e55f2aec57e1db4bbf Mon Sep 17 00:00:00 2001 From: sf-exg Date: Tue, 13 Oct 2015 08:10:43 +0000 Subject: Update copyright years. --- src/keyboard.C | 301 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 301 insertions(+) create mode 100644 src/keyboard.C diff --git a/src/keyboard.C b/src/keyboard.C new file mode 100644 index 0000000..ca779f1 --- /dev/null +++ b/src/keyboard.C @@ -0,0 +1,301 @@ +/*----------------------------------------------------------------------* + * File: keyboard.C + *----------------------------------------------------------------------* + * + * All portions of code are copyright by their respective author/s. + * Copyright (c) 2005 WU Fengguang + * Copyright (c) 2005-2006 Marc Lehmann + * Copyright (c) 2015 Emanuele Giaquinta + * + * This program is free software; you can redistribute it and/or modify + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + *----------------------------------------------------------------------*/ + +#include "../config.h" +#include "rxvt.h" + +#ifdef KEYSYM_RESOURCE + +#include + +#include "rxvtperl.h" +#include "keyboard.h" + +/* an intro to the data structure: + * + * vector keymap[] is grouped. + * + * inside each group, elements are sorted by the criteria given by compare_priority(). + * the lookup of keysym is done in two steps: + * 1) locate the group corresponds to the keysym; + * 2) do a linear search inside the group. + * + * array hash[] effectively defines a map from a keysym to a group in keymap[]. + * + * each group has its address(the index of first group element in keymap[]), + * which is computed and stored in hash[]. + * hash[] stores the addresses in the form of: + * index: 0 I1 I2 I3 In + * value: 0...0, A1...A1, A2...A2, A3...A3, ..., An...An + * where + * A1 = 0; + * Ai+1 = N1 + N2 + ... + Ni. + * it is computed from hash_bucket_size[]: + * index: 0 I1 I2 I3 In + * value: 0...0, N1, 0...0, N2, 0...0, N3, ..., Nn, 0...0 + * 0...0, 0.......0, N1.....N1, N1+N2...N1+N2, ... (the computation of hash[]) + * or we can say + * hash_bucket_size[Ii] = Ni; hash_bucket_size[elsewhere] = 0, + * where + * set {I1, I2, ..., In} = { hashkey of keymap[0]->keysym, ..., keymap[keymap.size-1]->keysym } + * where hashkey of keymap[i]->keysym = keymap[i]->keysym & KEYSYM_HASH_MASK + * n(the number of groups) = the number of non-zero member of hash_bucket_size[]; + * Ni(the size of group i) = hash_bucket_size[Ii]. + */ + +// return: priority_of_a - priority_of_b +static int +compare_priority (keysym_t *a, keysym_t *b) +{ + // (the more '1's in state; the less range): the greater priority + int ca = ecb_popcount32 (a->state /* & OtherModMask */); + int cb = ecb_popcount32 (b->state /* & OtherModMask */); + + return ca - cb; +} + +//////////////////////////////////////////////////////////////////////////////// +keyboard_manager::keyboard_manager () +{ + keymap.reserve (256); + hash [0] = 1; // hash[0] != 0 indicates uninitialized data +} + +keyboard_manager::~keyboard_manager () +{ + for (unsigned int i = 0; i < keymap.size (); ++i) + { + free (keymap [i]->str); + delete keymap [i]; + } +} + +void +keyboard_manager::unregister_action (KeySym keysym, unsigned int state) +{ + for (unsigned int i = 0; i < keymap.size (); ++i) + if (keymap [i]->keysym == keysym + && keymap [i]->state == state) + { + free (keymap [i]->str); + delete keymap [i]; + + if (i < keymap.size () - 1) + keymap [i] = keymap [keymap.size () - 1]; + keymap.pop_back (); + + break; + } +} + +void +keyboard_manager::register_action (KeySym keysym, unsigned int state, const wchar_t *ws) +{ + char *action = rxvt_wcstoutf8 (ws); + + keysym_t *key = new keysym_t; + + key->keysym = keysym; + key->state = state; + key->str = action; + key->type = keysym_t::STRING; + + if (strncmp (action, "builtin:", 8) == 0) + key->type = keysym_t::BUILTIN; + else if (strncmp (action, "builtin-string:", 15) == 0) + key->type = keysym_t::BUILTIN_STRING; + + unregister_action (keysym, state); + + if (keymap.size () == keymap.capacity ()) + keymap.reserve (keymap.size () * 2); + + keymap.push_back (key); + hash[0] = 3; +} + +keysym_t * +keyboard_manager::lookup_keysym (rxvt_term *term, KeySym keysym, unsigned int state) +{ + assert (("register_done() need to be called", hash[0] == 0)); + + state &= OtherModMask; // mask out uninteresting modifiers + + if (state & term->ModMetaMask) state |= MetaMask; + if (state & term->ModNumLockMask) state |= NumLockMask; + if (state & term->ModLevel3Mask) state |= Level3Mask; + + if (!!(term->priv_modes & PrivMode_aplKP) != !!(state & ShiftMask)) + state |= AppKeypadMask; + + int index = find_keysym (keysym, state); + + return index >= 0 ? keymap [index] : 0; +} + +bool +keyboard_manager::dispatch (rxvt_term *term, KeySym keysym, unsigned int state, const char *kbuf, int len) +{ + keysym_t *key = lookup_keysym (term, keysym, state); + + if (key) + { + if (key->type == keysym_t::BUILTIN_STRING) + { + term->tt_write_user_input (kbuf, len); + return true; + } + else if (key->type != keysym_t::BUILTIN) + { + wchar_t *ws = rxvt_utf8towcs (key->str); + char *str = rxvt_wcstombs (ws); + // TODO: do (some) translations, unescaping etc, here (allow \u escape etc.) + free (ws); + + if (char *colon = strchr (str, ':')) + { + if (strncmp (str, "command:", 8) == 0) + term->cmdbuf_append (str + 8, strlen (str) - 8); + else if (strncmp (str, "string:", 7) == 0) + term->tt_write_user_input (colon + 1, strlen (colon + 1)); + else if (strncmp (str, "perl:", 5) == 0) + HOOK_INVOKE ((term, HOOK_USER_COMMAND, DT_STR, colon + 1, DT_END)); + else + HOOK_INVOKE ((term, HOOK_ACTION, DT_STR_LEN, str, colon - str, DT_STR, colon + 1, DT_INT, 0, DT_STR_LEN, kbuf, len, DT_END)); + } + else + term->tt_write_user_input (str, strlen (str)); + + free (str); + + return true; + } + } + + return false; +} + +void +keyboard_manager::register_done () +{ + unsigned int i, index, hashkey; + uint16_t hash_bucket_size[KEYSYM_HASH_BUCKETS]; // size of each bucket + + memset (hash_bucket_size, 0, sizeof (hash_bucket_size)); + + // determine hash bucket size + for (i = 0; i < keymap.size (); ++i) + { + hashkey = keymap [i]->keysym & KEYSYM_HASH_MASK; + ++hash_bucket_size [hashkey]; + } + + // now we know the size of each bucket + // compute the index of each bucket + for (index = 0, i = 0; i < KEYSYM_HASH_BUCKETS; ++i) + { + hash [i] = index; + index += hash_bucket_size [i]; + } + + // and allocate just enough space + simplevec sorted_keymap (index, 0); + + memset (hash_bucket_size, 0, sizeof (hash_bucket_size)); + + // fill in sorted_keymap + // it is sorted in each bucket + for (i = 0; i < keymap.size (); ++i) + { + hashkey = keymap [i]->keysym & KEYSYM_HASH_MASK; + + index = hash [hashkey] + hash_bucket_size [hashkey]; + + while (index > hash [hashkey] + && compare_priority (keymap [i], sorted_keymap [index - 1]) > 0) + { + sorted_keymap [index] = sorted_keymap [index - 1]; + --index; + } + + sorted_keymap [index] = keymap [i]; + ++hash_bucket_size [hashkey]; + } + + keymap.swap (sorted_keymap); + +#ifndef NDEBUG + // check for invariants + for (i = 0; i < KEYSYM_HASH_BUCKETS; ++i) + { + index = hash[i]; + for (int j = 0; j < hash_bucket_size [i]; ++j) + { + assert (i == (keymap [index + j]->keysym & KEYSYM_HASH_MASK)); + + if (j) + assert (compare_priority (keymap [index + j - 1], + keymap [index + j]) >= 0); + } + } + + // this should be able to detect most possible bugs + for (i = 0; i < sorted_keymap.size (); ++i) + { + keysym_t *a = sorted_keymap[i]; + int index = find_keysym (a->keysym, a->state); + + assert (index >= 0); + keysym_t *b = keymap [index]; + assert (i == index // the normally expected result + || a->keysym == b->keysym + && compare_priority (a, b) <= 0); // is effectively the same or a closer match + } +#endif +} + +int +keyboard_manager::find_keysym (KeySym keysym, unsigned int state) +{ + int hashkey = keysym & KEYSYM_HASH_MASK; + unsigned int index = hash [hashkey]; + unsigned int end = hashkey < KEYSYM_HASH_BUCKETS - 1 + ? hash [hashkey + 1] + : keymap.size (); + + for (; index < end; ++index) + { + keysym_t *key = keymap [index]; + + if (key->keysym == keysym + // match only the specified bits in state and ignore others + && (key->state & state) == key->state) + return index; + } + + return -1; +} + +#endif /* KEYSYM_RESOURCE */ +// vim:et:ts=2:sw=2 -- cgit v1.2.3