4 cellwriter -- a character recognition input method
5 Copyright (C) 2007 Michael Levin <risujin@risujin.org>
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #include "hildon-im-ui.h"
31 #include <X11/keysym.h>
32 #include <X11/extensions/XTest.h>
35 /* Define this to always overwrite an unused KeyCode to send any KeySym */
36 /* #define ALWAYS_OVERWRITE */
38 /* Define this to print key events without actually generating them */
39 /* #define DEBUG_KEY_EVENTS */
46 KEY_TAKEN = 0, /* Has KeySyms, cannot be overwritten */
47 KEY_BAD, /* Manually marked as unusable */
48 KEY_USABLE, /* Has no KeySyms, can be overwritten */
49 KEY_ALLOCATED, /* Normally usable, but currently allocated */
50 /* Values greater than this represent multiple allocations */
53 extern HildonIMUI *ui;
55 static char usable[256], pressed[256];
56 static int key_min, key_max, key_offset, key_codes;
57 //static KeySym *keysyms = NULL;
58 //static XModifierKeymap *modmap = NULL;
60 /* Bad keycodes: Despite having no KeySym entries, certain KeyCodes will
61 generate special KeySyms even if their KeySym entries have been overwritten.
62 For instance, KeyCode 204 attempts to eject the CD-ROM even if there is no
63 CD-ROM device present! KeyCode 229 will launch GNOME file search even if
64 there is no search button on the physical keyboard. There is no programatic
65 way around this but to keep a list of commonly used "bad" KeyCodes. */
67 void bad_keycodes_read(void)
72 while (!profile_sync_int(&keycode)) {
73 if (keycode < key_min || keycode > key_max) {
74 g_warning("Cannot block bad keycode %d, out of range",
78 usable[keycode] = KEY_BAD;
83 void bad_keycodes_write(void)
88 profile_write("bad_keycodes");
89 for (i = key_min; i < key_max; i++)
90 if (usable[i] == KEY_BAD)
91 profile_write(va(" %d", i));
96 static void press_keycode(KeyCode k)
97 /* Called from various places to generate a key-down event */
99 //if (k >= key_min && k <= key_max)
100 // XTestFakeKeyEvent(GDK_DISPLAY(), k, True, 1);
103 static void release_keycode(KeyCode k)
104 /* Called from various places to generate a key-up event */
106 // if (k >= key_min && k <= key_max)
107 // XTestFakeKeyEvent(GDK_DISPLAY(), k, False, 1);
110 static void type_keycode(KeyCode k)
111 /* Key-down + key-up */
117 static void setup_usable(void)
118 /* Find unused slots in the key mapping */
123 /* Find all free keys
124 memset(usable, 0, sizeof (usable));
125 for (i = key_min, found = 0; i <= key_max; i++) {
128 for (j = 0; j < key_codes &&
129 keysyms[(i - key_min) * key_codes + j] == NoSymbol; j++);
131 usable[i] = KEY_TAKEN;
134 usable[i] = KEY_USABLE;
139 /* If we haven't found a usable key, it's probably because we have
140 already ran once ad used them all up without setting them back
142 usable[key_max - key_min - 1] = KEY_USABLE;
143 g_warning("Found no usable KeyCodes, restart the X server!");
145 g_debug("Found %d usable KeyCodes", found);
149 static void cleanup_usable(void)
150 /* Clear all the usable KeyCodes or we won't have any when we run again! */
153 int i, bad, unused = 0, freed;
155 for (i = 0, freed = 0, bad = 0; i <= key_max; i++)
156 if (usable[i] >= KEY_USABLE) {
157 int j, kc_used = FALSE;
159 for (j = 0; j < key_codes; j++) {
160 int index = (i - key_min) * key_codes + j;
162 if (keysyms[index] != NoSymbol)
164 keysyms[index] = NoSymbol;
170 } else if (usable[i] == KEY_BAD)
173 XChangeKeyboardMapping(GDK_DISPLAY(), key_min, key_codes,
174 keysyms, key_max - key_min + 1);
175 XFlush(GDK_DISPLAY());
177 g_debug("Free'd %d KeyCode(s), %d unused, %d marked bad",
182 static void release_held_keys(void)
183 /* Release all held keys that were not pressed by us */
189 XQueryKeymap(GDK_DISPLAY(), keys);
190 for (i = 0; i < 32; i++) {
193 for (j = 0; j < 8; j++) {
197 if (keys[i] & (1 << j) && !pressed[keycode]) {
198 g_debug("Released held KeyCode %d", keycode);
199 release_keycode(keycode);
210 int key_overwrites = 0, key_recycles = 0,
211 key_shifted = 0, key_num_locked = FALSE, key_caps_locked = FALSE,
212 key_disable_overwrite = FALSE;
214 static int alt_mask, num_lock_mask, meta_mask;
215 static KeyEvent ke_shift, ke_enter, ke_num_lock, ke_caps_lock;
217 static void reset_keyboard(void)
218 /* In order to reliably predict key event behavior we need to be able to
219 reset the keyboard modifier and pressed state */
223 int root_x, root_y, win_x, win_y;
227 XQueryPointer(GDK_DISPLAY(), GDK_WINDOW_XWINDOW(GDK_ROOT_PARENT()),
228 &root, &child, &root_x, &root_y, &win_x, &win_y, &mask);
230 type_keycode(ke_caps_lock.keycode);
231 if (mask & num_lock_mask)
232 type_keycode(ke_num_lock.keycode);
236 static int is_modifier(unsigned int keysym)
237 /* Returns TRUE for KeySyms that are tracked internally */
250 static void key_event_allocate(KeyEvent *key_event, unsigned int keysym)
251 /* Either finds the KeyCode associated with the given keysym or overwrites
252 a usable one to generate it */
259 key_event->keycode = -1;
260 key_event->keysym = 0;
264 /* First see if our KeySym is already in the mapping
265 key_event->shift = FALSE;
266 #ifndef ALWAYS_OVERWRITE
267 for (i = 0; i <= key_max - key_min; i++) {
268 if (keysyms[i * key_codes + 1] == keysym)
269 key_event->shift = TRUE;
270 if (keysyms[i * key_codes] == keysym || key_event->shift) {
271 key_event->keycode = key_min + i;
274 /* Bump the allocation count if this is an
276 if (usable[key_event->keycode] >= KEY_USABLE)
277 usable[key_event->keycode]++;
284 /* Key overwrites may be disabled, in which case we're out of luck
285 if (key_disable_overwrite) {
286 key_event->keycode = -1;
287 key_event->keysym = 0;
288 g_warning("Not allowed to overwrite KeyCode for %s",
289 XKeysymToString(keysym));
293 /* If not, find a usable KeyCode in the mapping
294 for (start = key_offset++; ; key_offset++) {
295 if (key_offset > key_max - key_min)
297 if (usable[key_min + key_offset] == KEY_USABLE &&
298 !pressed[key_min + key_offset])
301 /* If we can't find one, invalidate the event
302 if (key_offset == start) {
303 key_event->keycode = -1;
304 key_event->keysym = 0;
305 g_warning("Failed to allocate KeyCode for %s",
306 XKeysymToString(keysym));
311 key_event->keycode = key_min + key_offset;
312 usable[key_event->keycode] = KEY_ALLOCATED;
314 /* Modify the slot to hold our character
315 keysyms[key_offset * key_codes] = keysym;
316 keysyms[key_offset * key_codes + 1] = keysym;
317 XChangeKeyboardMapping(GDK_DISPLAY(), key_event->keycode, key_codes,
318 keysyms + key_offset * key_codes, 1);
319 XSync(GDK_DISPLAY(), False);
321 g_debug("Overwrote KeyCode %d for %s", key_event->keycode,
322 XKeysymToString(keysym));
326 void key_event_new(KeyEvent *key_event, unsigned int keysym)
327 /* Filters locks and shifts but allocates other keys normally */
329 key_event->keysym = keysym;
331 if (is_modifier(keysym))
333 key_event_allocate(key_event, keysym);
337 void key_event_free(KeyEvent *key_event)
338 /* Release resources associated with and invalidate a key event */
341 if (key_event->keycode >= key_min && key_event->keycode <= key_max &&
342 usable[key_event->keycode] == KEY_ALLOCATED)
343 usable[key_event->keycode] = KEY_USABLE;
344 key_event->keycode = -1;
345 key_event->keysym = 0;
349 void key_event_press(KeyEvent *key_event)
350 /* Press the KeyCode specified in the event */
353 /* Track modifiers without actually using them
354 if (key_event->keysym == XK_Shift_L ||
355 key_event->keysym == XK_Shift_R) {
358 } else if (key_event->keysym == XK_Caps_Lock) {
359 key_caps_locked = !key_caps_locked;
361 } else if (key_event->keysym == XK_Num_Lock) {
362 key_num_locked = !key_num_locked;
367 if (key_event->keycode < key_min || key_event->keycode > key_max)
370 /* If this KeyCode is already pressed, something is wrong
371 if (pressed[key_event->keycode]) {
372 g_debug("KeyCode %d is already pressed", key_event->keycode);
376 /* Keep track of what KeyCodes we pressed down
377 pressed[key_event->keycode] = TRUE;
380 if (key_event->shift)
381 press_keycode(ke_shift.keycode);
382 press_keycode(key_event->keycode);
383 XSync(GDK_DISPLAY(), False);
387 void key_event_release(KeyEvent *key_event)
390 /* Track modifiers without actually using them
391 if (key_event->keysym == XK_Shift_L ||
392 key_event->keysym == XK_Shift_R) {
398 if (key_event->keycode < key_min || key_event->keycode > key_max)
401 /* Keep track of what KeyCodes are pressed because of us
402 pressed[key_event->keycode] = FALSE;
404 /* Release our keycode
405 release_keycode(key_event->keycode);
406 if (key_event->shift)
407 release_keycode(ke_shift.keycode);
408 XSync(GDK_DISPLAY(), False);
412 void unicode_to_utf8(unsigned int code, char *out){
414 unsigned char *c = (unsigned char*)&code;
418 }else if(code < 0x800){
419 out[0] = 0xC0 | (c[1] << 2) | (c[0] >> 6);
420 out[1] = 0x80 | (0x3F & c[0]);
422 }else *out = 0; // todo
425 void key_event_send_char(int unichar)
429 unicode_to_utf8((unsigned int)unichar, string);
433 hildon_im_ui_send_communication_message(ui, HILDON_IM_CONTEXT_HANDLE_ENTER);
435 hildon_im_ui_send_utf8(ui, string);
442 /* Get the keysym for the unichar (may be unsupported)
443 keysym = XStringToKeysym(va("U%04X", unichar));
445 g_warning("XStringToKeysym failed to get Keysym for '%C'",
450 key_event_new(&key_event, keysym);
451 key_event_press(&key_event);
452 key_event_release(&key_event);
453 key_event_free(&key_event);
457 void key_event_send_enter()
459 //type_keycode(ke_enter.keycode);
465 /* Clear the array that keeps track of our pressed keys */
466 memset(pressed, 0, sizeof (pressed));
471 void key_event_cleanup(void)