1 /* This file is part of SDL_him - SDL Hildon Input Method addon
2 * Copyright (C) 2010 Javier S. Pedro
3 * Copyright (C) 2005-2010 Nokia Corporation and/or its subsidiary(-ies).
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 3 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA or see <http://www.gnu.org/licenses/>.
27 #include <X11/Xutil.h>
28 #include <X11/Xatom.h>
29 #include <X11/XKBlib.h>
30 #include <X11/extensions/XKBstr.h>
32 #include <SDL_syswm.h>
36 /* This pulls Gtk+ headers, but we do not link with Gtk+, Gdk or GLib */
38 #include <hildon-im-protocol.h>
40 /* Specific Maemo configuration */
41 #define COMPOSE_KEY XK_Multi_key
42 #define LEVEL_KEY XK_ISO_Level3_Shift
43 #define LevelMask Mod5Mask
45 #define NUMERIC_LEVEL 2
46 #define LOCKABLE_LEVEL 4
53 static Display *display;
55 static Uint8 event_base;
57 static Bool initialized = False;
58 static Bool enabled = False;
60 static HildonIMOptionMask options = 0;
61 static HildonIMTrigger trigger_mode = HILDON_IM_TRIGGER_NONE;
62 static HildonGtkInputMode input_mode = HILDON_GTK_INPUT_MODE_FULL;
63 static HildonIMCommitMode commit_mode = HILDON_IM_COMMIT_DIRECT;
64 static HildonIMInternalModifierMask mod_mask = 0;
66 static Uint32 combining_char = 0;
67 static KeySym last_keysym = XK_VoidSymbol;
68 static Bool auto_upper = False;
69 static Bool space_after_commit = False;
71 static dstring_t preedit_buffer = { 0, 0, 0 };
73 /** Gets the window id of the Hildon IM GUI.
74 We'll use it as target for our messages.
76 static Window find_hildon_im_window()
78 const Window rootWindow = RootWindow(display, DefaultScreen(display));
83 unsigned long nitems, bytes_after;
84 unsigned char *prop = NULL;
86 /* Use RootWindow's HILDON_IM_WINDOW property to find HIM's window */
87 status = XGetWindowProperty(display, rootWindow,
88 ATOM(HILDON_IM_WINDOW), 0, 1, False, XA_WINDOW,
89 &actual_type, &actual_format,
90 &nitems, &bytes_after, &prop);
92 if (status != Success || actual_type != XA_WINDOW ||
93 actual_format != HILDON_IM_WINDOW_ID_FORMAT || nitems != 1) {
94 SDL_SetError("Failed to get Hildon IM Window id");
98 window = *(Window*)prop;
104 /** Gets the window id for the current shown SDL window. */
105 static Window find_app_window()
108 SDL_Surface *screen = SDL_GetVideoSurface();
110 SDL_VERSION(&info.version);
111 if (SDL_GetWMInfo(&info) != 1) {
117 if (screen->flags & SDL_FULLSCREEN)
118 return info.info.x11.fswindow;
120 return info.info.x11.wmwindow;
123 /* This comes mostly straight from Qt Maemo.
124 Not sure what's exactly doing, or wheter I can replace it with XLookupKeysym */
125 static KeySym get_keysym_for_level(KeyCode keycode, int level)
127 XkbDescPtr xkb = XkbGetMap(display, XkbAllClientInfoMask, XkbUseCoreKbd);
131 KeySym keysym = XkbKeySymEntry(xkb, keycode, level, 0);
133 /* check that the keysym is not repeated in levels 0 and requested */
134 KeySym keysym_test = XkbKeySymEntry(xkb, keycode, 0, 1);
135 if (keysym == keysym_test) {
142 static void send_input_mode()
144 #if MAEMO_VERSION == 5
145 Window im_window = find_hildon_im_window();
146 Window app_window = find_app_window();
147 if (!app_window || !im_window) return;
150 e.xclient.type = ClientMessage;
151 e.xclient.window = im_window;
152 e.xclient.message_type = ATOM(HILDON_IM_INPUT_MODE);
153 e.xclient.format = HILDON_IM_INPUT_MODE_FORMAT;
155 HildonIMInputModeMessage *msg = (HildonIMInputModeMessage*)(&e.xclient.data);
157 msg->input_mode = input_mode;
158 msg->default_input_mode = HILDON_GTK_INPUT_MODE_FULL;
160 XSendEvent(display, im_window, False, 0, &e);
162 #elif MAEMO_VERSION == 4
163 /* On Diablo, input_mode is sent with the activation message. */
164 /* See send_hildon_command() */
166 #error Missing send_input_mode implementation
170 /** Sends a client message with the specified command to the IM window */
171 static void send_hildon_command(HildonIMCommand cmd)
173 Window im_window = find_hildon_im_window();
174 Window app_window = find_app_window();
175 if (!app_window || !im_window) return;
178 e.xclient.type = ClientMessage;
179 e.xclient.window = im_window;
180 e.xclient.message_type = ATOM(HILDON_IM_ACTIVATE);
181 e.xclient.format = HILDON_IM_ACTIVATE_FORMAT;
183 HildonIMActivateMessage *msg = (HildonIMActivateMessage*)(&e.xclient.data);
185 /* input_window is the window of the focused widget (text box, etc.) */
186 /* SDL doesn't do widgets, so we send the toplevel window */
187 msg->input_window = app_window;
188 /* app_window is the toplevel window containing input_window */
189 msg->app_window = app_window;
190 #if MAEMO_VERSION == 4
191 msg->input_mode = input_mode;
194 msg->trigger = trigger_mode;
196 XSendEvent(display, im_window, False, 0, &e);
200 /** Forward a key event to the hildon input method window.
201 Why'd we need such a thing? Well, to get the input method GUI to be
204 static void send_hildon_key_event
205 (Bool pressed, unsigned state, KeySym keysym, KeyCode keycode)
207 Window im_window = find_hildon_im_window();
208 Window app_window = find_app_window();
209 if (!app_window || !im_window) return;
212 e.xclient.type = ClientMessage;
213 e.xclient.window = im_window;
214 e.xclient.message_type = ATOM(HILDON_IM_KEY_EVENT);
215 e.xclient.format = HILDON_IM_KEY_EVENT_FORMAT;
217 HildonIMKeyEventMessage *msg = (HildonIMKeyEventMessage*)(&e.xclient.data);
219 msg->input_window = app_window;
221 /* This protocol is way too gnomish. */
223 msg->type = GDK_KEY_PRESS;
225 msg->type = GDK_KEY_RELEASE;
228 msg->keyval = keysym;
229 msg->hardware_keycode = keycode;
231 XSendEvent(display, im_window, False, 0, &e);
235 static void send_surrounding_content(const char * text)
237 if (!text) return; /* no need to send this if text is empty */
239 Window im_window = find_hildon_im_window();
240 Window app_window = find_app_window();
241 if (!app_window || !im_window) return;
243 const unsigned long total_len = strlen(text);
244 Bool first_part = True;
245 unsigned long offset = 0;
247 while (first_part || (offset < total_len)) {
249 e.xclient.type = ClientMessage;
250 e.xclient.window = im_window;
251 e.xclient.message_type = ATOM(HILDON_IM_SURROUNDING_CONTENT);
252 e.xclient.format = HILDON_IM_SURROUNDING_CONTENT_FORMAT;
254 unsigned long len = total_len - offset;
255 if (len > HILDON_IM_CLIENT_MESSAGE_BUFFER_SIZE - 1) {
256 /* the X11 event is limited in size. */
257 len = HILDON_IM_CLIENT_MESSAGE_BUFFER_SIZE - 1;
260 HildonIMSurroundingContentMessage *msg = (HildonIMSurroundingContentMessage*)(&e.xclient.data);
261 memcpy(msg->surrounding, text + offset, len);
264 msg->msg_flag = HILDON_IM_MSG_START;
265 } else if (offset == total_len) {
266 msg->msg_flag = HILDON_IM_MSG_END;
268 msg->msg_flag = HILDON_IM_MSG_CONTINUE;
271 XSendEvent(display, im_window, False, 0, &e);
280 static void send_surrounding_header(unsigned int cursor_offset)
282 Window im_window = find_hildon_im_window();
283 Window app_window = find_app_window();
284 if (!app_window || !im_window) return;
287 e.xclient.type = ClientMessage;
288 e.xclient.window = im_window;
289 e.xclient.message_type = ATOM(HILDON_IM_SURROUNDING);
290 e.xclient.format = HILDON_IM_SURROUNDING_FORMAT;
292 HildonIMSurroundingMessage *msg = (HildonIMSurroundingMessage*)(&e.xclient.data);
294 msg->commit_mode = commit_mode;
295 msg->cursor_offset = cursor_offset;
297 XSendEvent(display, im_window, False, 0, &e);
301 static void send_selection_reply(Bool has_selection)
303 Window im_window = find_hildon_im_window();
304 if (!im_window) return;
307 e.xclient.type = ClientMessage;
308 e.xclient.window = im_window;
309 e.xclient.message_type = ATOM(HILDON_IM_CLIPBOARD_SELECTION_REPLY);
310 e.xclient.format = HILDON_IM_CLIPBOARD_SELECTION_REPLY_FORMAT;
311 e.xclient.data.l[0] = has_selection;
313 XSendEvent(display, im_window, False, 0, &e);
317 static void send_sdl_text_event(Uint8 code, const char * text)
319 HIM_TextInputEvent event;
320 event.type = event_base;
323 /* TODO: The SDL event size is limited. Send more than one. */
324 strncpy(event.text, text, HIM_TEXTINPUTEVENT_TEXT_SIZE);
325 event.text[HIM_TEXTINPUTEVENT_TEXT_SIZE] = '\0';
327 SDL_PushEvent((SDL_Event*)&event);
330 static void send_sdl_request_surrounding_event(Bool full)
332 HIM_RequestSurroundingEvent event;
333 event.type = event_base;
334 event.code = HIM_REQUESTSURROUNDINGEVENT;
335 event.full = full ? 1 : 0;
337 SDL_PushEvent((SDL_Event*)&event);
340 static void send_sdl_clipboard_event(HIM_ClipboardAction action)
342 HIM_ClipboardEvent event;
343 event.type = event_base;
344 event.code = HIM_CLIPBOARDEVENT;
345 event.action = action;
347 SDL_PushEvent((SDL_Event*)&event);
350 static void set_sdl_cursor_location(Bool is_relative, int offset)
352 HIM_CursorMoveEvent event;
353 event.type = event_base;
354 event.code = HIM_CURSORMOVEEVENT;
355 event.relative = is_relative ? 1 : 0;
356 event.offset = offset;
358 SDL_PushEvent((SDL_Event*)&event);
361 static void commit_preedit_buffer()
363 if (space_after_commit) {
364 ds_append(&preedit_buffer, " ");
366 if (preedit_buffer.len > 0) {
367 send_sdl_text_event(HIM_TEXTINPUTEVENT, preedit_buffer.str);
368 ds_clear(&preedit_buffer);
372 static void cancel_preedit()
374 ds_clear(&preedit_buffer);
377 static void check_sentence_start()
379 if (!enabled) return;
381 if ((input_mode & (HILDON_GTK_INPUT_MODE_ALPHA |
382 HILDON_GTK_INPUT_MODE_AUTOCAP)) !=
383 (HILDON_GTK_INPUT_MODE_ALPHA |
384 HILDON_GTK_INPUT_MODE_AUTOCAP)) {
385 /* If autocap is off, but the mode contains alpha, send autocap message.
386 The important part is that when entering a numerical entry the autocap
387 is not defined, and the plugin sets the mode appropriate for the language */
388 if (input_mode & HILDON_GTK_INPUT_MODE_ALPHA) {
390 send_hildon_command(HILDON_IM_LOW);
394 } else if (input_mode & HILDON_GTK_INPUT_MODE_INVISIBLE) {
395 /* No autocap for passwords */
397 send_hildon_command(HILDON_IM_LOW);
400 // TODO Analyze surrounding for autocapitalization
404 @param flag HILDON_IM_MSG_START, HILDON_IM_MSG_CONTINUE, HILDON_IM_MSG_END
405 @param str the Utf8 string
407 static void insert_utf8(int flag, const char * str)
409 if (commit_mode == HILDON_IM_COMMIT_BUFFERED) {
410 if (flag == HILDON_IM_MSG_START) {
411 ds_set(&preedit_buffer, str);
413 ds_append(&preedit_buffer, str);
415 send_sdl_text_event(HIM_TEXTEDITINGEVENT, preedit_buffer.str);
416 } else { /* Direct, Redirect, and Proxy modes. */
417 send_sdl_text_event(HIM_TEXTINPUTEVENT, str);
421 static void insert_special_key(SDLKey key)
423 HIM_SpecialKeyEvent event = { event_base, HIM_SPECIALKEYEVENT, key };
424 SDL_PushEvent((SDL_Event*)&event);
427 static void set_commit_mode(HildonIMCommitMode new_mode)
429 ds_clear(&preedit_buffer);
430 commit_mode = new_mode;
433 static void set_mask_state(HildonIMInternalModifierMask *mask,
434 HildonIMInternalModifierMask lock_mask,
435 HildonIMInternalModifierMask sticky_mask,
436 Bool was_press_and_release)
438 /* Locking Fn is disabled in TELE and NUMERIC */
439 if (!(input_mode & HILDON_GTK_INPUT_MODE_ALPHA) &&
440 !(input_mode & HILDON_GTK_INPUT_MODE_HEXA) &&
441 ((input_mode & HILDON_GTK_INPUT_MODE_TELE) ||
442 (input_mode & HILDON_GTK_INPUT_MODE_NUMERIC))) {
443 if (*mask & lock_mask) {
444 /* already locked, remove lock and set it to sticky */
445 *mask &= ~(lock_mask | sticky_mask);
446 *mask |= sticky_mask;
447 } else if (*mask & sticky_mask) {
448 /* the key is already sticky, it's fine */
449 } else if (was_press_and_release) {
450 /* Pressing the key for the first time stickies the key for one character,
451 * but only if no characters were entered while holding the key down */
452 *mask |= sticky_mask;
458 if (*mask & lock_mask) {
459 /* Pressing the key while already locked clears the state */
460 *mask &= ~(lock_mask | sticky_mask);
462 #if MAEMO_VERSION == 5
463 if (lock_mask & HILDON_IM_SHIFT_LOCK_MASK) {
464 send_hildon_command(HILDON_IM_SHIFT_UNLOCKED);
465 } else if (lock_mask & HILDON_IM_LEVEL_LOCK_MASK) {
466 send_hildon_command(HILDON_IM_MOD_UNLOCKED);
470 } else if (*mask & sticky_mask) {
471 /* When the key is already sticky, a second press locks the key */
474 #if MAEMO_VERSION == 5
475 if (lock_mask & HILDON_IM_SHIFT_LOCK_MASK) {
476 send_hildon_command(HILDON_IM_SHIFT_LOCKED);
477 } else if (lock_mask & HILDON_IM_LEVEL_LOCK_MASK) {
478 send_hildon_command(HILDON_IM_MOD_LOCKED);
482 } else if (was_press_and_release) {
483 /* Pressing the key for the first time stickies the key for one character,
484 * but only if no characters were entered while holding the key down */
485 *mask |= sticky_mask;
489 static int handle_x_event(const XEvent *e)
491 if (e->type == ClientMessage) {
492 /* Receive messages from HIM */
493 if (e->xclient.message_type == ATOM(HILDON_IM_INSERT_UTF8)) {
494 /* Text insertion message */
495 HildonIMInsertUtf8Message *msg =
496 (HildonIMInsertUtf8Message*)&e->xclient.data;
497 assert(e->xclient.format == HILDON_IM_INSERT_UTF8_FORMAT);
499 insert_utf8(msg->msg_flag, msg->utf8_str);
502 } else if (e->xclient.message_type == ATOM(HILDON_IM_COM)) {
503 HildonIMComMessage *msg = (HildonIMComMessage*)&e->xclient.data;
505 options = msg->options;
508 case HILDON_IM_CONTEXT_HANDLE_ENTER:
509 insert_special_key(SDLK_RETURN);
511 case HILDON_IM_CONTEXT_HANDLE_TAB:
512 insert_special_key(SDLK_TAB);
514 case HILDON_IM_CONTEXT_HANDLE_BACKSPACE:
515 insert_special_key(SDLK_BACKSPACE);
517 case HILDON_IM_CONTEXT_HANDLE_SPACE:
518 insert_special_key(SDLK_SPACE);
521 case HILDON_IM_CONTEXT_CLIPBOARD_CUT:
522 send_sdl_clipboard_event(HIM_CLIPBOARD_CUT);
524 case HILDON_IM_CONTEXT_CLIPBOARD_COPY:
525 send_sdl_clipboard_event(HIM_CLIPBOARD_COPY);
527 case HILDON_IM_CONTEXT_CLIPBOARD_PASTE:
528 send_sdl_clipboard_event(HIM_CLIPBOARD_PASTE);
530 case HILDON_IM_CONTEXT_CLIPBOARD_SELECTION_QUERY:
531 send_sdl_clipboard_event(HIM_CLIPBOARD_REQUEST_SELECTION);
534 case HILDON_IM_CONTEXT_DIRECT_MODE:
535 set_commit_mode(HILDON_IM_COMMIT_DIRECT);
537 case HILDON_IM_CONTEXT_BUFFERED_MODE:
538 set_commit_mode(HILDON_IM_COMMIT_BUFFERED);
540 case HILDON_IM_CONTEXT_REDIRECT_MODE:
541 set_commit_mode(HILDON_IM_COMMIT_REDIRECT);
543 case HILDON_IM_CONTEXT_SURROUNDING_MODE:
544 set_commit_mode(HILDON_IM_COMMIT_SURROUNDING);
547 case HILDON_IM_CONTEXT_CONFIRM_SENTENCE_START:
548 check_sentence_start();
550 case HILDON_IM_CONTEXT_FLUSH_PREEDIT:
551 commit_preedit_buffer();
554 case HILDON_IM_CONTEXT_REQUEST_SURROUNDING:
555 send_sdl_request_surrounding_event(False);
557 case HILDON_IM_CONTEXT_CLEAR_STICKY:
558 mod_mask &= ~(HILDON_IM_SHIFT_STICKY_MASK |
559 HILDON_IM_SHIFT_LOCK_MASK |
560 HILDON_IM_LEVEL_STICKY_MASK |
561 HILDON_IM_LEVEL_LOCK_MASK);
564 case HILDON_IM_CONTEXT_WIDGET_CHANGED:
565 mod_mask = 0; /* Clear modifier status */
567 case HILDON_IM_CONTEXT_ENTER_ON_FOCUS:
568 /* We don't have the concept of focused widget. */
569 insert_special_key(SDLK_RETURN);
572 #if MAEMO_VERSION == 5
573 case HILDON_IM_CONTEXT_PREEDIT_MODE:
574 set_commit_mode(HILDON_IM_COMMIT_PREEDIT);
577 case HILDON_IM_CONTEXT_REQUEST_SURROUNDING_FULL:
578 send_sdl_request_surrounding_event(True);
580 case HILDON_IM_CONTEXT_CANCEL_PREEDIT:
584 case HILDON_IM_CONTEXT_SPACE_AFTER_COMMIT:
585 space_after_commit = True;
587 case HILDON_IM_CONTEXT_NO_SPACE_AFTER_COMMIT:
588 space_after_commit = False;
593 fprintf(stderr, "Unknown Hildon IM COM message %d\n",
599 } else if (e->xclient.message_type == ATOM(HILDON_IM_SURROUNDING_CONTENT)) {
600 assert(e->xclient.format == HILDON_IM_SURROUNDING_CONTENT_FORMAT);
601 // TODO Surrounding content
602 fprintf(stderr, "Surrounding content is not implemented\n");
603 } else if (e->xclient.message_type == ATOM(HILDON_IM_SURROUNDING)) {
604 assert(e->xclient.format == HILDON_IM_SURROUNDING_FORMAT);
605 HildonIMSurroundingMessage *msg = (HildonIMSurroundingMessage*)(&e->xclient.data);
606 set_sdl_cursor_location(msg->offset_is_relative, msg->cursor_offset);
613 static unsigned sdl_mod_to_x_state(const SDLMod mod)
616 if (mod & KMOD_SHIFT) state |= ShiftMask;
617 if (mod & KMOD_CAPS) state |= LockMask;
618 if (mod & KMOD_CTRL) state |= ControlMask;
619 if (mod & KMOD_MODE) state |= LevelMask;
623 /** @return 1 hides the event from the SDL queue */
624 static int filter_keypress(const SDL_KeyboardEvent *event)
626 /* Basically, convert a SDL KeyboardEvent back into a XKeyEvent. */
627 const Bool pressed = event->state == SDL_PRESSED;
628 KeyCode keycode = event->keysym.scancode; // As in, Xlib KeyCode.
629 unsigned int mods_rtrn, state = sdl_mod_to_x_state(event->keysym.mod);
631 XkbLookupKeySym(display, keycode, state, &mods_rtrn, &keysym);
633 Uint32 c = 0; /* To be replaced by the commited unicode character. */
635 /** A special setting that causes "normal" keys not to be filtered,
636 even if HIM will latter send a commit event. Great for games.
638 const int normal_key_return = input_mode & HIM_MODE_NO_FILTER ? 0 : 1;
640 /* A dead key will not be immediately commited, but combined with the next key */
641 if (keysym >= XK_dead_grave && keysym <= XK_dead_horn) {
642 mod_mask |= HILDON_IM_DEAD_KEY_MASK;
644 mod_mask &= ~HILDON_IM_DEAD_KEY_MASK;
647 /* A normal dead key first press */
648 if ((mod_mask & HILDON_IM_DEAD_KEY_MASK) && combining_char == 0) {
649 combining_char = dead_key_to_unicode_combining_character(keysym);
650 return normal_key_return; /* Treat dead keys as "normal keys" */
653 /* Pressing any key while the compose key is pressed will keep that
654 character from being directly submitted to the application. This
655 allows the IM process to override the interpretation of the key */
656 if (keysym == COMPOSE_KEY) {
658 mod_mask |= HILDON_IM_COMPOSE_MASK;
660 mod_mask &= ~HILDON_IM_COMPOSE_MASK;
664 /* Sticky and locking keys initialization */
666 /* Set the appropiate mask when Shift or Fn is released. */
667 if (keysym == XK_Shift_L || keysym == XK_Shift_R) {
668 set_mask_state(&mod_mask,
669 HILDON_IM_SHIFT_LOCK_MASK, HILDON_IM_SHIFT_STICKY_MASK,
670 last_keysym == XK_Shift_L || last_keysym == XK_Shift_R);
671 } else if (keysym == LEVEL_KEY) {
672 set_mask_state(&mod_mask,
673 HILDON_IM_LEVEL_LOCK_MASK, HILDON_IM_LEVEL_STICKY_MASK,
674 last_keysym == LEVEL_KEY);
678 /* Update last_keysym now; we do not need it later. */
679 last_keysym = keysym;
681 /* We let the input method know of things like Return, Tab, etc. */
682 if (keysym == XK_Return || keysym == XK_KP_Enter
683 || keysym == XK_ISO_Enter || keysym == XK_Tab) {
684 send_hildon_key_event(pressed, state, keysym, keycode);
688 /* When the level key is in sticky or locked state, translate the
689 keyboard state as if that level key was being held down. */
690 if ((mod_mask & (HILDON_IM_LEVEL_STICKY_MASK | HILDON_IM_LEVEL_LOCK_MASK)) ||
691 state == LevelMask) {
692 /* Use Xkb to query the keymap and get the key we would get if we
693 were to be holding Fn */
694 unsigned int mods_rtrn;
696 if (XkbLookupKeySym(display, keycode, LevelMask, &mods_rtrn, &new_keysym)) {
699 } else if (options & HILDON_IM_AUTOLEVEL_NUMERIC &&
700 (input_mode & HILDON_GTK_INPUT_MODE_FULL) == HILDON_GTK_INPUT_MODE_NUMERIC) {
701 keysym = get_keysym_for_level(keycode, NUMERIC_LEVEL);
702 } else if (options & HILDON_IM_LOCK_LEVEL) {
703 keysym = get_keysym_for_level(keycode, LOCKABLE_LEVEL);
707 /* Hardware keyboard autocapitalization */
708 if (auto_upper && (input_mode & HILDON_GTK_INPUT_MODE_AUTOCAP)) {
710 XConvertCase(keysym, &lower, &upper);
712 if (state & ShiftMask) {
713 keysym = lower; // Since we're latter going to shift it :)
719 /* Shift lock or holding the shift key down forces uppercase */
720 if (mod_mask & HILDON_IM_SHIFT_LOCK_MASK || state & ShiftMask) {
722 XConvertCase(keysym, &lower, &upper);
725 } else if (mod_mask & HILDON_IM_SHIFT_STICKY_MASK) {
727 XConvertCase(keysym, &lower, &upper);
729 if (lower == upper) {
730 /* Non printable character: check the keyboard map */
731 unsigned int mods_rtrn;
733 if (XkbLookupKeySym(display, keycode, ShiftMask, &mods_rtrn, &new_keysym)) {
736 } else if (keysym == upper) {
737 /* Previously upper case (means caps lock is on) ==> lower case. */
744 /* Sticky and lock state reset */
746 if (keysym != XK_Shift_L && keysym != XK_Shift_R) {
747 /* Pressing anything other than shift, if not locked,
748 resets shift state (this is the other important part of
750 if (!(mod_mask & HILDON_IM_SHIFT_LOCK_MASK)) {
751 mod_mask &= ~HILDON_IM_SHIFT_STICKY_MASK;
754 if (keysym != LEVEL_KEY) {
755 /* Same for Fn/Level key */
756 if (!(mod_mask & HILDON_IM_LEVEL_LOCK_MASK)) {
757 mod_mask &= ~HILDON_IM_LEVEL_STICKY_MASK;
762 /* From now on, just ignore release key events. */
763 /* Save for Ctrl+Anykey. Forward those to HIM. CtrlC CtrlV support? */
764 if (!pressed || state & ControlMask) {
765 send_hildon_key_event(pressed, state, keysym, keycode);
769 /* Pressing a dead key twice, or if followed by a space, inputs
770 the dead key's character representation. */
771 if ((mod_mask & HILDON_IM_DEAD_KEY_MASK || keysym == XK_space) &&
773 Uint32 last = dead_key_to_unicode_combining_character(keysym);
774 if (last == combining_char || keysym == XK_space) {
775 /* Pressed twice or followed by space => print dead key. */
776 c = combining_character_to_unicode(combining_char);
778 /* Some other thing. Consider as regular keypress. */
779 c = keysym_to_unicode(keysym);
783 /* A regular keypress. */
784 if (mod_mask & HILDON_IM_COMPOSE_MASK) {
785 /* Chr key pressed. HIM handles all of this. */
786 send_hildon_key_event(pressed, state, keysym, keycode);
789 /* A completely regular keypress. */
790 c = keysym_to_unicode(keysym);
795 /* Prepare a buffer for text input event */
796 HIM_TextInputEvent event;
797 event.type = event_base;
798 event.code = HIM_TEXTINPUTEVENT;
800 /* Check if we have pending diactrics... */
801 if (combining_char) {
802 /* This is not a full unicode normalizer, but works. */
803 int new_c = compose_unicode_characters(c, combining_char);
805 if (new_c != NOT_COMPOSITE) {
812 unicode_to_utf8(c, event.text);
813 send_hildon_key_event(pressed, state, unicode_to_keysym(c), keycode);
815 SDL_PushEvent((SDL_Event*)&event);
817 return normal_key_return;
819 /* We didn't output any string. */
820 send_hildon_key_event(pressed, state, keysym, keycode);
822 /* Non-printable characters invalidate any previous dead keys */
823 /* Save for shift, which you might need. */
824 if (keysym != XK_Shift_L && keysym != XK_Shift_R) {
834 int HIM_Init(Uint32 flags, Uint8 event)
838 SDL_VERSION(&info.version);
839 if (SDL_GetWMInfo(&info) != 1) {
840 SDL_SetError("SDL_him is incompatible with this SDL version");
844 if (event != 0 && (event < SDL_USEREVENT || event >= SDL_NUMEVENTS)) {
845 SDL_SetError("Invalid event number");
849 display = info.info.x11.display;
853 XInternAtoms(display, (char**)atom_names,
854 HILDON_IM_NUM_ATOMS, True, atom_values);
855 if (ATOM(HILDON_IM_WINDOW) == None) {
856 /* a required atom was not found */
857 SDL_SetError("Hildon Input Method is not loaded");
862 /* Enable some SDL features we need */
863 SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
878 void HIM_Enable(HIM_Mode mode)
882 /* Send the input mode before the command */
885 /* Default commit mode required by Fremantle */
886 commit_mode = HILDON_IM_COMMIT_REDIRECT;
888 /* Reset some stuff, like if we had changed focused widget. */
890 last_keysym = XK_VoidSymbol;
893 send_hildon_command(HILDON_IM_SETCLIENT);
902 send_hildon_command(HILDON_IM_CLEAR);
905 void HIM_ShowKeyboard(HIM_Trigger reason)
907 trigger_mode = reason;
908 send_hildon_command(HILDON_IM_SETNSHOW);
911 void HIM_HideKeyboard()
913 send_hildon_command(HILDON_IM_HIDE);
916 int HIM_FilterEvent(const SDL_Event *event)
918 switch (event->type) {
920 return handle_x_event(&event->syswm.msg->event.xevent);
925 /* Only process keypresses at all if enabled */
926 return filter_keypress(&event->key);
934 void HIM_SendSurrounding(const char * text, unsigned long cursor)
936 send_surrounding_content(text);
937 send_surrounding_header(cursor);
940 void HIM_SendSelection(const char * text)
942 Bool has_selection = text && text[0] != '\0';
943 send_selection_reply(has_selection);