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)
374 return UKey_Backspace;
403 return UKey_F1 + ofs;
422 return UKey_Num_Lock;
424 return UKey_Caps_Lock;
426 return UKey_Scroll_Lock;
456 // api entry, must check for UIM availability
457 qboolean UIM_KeyUp(int key, Uchar unicode)
459 if (!UIM_Available())
462 return (quim_release_key(quim.ctx, UIM_KeyToUKey(key, unicode), UIM_GetKeyMod()) == 0) && !!quim.actions;
465 // api entry, must check for UIM availability
466 qboolean UIM_KeyDown(int key, Uchar unicode)
468 if (!UIM_Available())
471 return (quim_press_key(quim.ctx, UIM_KeyToUKey(key, unicode), UIM_GetKeyMod()) == 0) && !!quim.actions;
474 // api entry, must check for UIM availability
475 qboolean UIM_Key(int key, Uchar unicode)
477 qboolean handled = false;
479 if (!UIM_Available())
482 mod = UIM_GetKeyMod();
483 ukey = UIM_KeyToUKey(key, unicode);
484 //Con_Printf("uim handling key: %i (mod: %i) char: %c\n", ukey, mod, (ukey >= 32 && ukey < 0x7F) ? ukey : '.');
485 if (quim_press_key(quim.ctx, ukey, mod) == 0)
487 if (quim_release_key(quim.ctx, ukey, mod) == 0)
489 return handled && !!quim.actions;
492 // api entry, must check for UIM availability
493 static char defcolor[3];
494 qboolean UIM_EnterBuffer(char *buffer, size_t bufsize, size_t pos, qUIM_SetCursor setcursor_cb)
496 if (!UIM_Available())
506 quim.buffer = buffer;
507 quim.buffer_size = bufsize;
508 quim.buffer_pos = pos;
509 quim.edit_start = pos;
511 quim.edit_length = 0;
512 quim.length = strlen(buffer);
513 quim.tail_length = quim.length - quim.edit_start;
515 quim.setcursor = setcursor_cb;
517 defcolor[0] = STRING_COLOR_TAG;
523 // search for the previous color code
529 if (pos >= 1 && buffer[pos-1] == STRING_COLOR_TAG && isdigit(buffer[pos]))
531 quim.pc = &buffer[pos-1];
536 buffer[pos-4] == STRING_COLOR_TAG &&
537 buffer[pos-3] == STRING_COLOR_RGB_TAG_CHAR &&
538 isxdigit(buffer[pos-2]) &&
539 isxdigit(buffer[pos-1]) &&
540 isxdigit(buffer[pos]))
542 quim.pc = &buffer[pos-4];
551 // api entry, must check for UIM availability
552 void UIM_CancelBuffer(void)
554 if (!UIM_Available())
556 quim_reset_context(quim.ctx);
558 quim.setcursor = NULL;
563 // api entry, must check for UIM availability
564 void UIM_SetCursor(int pos)
566 UIM_EnterBuffer(quim.buffer, quim.buffer_size, pos, quim.setcursor);
569 static qboolean UIM_Insert2(const char *str, size_t _slen)
578 if (quim.edit_pos + slen + quim.tail_length >= quim.buffer_size - 1) {
579 Con_Print("UIM: Insertion failed: not enough space!\n");
582 //Con_Printf("Inserting [%s]\n", str);
583 //Con_Printf("Insertion point: %lu\n", (unsigned long)quim.edit_pos);
584 memmove(quim.buffer + quim.edit_pos + slen,
585 quim.buffer + quim.edit_pos,
586 quim.buffer_size - quim.edit_pos - slen);
587 memcpy(quim.buffer + quim.edit_pos, str, slen);
588 if (quim.edit_pos >= quim.length)
589 quim.buffer[quim.edit_pos + slen] = 0;
590 quim.edit_pos += slen;
591 quim.edit_length += slen;
595 static inline qboolean UIM_Insert(const char *str)
597 return UIM_Insert2(str, 0);
600 static void UIM_Commit(void *cookie, const char *str)
604 //Con_Printf("UIM_Commit: %s\n", str);
606 if (!UIM_Insert(str))
608 Con_Print("UIM: Failed to commit buffer\n");
611 quim.buffer_pos = quim.edit_pos;
612 quim.edit_start = quim.edit_pos;
613 quim.edit_length = 0;
616 quim.setcursor(quim.buffer_pos);
619 static void UIM_HelperDisconnected(void)
622 //Con_Print("UIM Helper disconnected\n");
627 static void UIM_Clear(void *cookie)
631 //Con_Print("UIM_Clear\n");
632 memmove(quim.buffer + quim.buffer_pos,
633 quim.buffer + quim.edit_pos,
634 quim.buffer_size - quim.edit_pos);
635 quim.edit_pos = quim.buffer_pos;
636 quim.edit_length = 0;
637 quim.cursor_pos = quim.edit_pos;
638 quim.cursor_length = 0;
639 quim.cursor_inpos = quim.edit_pos;
640 quim.cursor_inlength = 0;
643 quim.setcursor(quim.edit_pos);
646 // we have BUFSTART and QUIM_CURSOR
647 static void UIM_Push(void *cookie, int attr, const char *str)
653 //Con_Printf("UIM_Push: (%i) [%s] <%lu>\n", attr, str, strlen(str));
654 if ((attr & (UPreeditAttr_Cursor | UPreeditAttr_Reverse)) == (UPreeditAttr_Cursor | UPreeditAttr_Reverse))
656 quim.cursor_pos = quim.edit_pos;
657 quim.cursor_length = 0;
658 quim.cursor_inpos = quim.edit_pos;
659 quim.cursor_inlength = 0;
660 if (!UIM_Insert(im_cursor_start.string))
662 quim.cursor_inpos = quim.edit_pos;
663 quim.cursor_length = strlen(im_cursor_start.string);
665 if (attr & UPreeditAttr_UnderLine)
667 if (!UIM_Insert(im_selection_start.string))
672 if (!UIM_Insert(str))
675 if (attr & UPreeditAttr_UnderLine)
677 if (!UIM_Insert(im_selection_end.string))
680 if (attr & UPreeditAttr_Cursor)
682 if (attr & UPreeditAttr_Reverse)
684 size_t slen = strlen(str);
685 size_t cl = quim.cursor_length;
686 quim.cursor_length += slen;
687 quim.cursor_inlength += slen;
688 if (!UIM_Insert(im_cursor_end.string))
690 quim.cursor_length += cl;
695 size_t epos = quim.edit_pos;
696 size_t elen = quim.edit_length;
698 if (!UIM_Insert(im_cursor.string))
701 quim.edit_pos = epos;
702 quim.edit_length = elen;
708 static void UIM_Update(void *cookie)
711 if (quim.pushed) // do not append color to commits, only to pushed objects
712 UIM_Insert2(quim.pc, quim.pc_len);
714 quim.setcursor(quim.edit_pos);
715 //Con_Print("UIM_Update\n");
717 // we could allocate a buffer in which we work
718 // and only update on "update"
719 // but who cares, seriously?
722 static void UIM_Activate(void *cookie, int nr, int display_limit)
725 //Con_Print("UIM_Activate\n");
727 quim.active_count = nr;
728 quim.active_limit = display_limit;
731 static void UIM_Select(void *cookie, int index)
738 //Con_Print("UIM_Select\n");
739 cd = quim_get_candidate(quim.ctx, index, quim.active_count);
740 str = quim_candidate_get_cand_str(cd);
744 // check if we even fit into that buffer
746 quim.edit_pos + quim.tail_length + slen - quim.cursor_inlength >= quim.buffer_size - 1)
748 // erase the part in the cursor:
749 memmove(quim.buffer + quim.cursor_inpos,
750 quim.buffer + quim.cursor_inpos + quim.cursor_inlength,
751 quim.buffer_size - quim.cursor_inpos - quim.cursor_inlength);
752 quim.cursor_length -= quim.cursor_inlength;
753 quim.cursor_inpos -= quim.cursor_inlength;
754 quim.edit_length -= quim.cursor_inlength;
755 quim.cursor_inlength = 0;
757 Con_Print("Failed to select entry: buffer too short\n");
761 // we can insert this stuff
762 // move the cursor-end + tail
763 memmove(quim.buffer + quim.cursor_inpos + quim.cursor_inlength,
764 quim.buffer + quim.cursor_pos + quim.cursor_length,
765 quim.buffer_size - quim.cursor_pos - quim.cursor_length);
767 memcpy(quim.buffer + quim.cursor_inpos, str, slen);
769 quim.edit_length = quim.edit_length + slen - quim.cursor_inlength;
770 quim.cursor_length = quim.cursor_length + slen - quim.cursor_inlength;
771 quim.cursor_inlength = slen;
773 quim_candidate_free(cd);
776 static void UIM_Shift(void *cookie, int dir)
779 Con_Print("^1ERROR: ^7UIM_Shift: Not implemented\n");
782 static void UIM_Deactivate(void *cookie)
786 //Con_Print("^3UIM: make UIM_Deactivate move the cursor...\n");
789 static void UIM_ConfigChanged(void *cookie)
792 //Con_Print("UIM_ConfigChanged\n");
795 static void UIM_PropListUpdate(void *cookie, const char *str)
800 char buffer[1024<<2];
801 //Con_Printf("UIM_PropListUpdate\n%s\n", str);
802 dpsnprintf(buffer, sizeof(buffer), "prop_list_update\ncharset=UTF-8\n%s", str);
803 quim_helper_send_message(quim.fd, buffer);