1 /* 2 * Copyright 2013 Ran Benita <ran234 (at) gmail.com> 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 */ 23 24 #include "table.h" 25 #include "utils.h" 26 #include "keysym.h" 27 28 struct xkb_compose_state { 29 int refcnt; 30 enum xkb_compose_state_flags flags; 31 struct xkb_compose_table *table; 32 33 /* 34 * Offsets into xkb_compose_table::nodes. 35 * 36 * They maintain the current and previous position in the trie; see 37 * xkb_compose_state_feed(). 38 * 39 * This is also sufficient for inferring the current status; see 40 * xkb_compose_state_get_status(). 41 */ 42 uint32_t prev_context; 43 uint32_t context; 44 }; 45 46 XKB_EXPORT struct xkb_compose_state * 47 xkb_compose_state_new(struct xkb_compose_table *table, 48 enum xkb_compose_state_flags flags) 49 { 50 struct xkb_compose_state *state; 51 52 state = calloc(1, sizeof(*state)); 53 if (!state) 54 return NULL; 55 56 state->refcnt = 1; 57 state->table = xkb_compose_table_ref(table); 58 59 state->flags = flags; 60 state->prev_context = 0; 61 state->context = 0; 62 63 return state; 64 } 65 66 XKB_EXPORT struct xkb_compose_state * 67 xkb_compose_state_ref(struct xkb_compose_state *state) 68 { 69 state->refcnt++; 70 return state; 71 } 72 73 XKB_EXPORT void 74 xkb_compose_state_unref(struct xkb_compose_state *state) 75 { 76 if (!state || --state->refcnt > 0) 77 return; 78 79 xkb_compose_table_unref(state->table); 80 free(state); 81 } 82 83 XKB_EXPORT struct xkb_compose_table * 84 xkb_compose_state_get_compose_table(struct xkb_compose_state *state) 85 { 86 return state->table; 87 } 88 89 XKB_EXPORT enum xkb_compose_feed_result 90 xkb_compose_state_feed(struct xkb_compose_state *state, xkb_keysym_t keysym) 91 { 92 uint32_t context; 93 const struct compose_node *node; 94 95 /* 96 * Modifiers do not affect the sequence directly. In particular, 97 * they do not cancel a sequence; otherwise it'd be impossible to 98 * have a sequence like <dead_acute><A> (needs Shift in the middle). 99 * 100 * The following test is not really accurate - in order to test if 101 * a key is "modifier key", we really need the keymap, but we don't 102 * have it here. However, this is (approximately) what libX11 does 103 * as well. 104 */ 105 if (xkb_keysym_is_modifier(keysym)) 106 return XKB_COMPOSE_FEED_IGNORED; 107 108 node = &darray_item(state->table->nodes, state->context); 109 110 context = (node->is_leaf ? 0 : node->u.successor); 111 node = &darray_item(state->table->nodes, context); 112 113 while (node->keysym != keysym && node->next != 0) { 114 context = node->next; 115 node = &darray_item(state->table->nodes, context); 116 } 117 118 if (node->keysym != keysym) 119 context = 0; 120 121 state->prev_context = state->context; 122 state->context = context; 123 return XKB_COMPOSE_FEED_ACCEPTED; 124 } 125 126 XKB_EXPORT void 127 xkb_compose_state_reset(struct xkb_compose_state *state) 128 { 129 state->prev_context = 0; 130 state->context = 0; 131 } 132 133 XKB_EXPORT enum xkb_compose_status 134 xkb_compose_state_get_status(struct xkb_compose_state *state) 135 { 136 const struct compose_node *prev_node, *node; 137 138 prev_node = &darray_item(state->table->nodes, state->prev_context); 139 node = &darray_item(state->table->nodes, state->context); 140 141 if (state->context == 0 && !prev_node->is_leaf) 142 return XKB_COMPOSE_CANCELLED; 143 144 if (state->context == 0) 145 return XKB_COMPOSE_NOTHING; 146 147 if (!node->is_leaf) 148 return XKB_COMPOSE_COMPOSING; 149 150 return XKB_COMPOSE_COMPOSED; 151 } 152 153 XKB_EXPORT int 154 xkb_compose_state_get_utf8(struct xkb_compose_state *state, 155 char *buffer, size_t size) 156 { 157 const struct compose_node *node = 158 &darray_item(state->table->nodes, state->context); 159 160 if (!node->is_leaf) 161 goto fail; 162 163 /* If there's no string specified, but only a keysym, try to do the 164 * most helpful thing. */ 165 if (node->u.leaf.utf8 == 0 && node->u.leaf.keysym != XKB_KEY_NoSymbol) { 166 char name[64]; 167 int ret; 168 169 ret = xkb_keysym_to_utf8(node->u.leaf.keysym, name, sizeof(name)); 170 if (ret < 0 || ret == 0) { 171 /* ret < 0 is impossible. 172 * ret == 0 means the keysym has no string representation. */ 173 goto fail; 174 } 175 176 return snprintf(buffer, size, "%s", name); 177 } 178 179 return snprintf(buffer, size, "%s", 180 &darray_item(state->table->utf8, node->u.leaf.utf8)); 181 182 fail: 183 if (size > 0) 184 buffer[0] = '\0'; 185 return 0; 186 } 187 188 XKB_EXPORT xkb_keysym_t 189 xkb_compose_state_get_one_sym(struct xkb_compose_state *state) 190 { 191 const struct compose_node *node = 192 &darray_item(state->table->nodes, state->context); 193 if (!node->is_leaf) 194 return XKB_KEY_NoSymbol; 195 return node->u.leaf.keysym; 196 } 197