1 /* UIM input support for
12 ================================================================================
13 CVars introduced with the UIM extension
14 ================================================================================
17 cvar_t im_enabled = {CVAR_SAVE, "im_enabled", "1", "use UIM input"};
18 cvar_t im_engine = {CVAR_SAVE, "im_engine", "anthy", "which input method to use if uim is supported and active"};
19 cvar_t im_language = {CVAR_SAVE, "im_language", "ja", "which language should be used for the input editor (en for english, ja for japanese, etc.)"};
21 cvar_t im_cursor = {CVAR_SAVE, "im_cursor", "", "the cursor to append to what you type when not selecting candidates - you don't want this for the chatbox"};
22 cvar_t im_cursor_start = {CVAR_SAVE, "im_cursor_start", "^3[^x888", "how to mark the beginning of the input cursor"};
23 cvar_t im_cursor_end = {CVAR_SAVE, "im_cursor_end", "^3]", "how to mark the end of the input cursor"};
24 cvar_t im_selection_start = {CVAR_SAVE, "im_selection_start", "^xf80", "how to mark the beginning of the current selection (as in, what UIM would display underlined)"};
25 cvar_t im_selection_end = {CVAR_SAVE, "im_selection_end", "", "how to mark the end of the current selection (as in, what UIM would display underlined)"};
26 cvar_t im_separator = {CVAR_SAVE, "im_separator", "|", "separator to use..."};
29 ================================================================================
30 Library imports. Taken from the uim headers.
31 ================================================================================
33 struct uim_code_converter **quim_iconv;
35 int (*quim_init)(void);
36 void (*quim_quit)(void);
37 uim_candidate (*quim_get_candidate)(uim_context, int index, int accel_enum_hint);
38 void (*quim_candidate_free)(uim_candidate);
39 int (*quim_get_candidate_index)(uim_context);
40 const char* (*quim_candidate_get_cand_str)(uim_candidate);
41 const char* (*quim_get_default_im_name)(const char *localename);
42 uim_context (*quim_create_context)(void *cookie,
46 struct uim_code_converter *conv,
47 void (*commit_cb)(void *ptr, const char *str));
48 void (*quim_reset_context)(uim_context);
49 void (*quim_release_context)(uim_context);
50 int (*quim_helper_init_client_fd)(void (*disconnect_cb)(void));
51 void (*quim_helper_close_client_fd)(int);
52 void (*quim_helper_send_message)(int, const char*);
53 void (*quim_set_preedit_cb)(uim_context,
54 void (*clear_cb)(void*),
55 void (*pushback_cb)(void*, int attr, const char *str),
56 void (*update_cb)(void*));
57 void (*quim_set_candidate_selector_cb)(uim_context,
58 void (*activate_cb)(void*, int nr, int display_limit),
59 void (*select_cb)(void*, int index),
60 void (*shift_page_cb)(void*, int direction),
61 void (*deactivate_cb)(void*));
62 void (*quim_prop_list_update)(uim_context);
63 int (*quim_press_key)(uim_context, int key, int mods);
64 int (*quim_release_key)(uim_context, int key, int mods);
65 void (*quim_set_prop_list_update_cb)(uim_context, void (*update_cb)(void*, const char *str));
66 void (*quim_set_configuration_changed_cb)(uim_context, void (*changed_cb)(void*));
69 ================================================================================
70 Support for dynamically loading the UIM library
71 ================================================================================
74 static dllfunction_t uimfuncs[] =
76 {"uim_init", (void **) &quim_init},
77 {"uim_quit", (void **) &quim_quit},
78 {"uim_get_candidate", (void **) &quim_get_candidate},
79 {"uim_candidate_free", (void **) &quim_candidate_free},
80 {"uim_get_candidate_index", (void **) &quim_get_candidate_index},
81 {"uim_candidate_get_cand_str", (void **) &quim_candidate_get_cand_str},
82 {"uim_get_default_im_name", (void **) &quim_get_default_im_name},
83 {"uim_create_context", (void **) &quim_create_context},
84 {"uim_reset_context", (void **) &quim_reset_context},
85 {"uim_release_context", (void **) &quim_release_context},
86 {"uim_helper_init_client_fd", (void **) &quim_helper_init_client_fd},
87 {"uim_helper_close_client_fd", (void **) &quim_helper_close_client_fd},
88 {"uim_helper_send_message", (void **) &quim_helper_send_message},
89 {"uim_set_preedit_cb", (void **) &quim_set_preedit_cb},
90 {"uim_set_candidate_selector_cb", (void **) &quim_set_candidate_selector_cb},
91 {"uim_prop_list_update", (void **) &quim_prop_list_update},
92 {"uim_press_key", (void **) &quim_press_key},
93 {"uim_release_key", (void **) &quim_release_key},
94 {"uim_iconv", (void **) &quim_iconv},
95 {"uim_set_prop_list_update_cb", (void **) &quim_set_prop_list_update_cb},
96 {"uim_set_configuration_changed_cb", (void **) &quim_set_configuration_changed_cb},
101 /// Handle for UIM dll
102 static dllhandle_t uim_dll = NULL;
111 void UIM_CloseLibrary (void)
113 Sys_UnloadLibrary (&uim_dll);
120 Try to load the UIM DLL
123 qboolean UIM_OpenLibrary (void)
125 const char* dllnames [] =
128 #error path for freetype 2 dll
130 #error path for freetype 2 dll
131 #elif defined(MACOSX)
145 if (!Sys_LoadLibrary (dllnames, &uim_dll, uimfuncs))
151 ================================================================================
152 UIM input method implementation.
153 ================================================================================
160 //mempool_t *mempool;
171 size_t cursor_length;
173 size_t cursor_inlength;
175 // where can we find the previous color code?
190 qUIM_SetCursor setcursor;
192 // this is the only way K_RETURN actually sends a chat message
193 // when using stuff like anthy
198 static quim_state quim;
200 static void UIM_PropListUpdate(void*, const char *str);
201 static void UIM_Commit(void*, const char *str);
202 static void UIM_HelperDisconnected(void);
203 static void UIM_Clear(void*);
204 static void UIM_Push(void*, int attr, const char *str);
205 static void UIM_Update(void*);
206 static void UIM_Activate(void*, int nr, int display_limit);
207 static void UIM_Select(void*, int index);
208 static void UIM_Shift(void*, int dir);
209 static void UIM_Deactivate(void*);
210 static void UIM_ConfigChanged(void*);
211 static void UIM_Restart_f(void);
217 Load UIM support and register commands / cvars
220 void UIM_Start(void);
221 static qboolean UIM_InitConverter(void);
224 // register the cvars in any case so they're at least saved,
225 // after all, they're for the user
227 Cvar_RegisterVariable(&im_engine);
228 Cvar_RegisterVariable(&im_language);
229 Cvar_RegisterVariable(&im_enabled);
230 Cvar_RegisterVariable(&im_cursor);
231 Cvar_RegisterVariable(&im_cursor_start);
232 Cvar_RegisterVariable(&im_cursor_end);
233 Cvar_RegisterVariable(&im_selection_start);
234 Cvar_RegisterVariable(&im_selection_end);
235 Cvar_RegisterVariable(&im_separator);
236 Cmd_AddCommand ("im_restart", UIM_Restart_f, "restart the input editor");
238 //quim.mempool = Mem_AllocPool("UIM", 0, NULL);
242 static void UIM_Restart_f(void)
248 static struct uim_code_converter *dp_converter;
250 static qboolean UIM_InitConverter(void)
253 dp_converter = &dp_uim_conv;
254 dp_uim_conv.is_convertible = &conv_IsConvertible;
255 dp_uim_conv.create = &conv_Create;
256 dp_uim_conv.convert = &conv_Convert;
257 dp_uim_conv.release = &conv_Release;
259 dp_converter = *quim_iconv;
267 Unload all UIM resources
270 void UIM_Shutdown(void)
277 quim_release_context(quim.ctx);
278 quim_helper_close_client_fd(fd);
282 // Mem_FreePool(&quim.mempool);
289 Try starting up UIM, this should be made available as console command maybe?
294 if (!UIM_OpenLibrary())
297 if (!UIM_InitConverter())
300 //setlocale(LC_CTYPE, NULL);
301 if (quim_init() != 0)
303 Con_Print("Failed to initialize UIM support\n");
308 quim.ctx = quim_create_context(NULL, "UTF-8", im_language.string, im_engine.string, dp_converter, &UIM_Commit);
309 if (quim.ctx == NULL)
311 Con_Print("Failed to create UIM context\n");
316 quim.fd = quim_helper_init_client_fd(&UIM_HelperDisconnected);
317 if (quim.fd < 2) // well, in and out aren't exactly ... good
319 Con_Print("Failed to initialize UIM helper client fd\n");
323 Con_Print("UIM Helper connected\n");
325 quim_set_preedit_cb(quim.ctx, &UIM_Clear, &UIM_Push, &UIM_Update);
326 quim_set_candidate_selector_cb(quim.ctx, &UIM_Activate, &UIM_Select, &UIM_Shift, &UIM_Deactivate);
327 quim_set_prop_list_update_cb(quim.ctx, &UIM_PropListUpdate);
328 quim_set_configuration_changed_cb(quim.ctx, &UIM_ConfigChanged);
329 quim_prop_list_update(quim.ctx);
332 // api entry, must check for UIM availability *ahem*
333 qboolean UIM_Available(void)
335 if (!im_enabled.integer)
337 return (!!uim_dll && quim.ctx);
340 // api entry, must check for UIM availability
341 qboolean UIM_Direct(void)
343 if (!uim_dll || !quim.buffer || !quim.ctx || quim.fd <= 0 || !quim.buffer_size)
349 static int UIM_GetKeyMod(void)
352 if (Key_IsPressed(K_SHIFT)) mod |= UMod_Shift;
353 if (Key_IsPressed(K_ALT)) mod |= UMod_Alt;
354 if (Key_IsPressed(K_CTRL)) mod |= UMod_Control;
355 if (Key_IsPressed(K_SUPER)) mod |= UMod_Super;
359 static int UIM_KeyToUKey(int key, Uchar unicode)
373 return UKey_Backspace;
432 return UKey_Num_Lock;
434 return UKey_Caps_Lock;
436 return UKey_Scroll_Lock;
466 // api entry, must check for UIM availability
467 qboolean UIM_KeyUp(int key, Uchar unicode)
469 if (!UIM_Available())
472 return (quim_release_key(quim.ctx, UIM_KeyToUKey(key, unicode), UIM_GetKeyMod()) == 0) && !!quim.actions;
475 // api entry, must check for UIM availability
476 qboolean UIM_KeyDown(int key, Uchar unicode)
478 if (!UIM_Available())
481 return (quim_press_key(quim.ctx, UIM_KeyToUKey(key, unicode), UIM_GetKeyMod()) == 0) && !!quim.actions;
484 // api entry, must check for UIM availability
485 qboolean UIM_Key(int key, Uchar unicode)
487 qboolean handled = false;
489 if (!UIM_Available())
492 mod = UIM_GetKeyMod();
493 ukey = UIM_KeyToUKey(key, unicode);
494 //Con_Printf("uim handling key: %i (mod: %i) char: %c\n", ukey, mod, (ukey >= 32 && ukey < 0x7F) ? ukey : '.');
495 if (quim_press_key(quim.ctx, ukey, mod) == 0)
497 if (quim_release_key(quim.ctx, ukey, mod) == 0)
499 return handled && !!quim.actions;
502 // api entry, must check for UIM availability
503 static char defcolor[3];
504 qboolean UIM_EnterBuffer(char *buffer, size_t bufsize, size_t pos, qUIM_SetCursor setcursor_cb)
506 if (!UIM_Available())
516 quim.buffer = buffer;
517 quim.buffer_size = bufsize;
518 quim.buffer_pos = pos;
519 quim.edit_start = pos;
521 quim.edit_length = 0;
522 quim.length = strlen(buffer);
523 quim.tail_length = quim.length - quim.edit_start;
525 quim.setcursor = setcursor_cb;
527 defcolor[0] = STRING_COLOR_TAG;
533 // search for the previous color code
539 if (pos >= 1 && buffer[pos-1] == STRING_COLOR_TAG && isdigit(buffer[pos]))
541 quim.pc = &buffer[pos-1];
546 buffer[pos-4] == STRING_COLOR_TAG &&
547 buffer[pos-3] == STRING_COLOR_RGB_TAG_CHAR &&
548 isxdigit(buffer[pos-2]) &&
549 isxdigit(buffer[pos-1]) &&
550 isxdigit(buffer[pos]))
552 quim.pc = &buffer[pos-4];
561 // api entry, must check for UIM availability
562 void UIM_CancelBuffer(void)
564 if (!UIM_Available())
566 quim_reset_context(quim.ctx);
568 quim.setcursor = NULL;
573 // api entry, must check for UIM availability
574 void UIM_SetCursor(int pos)
576 UIM_EnterBuffer(quim.buffer, quim.buffer_size, pos, quim.setcursor);
579 static qboolean UIM_Insert2(const char *str, size_t _slen)
588 if (quim.edit_pos + slen + quim.tail_length >= quim.buffer_size - 1) {
589 Con_Print("UIM: Insertion failed: not enough space!\n");
592 //Con_Printf("Inserting [%s]\n", str);
593 //Con_Printf("Insertion point: %lu\n", (unsigned long)quim.edit_pos);
594 memmove(quim.buffer + quim.edit_pos + slen,
595 quim.buffer + quim.edit_pos,
596 quim.buffer_size - quim.edit_pos - slen);
597 memcpy(quim.buffer + quim.edit_pos, str, slen);
598 if (quim.edit_pos >= quim.length)
599 quim.buffer[quim.edit_pos + slen] = 0;
600 quim.edit_pos += slen;
601 quim.edit_length += slen;
605 static inline qboolean UIM_Insert(const char *str)
607 return UIM_Insert2(str, 0);
610 static void UIM_Commit(void *cookie, const char *str)
614 //Con_Printf("UIM_Commit: %s\n", str);
616 if (!UIM_Insert(str))
618 Con_Print("UIM: Failed to commit buffer\n");
621 quim.buffer_pos = quim.edit_pos;
622 quim.edit_start = quim.edit_pos;
623 quim.edit_length = 0;
626 quim.setcursor(quim.buffer_pos);
629 static void UIM_HelperDisconnected(void)
632 //Con_Print("UIM Helper disconnected\n");
637 static void UIM_Clear(void *cookie)
641 //Con_Print("UIM_Clear\n");
642 memmove(quim.buffer + quim.buffer_pos,
643 quim.buffer + quim.edit_pos,
644 quim.buffer_size - quim.edit_pos);
645 quim.edit_pos = quim.buffer_pos;
646 quim.edit_length = 0;
647 quim.cursor_pos = quim.edit_pos;
648 quim.cursor_length = 0;
649 quim.cursor_inpos = quim.edit_pos;
650 quim.cursor_inlength = 0;
653 quim.setcursor(quim.edit_pos);
656 // we have BUFSTART and QUIM_CURSOR
657 static void UIM_Push(void *cookie, int attr, const char *str)
663 //Con_Printf("UIM_Push: (%i) [%s] <%lu>\n", attr, str, strlen(str));
664 if ((attr & (UPreeditAttr_Cursor | UPreeditAttr_Reverse)) == (UPreeditAttr_Cursor | UPreeditAttr_Reverse))
666 quim.cursor_pos = quim.edit_pos;
667 quim.cursor_length = 0;
668 quim.cursor_inpos = quim.edit_pos;
669 quim.cursor_inlength = 0;
670 if (!UIM_Insert(im_cursor_start.string))
672 quim.cursor_inpos = quim.edit_pos;
673 quim.cursor_length = strlen(im_cursor_start.string);
675 if (attr & UPreeditAttr_UnderLine)
677 if (!UIM_Insert(im_selection_start.string))
682 if (!UIM_Insert(str))
685 if (attr & UPreeditAttr_UnderLine)
687 if (!UIM_Insert(im_selection_end.string))
690 if (attr & UPreeditAttr_Cursor)
692 if (attr & UPreeditAttr_Reverse)
694 size_t slen = strlen(str);
695 size_t cl = quim.cursor_length;
696 quim.cursor_length += slen;
697 quim.cursor_inlength += slen;
698 if (!UIM_Insert(im_cursor_end.string))
700 quim.cursor_length += cl;
705 size_t epos = quim.edit_pos;
706 size_t elen = quim.edit_length;
708 if (!UIM_Insert(im_cursor.string))
711 quim.edit_pos = epos;
712 quim.edit_length = elen;
718 static void UIM_Update(void *cookie)
721 if (quim.pushed) // do not append color to commits, only to pushed objects
722 UIM_Insert2(quim.pc, quim.pc_len);
724 quim.setcursor(quim.edit_pos);
725 //Con_Print("UIM_Update\n");
727 // we could allocate a buffer in which we work
728 // and only update on "update"
729 // but who cares, seriously?
732 static void UIM_Activate(void *cookie, int nr, int display_limit)
735 //Con_Print("UIM_Activate\n");
737 quim.active_count = nr;
738 quim.active_limit = display_limit;
741 static void UIM_Select(void *cookie, int index)
748 //Con_Print("UIM_Select\n");
749 cd = quim_get_candidate(quim.ctx, index, quim.active_count);
750 str = quim_candidate_get_cand_str(cd);
754 // check if we even fit into that buffer
756 quim.edit_pos + quim.tail_length + slen - quim.cursor_inlength >= quim.buffer_size - 1)
758 // erase the part in the cursor:
759 memmove(quim.buffer + quim.cursor_inpos,
760 quim.buffer + quim.cursor_inpos + quim.cursor_inlength,
761 quim.buffer_size - quim.cursor_inpos - quim.cursor_inlength);
762 quim.cursor_length -= quim.cursor_inlength;
763 quim.cursor_inpos -= quim.cursor_inlength;
764 quim.edit_length -= quim.cursor_inlength;
765 quim.cursor_inlength = 0;
767 Con_Print("Failed to select entry: buffer too short\n");
771 // we can insert this stuff
772 // move the cursor-end + tail
773 memmove(quim.buffer + quim.cursor_inpos + quim.cursor_inlength,
774 quim.buffer + quim.cursor_pos + quim.cursor_length,
775 quim.buffer_size - quim.cursor_pos - quim.cursor_length);
777 memcpy(quim.buffer + quim.cursor_inpos, str, slen);
779 quim.edit_length = quim.edit_length + slen - quim.cursor_inlength;
780 quim.cursor_length = quim.cursor_length + slen - quim.cursor_inlength;
781 quim.cursor_inlength = slen;
783 quim_candidate_free(cd);
786 static void UIM_Shift(void *cookie, int dir)
789 Con_Print("^1ERROR: ^7UIM_Shift: Not implemented\n");
792 static void UIM_Deactivate(void *cookie)
796 //Con_Print("^3UIM: make UIM_Deactivate move the cursor...\n");
799 static void UIM_ConfigChanged(void *cookie)
802 //Con_Print("UIM_ConfigChanged\n");
805 static void UIM_PropListUpdate(void *cookie, const char *str)
810 char buffer[1024<<2];
811 //Con_Printf("UIM_PropListUpdate\n%s\n", str);
812 dpsnprintf(buffer, sizeof(buffer), "prop_list_update\ncharset=UTF-8\n%s", str);
813 quim_helper_send_message(quim.fd, buffer);