2 * Copyright (C) 2004 - 2009 Choe Hwanjin
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28 #include "hangul-gettext.h"
30 #include "hangulinternals.h"
33 * @defgroup hangulic 한글 입력 기능 구현
35 * @section hangulicusage Hangul Input Context의 사용법
36 * 이 섹션에서는 한글 입력 기능을 구현하는 핵심 기능에 대해 설명한다.
38 * 먼저 preedit string과 commit string 이 두 용어에 대해서 설멍하겠다.
39 * 이 두가지 용어는 Unix 계열의 입력기 framework에서 널리 쓰이는 표현이다.
41 * preedit string은 아직 조합중으로 어플리케이션에 완전히 입력되지 않은
42 * 스트링을 가리킨다. 일반적으로 한글 입력기에서는 역상으로 보이고
43 * 일본 중국어 입력기에서는 underline이 붙어 나타난다. 아직 완성이 되지
44 * 않은 스트링이므로 어플리케이션에 전달이 되지 않고 사라질 수도 있다.
46 * commit string은 조합이 완료되어 어플리케이션에 전달되는 스트링이다.
47 * 이 스트링은 실제 어플리케이션의 텍스트로 인식이 되므로 이 이후에는
48 * 더이상 입력기가 관리할 수 있는 데이터가 아니다.
50 * 한글 입력과정은 다음과 같은 과정을 거치게 된다.
51 * 입력된 영문 키를 그에 해댱하는 한글 자모로 변환한후 한글 자모를 모아
52 * 하나의 음절을 만든다. 여기까지 이루어지는 과정을 preedit string 형태로
53 * 사용자에게 계속 보이게 하는 것이 필요하다.
54 * 그리고는 한글 음절이 완성되고나면 그 글자를 어플리케이션에 commit
55 * string 형태로 보내여 입력을 완료하는 것이다. 다음 키를 받게 되면
58 * libhangul에서 한글 조합 기능은 @ref HangulInputContext를 이용해서 구현하게
59 * 되는데 기본 적인 방법은 @ref HangulInputContext에 사용자로부터의 입력을
60 * 순서대로 전달하면서 그 상태가 바뀜에 따라서 preedit 나 commit 스트링을
63 * 입력 코드들은 GUI 코드와 밀접하게 붙어 있어서 키 이벤트를 받아서
64 * 처리하도록 구현하는 것이 보통이다. 그런데 유닉스에는 많은 입력 프레임웍들이
65 * 난립하고 있는 상황이어서 매 입력 프레임웍마다 한글 조합 루틴을 작성해서
66 * 넣는 것은 비효율적이다. 간단한 API를 구현하여 여러 프레임웍에서 바로
67 * 사용할 수 있도록 구현하는 편이 사용성이 높아지게 된다.
69 * 그래서 libhangul에서는 키 이벤트를 따로 재정의하지 않고 ASCII 코드를
70 * 직접 사용하는 방향으로 재정의된 데이터가 많지 않도록 하였다.
71 * 실제 사용 방법은 말로 설명하는 것보다 샘플 코드를 사용하는 편이
72 * 이해가 빠를 것이다. 그래서 대략적인 진행 과정을 샘플 코드로
75 * 아래 예제는 실제로는 존재하지 않는 GUI 라이브러리 코드를 사용하였다.
76 * 실제 GUI 코드를 사용하면 코드가 너무 길어져서 설명이 어렵고 코드가
77 * 길어지면 핵심을 놓치기 쉽기 때문에 가공의 함수를 사용하였다.
78 * 또한 텍스트의 encoding conversion 관련된 부분도 생략하였다.
79 * 여기서 사용한 가공의 GUI 코드는 TWin으로 시작하게 하였다.
83 HangulInputContext* hic = hangul_ic_new("2");
86 // 아래는 키 입력만 처리하는 이벤트 루프이다.
87 // 실제 GUI코드는 이렇게 단순하진 않지만
88 // 편의상 키 입력만 처리하는 코드로 작성하였다.
90 TWinKeyEvent event = TWinGetKeyEvent(); // 키이벤트를 받는 이런 함수가
94 if (event.isBackspace()) {
95 // backspace를 ascii로 변환하기가 좀 꺼림직해서
96 // libhangul에서는 backspace 처리를 위한
98 res = hangul_ic_backspace(hic);
100 // 키 입력을 해당하는 ascii 코드로 변환한다.
101 // libhangul에서는 이 ascii 코드가 키 이벤트
103 int ascii = event.getAscii();
105 // 키 입력을 받았으면 이것을 hic에 먼저 보낸다.
106 // 그래야 hic가 이 키를 사용할 것인지 아닌지를 판단할 수 있다.
107 // 함수가 true를 리턴하면 이 키를 사용했다는 의미이므로
108 // GUI 코드가 이 키 입력을 프로세싱하지 않도록 해야 한다.
109 // 그렇지 않으면 한 키입력이 두번 프로세싱된다.
110 res = hangul_ic_process(hic, ascii);
113 // hic는 한번 키입력을 받고 나면 내부 상태 변화가 일어나고
114 // 완성된 글자를 어플리케이션에 보내야 하는 상황이 있을 수 있다.
115 // 이것을 HangulInputContext에서는 commit 스트링이 있는지로
116 // 판단한다. commit 스트링을 받아봐서 스트링이 있다면
117 // 그 스트링으로 입력이 완료된 걸로 본다.
118 const ucschar commit;
119 commit = hangul_ic_get_commit_string(hic);
120 if (commit[0] != 0) { // 스트링의 길이를 재서 commit 스트링이 있는지
122 TWinInputUnicodeChars(commit);
125 // 키입력 후에는 preedit string도 역시 변화하게 되는데
126 // 입력기 프레임웍에서는 이 스트링을 화면에 보여주어야
127 // 조합중인 글자가 화면에 표시가 되는 것이다.
128 const ucschar preedit;
129 preedit = hangul_ic_get_preedit_string(hic);
130 // 이 경우에는 스트링의 길이에 관계없이 항상 업데이트를
131 // 해야 한다. 왜냐하면 이전에 조합중이던 글자가 있다가
132 // 조합이 완료되면서 조합중인 상태의 글자가 없어질 수도 있기 때문에
133 // 스트링의 길이에 관계없이 현재 상태의 스트링을 preedit
134 // 스트링으로 보여주면 되는 것이다.
135 TWinUpdatePreeditString(preedit);
137 // 위 두작업이 끝난후에는 키 이벤트를 계속 프로세싱해야 하는지
139 // hic가 키 이벤트를 사용하지 않았다면 기본 GUI 코드에 계속해서
140 // 키 이벤트 프로세싱을 진행하도록 해야 한다.
142 TWinForwardKeyEventToUI(ascii);
144 ascii = GetKeyEvent();
147 hangul_ic_delete(hic);
153 * @file hangulinputcontext.c
158 * @typedef HangulInputContext
159 * @brief 한글 입력 상태를 관리하기 위한 오브젝트
161 * libhangul에서 제공하는 한글 조합 루틴에서 상태 정보를 저장하는 opaque
162 * 데이타 오브젝트이다. 이 오브젝트에 키입력 정보를 순차적으로 보내주면서
163 * preedit 스트링이나, commit 스트링을 받아서 처리하면 한글 입력 기능을
165 * 내부의 데이터 멤버는 공개되어 있지 않다. 각각의 멤버는 accessor 함수로만
177 #define HANGUL_KEYBOARD_TABLE_SIZE 0x80
179 typedef void (*HangulOnTranslate) (HangulInputContext*,
183 typedef bool (*HangulOnTransition) (HangulInputContext*,
188 typedef struct _HangulCombinationItem HangulCombinationItem;
190 struct _HangulKeyboard {
194 const ucschar* table;
195 const HangulCombination* combination;
198 struct _HangulCombinationItem {
203 struct _HangulCombination {
205 HangulCombinationItem *table;
208 struct _HangulBuffer {
217 struct _HangulInputContext {
220 const HangulKeyboard* keyboard;
225 ucschar preedit_string[64];
226 ucschar commit_string[64];
227 ucschar flushed_string[64];
229 HangulOnTranslate on_translate;
230 void* on_translate_data;
232 HangulOnTransition on_transition;
233 void* on_transition_data;
235 unsigned int use_jamo_mode_only : 1;
238 #include "hangulkeyboard.h"
240 static const HangulCombination hangul_combination_default = {
241 N_ELEMENTS(hangul_combination_table_default),
242 (HangulCombinationItem*)hangul_combination_table_default
245 static const HangulCombination hangul_combination_romaja = {
246 N_ELEMENTS(hangul_combination_table_romaja),
247 (HangulCombinationItem*)hangul_combination_table_romaja
250 static const HangulCombination hangul_combination_full = {
251 N_ELEMENTS(hangul_combination_table_full),
252 (HangulCombinationItem*)hangul_combination_table_full
255 static const HangulCombination hangul_combination_ahn = {
256 N_ELEMENTS(hangul_combination_table_ahn),
257 (HangulCombinationItem*)hangul_combination_table_ahn
260 static const HangulKeyboard hangul_keyboard_2 = {
261 HANGUL_KEYBOARD_TYPE_JAMO,
264 (ucschar*)hangul_keyboard_table_2,
265 &hangul_combination_default
268 static const HangulKeyboard hangul_keyboard_2y = {
269 HANGUL_KEYBOARD_TYPE_JAMO,
271 N_("Dubeolsik Yetgeul"),
272 (ucschar*)hangul_keyboard_table_2y,
273 &hangul_combination_full
276 static const HangulKeyboard hangul_keyboard_32 = {
277 HANGUL_KEYBOARD_TYPE_JASO,
279 N_("Sebeolsik Dubeol Layout"),
280 (ucschar*)hangul_keyboard_table_32,
281 &hangul_combination_default
284 static const HangulKeyboard hangul_keyboard_390 = {
285 HANGUL_KEYBOARD_TYPE_JASO,
288 (ucschar*)hangul_keyboard_table_390,
289 &hangul_combination_default
292 static const HangulKeyboard hangul_keyboard_3final = {
293 HANGUL_KEYBOARD_TYPE_JASO,
295 N_("Sebeolsik Final"),
296 (ucschar*)hangul_keyboard_table_3final,
297 &hangul_combination_default
300 static const HangulKeyboard hangul_keyboard_3sun = {
301 HANGUL_KEYBOARD_TYPE_JASO,
303 N_("Sebeolsik Noshift"),
304 (ucschar*)hangul_keyboard_table_3sun,
305 &hangul_combination_default
308 static const HangulKeyboard hangul_keyboard_3yet = {
309 HANGUL_KEYBOARD_TYPE_JASO,
311 N_("Sebeolsik Yetgeul"),
312 (ucschar*)hangul_keyboard_table_3yet,
313 &hangul_combination_full
316 static const HangulKeyboard hangul_keyboard_romaja = {
317 HANGUL_KEYBOARD_TYPE_ROMAJA,
320 (ucschar*)hangul_keyboard_table_romaja,
321 &hangul_combination_romaja
324 static const HangulKeyboard hangul_keyboard_ahn = {
325 HANGUL_KEYBOARD_TYPE_JASO,
328 (ucschar*)hangul_keyboard_table_ahn,
329 &hangul_combination_ahn
332 static const HangulKeyboard* hangul_keyboards[] = {
335 &hangul_keyboard_390,
336 &hangul_keyboard_3final,
337 &hangul_keyboard_3sun,
338 &hangul_keyboard_3yet,
340 &hangul_keyboard_romaja,
341 &hangul_keyboard_ahn,
345 static void hangul_buffer_push(HangulBuffer *buffer, ucschar ch);
346 static ucschar hangul_buffer_pop (HangulBuffer *buffer);
347 static ucschar hangul_buffer_peek(HangulBuffer *buffer);
349 static void hangul_buffer_clear(HangulBuffer *buffer);
350 static int hangul_buffer_get_string(HangulBuffer *buffer, ucschar*buf, int buflen);
351 static int hangul_buffer_get_jamo_string(HangulBuffer *buffer, ucschar *buf, int buflen);
353 static void hangul_ic_flush_internal(HangulInputContext *hic);
356 hangul_keyboard_new()
358 HangulKeyboard *keyboard = malloc(sizeof(HangulKeyboard));
359 if (keyboard != NULL) {
360 ucschar* table = malloc(sizeof(ucschar) * HANGUL_KEYBOARD_TABLE_SIZE);
363 for (i = 0; i < HANGUL_KEYBOARD_TABLE_SIZE; i++)
366 keyboard->table = table;
376 hangul_keyboard_get_value(const HangulKeyboard *keyboard, int key)
378 if (keyboard != NULL) {
379 if (key >= 0 && key < HANGUL_KEYBOARD_TABLE_SIZE)
380 return keyboard->table[key];
387 hangul_keyboard_set_value(HangulKeyboard *keyboard, int key, ucschar value)
389 if (keyboard != NULL) {
390 if (key >= 0 && key < N_ELEMENTS(keyboard->table)) {
391 ucschar* table = (ucschar*)keyboard->table;
398 hangul_keyboard_get_type(const HangulKeyboard *keyboard)
401 if (keyboard != NULL) {
402 type = keyboard->type;
408 hangul_keyboard_set_type(HangulKeyboard *keyboard, int type)
410 if (keyboard != NULL) {
411 keyboard->type = type;
416 hangul_keyboard_delete(HangulKeyboard *keyboard)
418 if (keyboard != NULL)
423 hangul_combination_new()
425 HangulCombination *combination = malloc(sizeof(HangulCombination));
426 if (combination != NULL) {
427 combination->size = 0;
428 combination->table = NULL;
436 hangul_combination_delete(HangulCombination *combination)
438 if (combination != NULL) {
439 if (combination->table != NULL)
440 free(combination->table);
446 hangul_combination_make_key(ucschar first, ucschar second)
448 return first << 16 | second;
452 hangul_combination_set_data(HangulCombination* combination,
453 ucschar* first, ucschar* second, ucschar* result,
456 if (combination == NULL)
459 if (n == 0 || n > ULONG_MAX / sizeof(HangulCombinationItem))
462 combination->table = malloc(sizeof(HangulCombinationItem) * n);
463 if (combination->table != NULL) {
466 combination->size = n;
467 for (i = 0; i < n; i++) {
468 combination->table[i].key = hangul_combination_make_key(first[i], second[i]);
469 combination->table[i].code = result[i];
478 hangul_combination_cmp(const void* p1, const void* p2)
480 const HangulCombinationItem *item1 = p1;
481 const HangulCombinationItem *item2 = p2;
483 /* key는 unsigned int이므로 단순히 빼서 리턴하면 안된다.
484 * 두 수의 차가 큰 경우 int로 변환하면서 음수가 될 수 있다. */
485 if (item1->key < item2->key)
487 else if (item1->key > item2->key)
494 hangul_combination_combine(const HangulCombination* combination,
495 ucschar first, ucschar second)
497 HangulCombinationItem *res;
498 HangulCombinationItem key;
500 if (combination == NULL)
503 key.key = hangul_combination_make_key(first, second);
504 res = bsearch(&key, combination->table, combination->size,
505 sizeof(combination->table[0]), hangul_combination_cmp);
513 hangul_buffer_is_empty(HangulBuffer *buffer)
515 return buffer->choseong == 0 && buffer->jungseong == 0 &&
516 buffer->jongseong == 0;
520 hangul_buffer_has_choseong(HangulBuffer *buffer)
522 return buffer->choseong != 0;
526 hangul_buffer_has_jungseong(HangulBuffer *buffer)
528 return buffer->jungseong != 0;
532 hangul_buffer_has_jongseong(HangulBuffer *buffer)
534 return buffer->jongseong != 0;
538 hangul_buffer_push(HangulBuffer *buffer, ucschar ch)
540 if (hangul_is_choseong(ch)) {
541 buffer->choseong = ch;
542 } else if (hangul_is_jungseong(ch)) {
543 buffer->jungseong = ch;
544 } else if (hangul_is_jongseong(ch)) {
545 buffer->jongseong = ch;
549 buffer->stack[++buffer->index] = ch;
553 hangul_buffer_pop(HangulBuffer *buffer)
555 return buffer->stack[buffer->index--];
559 hangul_buffer_peek(HangulBuffer *buffer)
561 if (buffer->index < 0)
564 return buffer->stack[buffer->index];
568 hangul_buffer_clear(HangulBuffer *buffer)
570 buffer->choseong = 0;
571 buffer->jungseong = 0;
572 buffer->jongseong = 0;
575 buffer->stack[0] = 0;
576 buffer->stack[1] = 0;
577 buffer->stack[2] = 0;
578 buffer->stack[3] = 0;
579 buffer->stack[4] = 0;
580 buffer->stack[5] = 0;
581 buffer->stack[6] = 0;
582 buffer->stack[7] = 0;
583 buffer->stack[8] = 0;
584 buffer->stack[9] = 0;
585 buffer->stack[10] = 0;
586 buffer->stack[11] = 0;
590 hangul_buffer_get_jamo_string(HangulBuffer *buffer, ucschar *buf, int buflen)
594 if (buffer->choseong || buffer->jungseong || buffer->jongseong) {
595 if (buffer->choseong) {
596 buf[n++] = buffer->choseong;
598 buf[n++] = HANGUL_CHOSEONG_FILLER;
600 if (buffer->jungseong) {
601 buf[n++] = buffer->jungseong;
603 buf[n++] = HANGUL_JUNGSEONG_FILLER;
605 if (buffer->jongseong) {
606 buf[n++] = buffer->jongseong;
616 hangul_jaso_to_string(ucschar cho, ucschar jung, ucschar jong,
617 ucschar *buf, int len)
624 /* have cho, jung, jong or no jong */
625 ch = hangul_jamo_to_syllable(cho, jung, jong);
629 /* 한글 음절로 표현 불가능한 경우 */
639 buf[n++] = HANGUL_JUNGSEONG_FILLER;
643 ch = hangul_jamo_to_cjamo(cho);
644 if (hangul_is_cjamo(ch)) {
648 buf[n++] = HANGUL_JUNGSEONG_FILLER;
655 /* have jung, jong */
656 buf[n++] = HANGUL_CHOSEONG_FILLER;
661 ch = hangul_jamo_to_cjamo(jung);
662 if (hangul_is_cjamo(ch)) {
665 buf[n++] = HANGUL_CHOSEONG_FILLER;
672 ch = hangul_jamo_to_cjamo(jong);
673 if (hangul_is_cjamo(ch)) {
676 buf[n++] = HANGUL_CHOSEONG_FILLER;
677 buf[n++] = HANGUL_JUNGSEONG_FILLER;
692 hangul_buffer_get_string(HangulBuffer *buffer, ucschar *buf, int buflen)
694 return hangul_jaso_to_string(buffer->choseong,
701 hangul_buffer_backspace(HangulBuffer *buffer)
703 if (buffer->index >= 0) {
704 ucschar ch = hangul_buffer_pop(buffer);
708 if (buffer->index >= 0) {
709 if (hangul_is_choseong(ch)) {
710 ch = hangul_buffer_peek(buffer);
711 buffer->choseong = hangul_is_choseong(ch) ? ch : 0;
713 } else if (hangul_is_jungseong(ch)) {
714 ch = hangul_buffer_peek(buffer);
715 buffer->jungseong = hangul_is_jungseong(ch) ? ch : 0;
717 } else if (hangul_is_jongseong(ch)) {
718 ch = hangul_buffer_peek(buffer);
719 buffer->jongseong = hangul_is_jongseong(ch) ? ch : 0;
723 buffer->choseong = 0;
724 buffer->jungseong = 0;
725 buffer->jongseong = 0;
733 hangul_ic_push(HangulInputContext *hic, ucschar c)
735 ucschar buf[64] = { 0, };
736 if (hic->on_transition != NULL) {
737 ucschar cho, jung, jong;
738 if (hangul_is_choseong(c)) {
740 jung = hic->buffer.jungseong;
741 jong = hic->buffer.jongseong;
742 } else if (hangul_is_jungseong(c)) {
743 cho = hic->buffer.choseong;
745 jong = hic->buffer.jongseong;
746 } else if (hangul_is_jongseong(c)) {
747 cho = hic->buffer.choseong;
748 jung = hic->buffer.jungseong;
751 hangul_ic_flush_internal(hic);
755 hangul_jaso_to_string(cho, jung, jong, buf, N_ELEMENTS(buf));
756 if (!hic->on_transition(hic, c, buf, hic->on_transition_data)) {
757 hangul_ic_flush_internal(hic);
761 if (!hangul_is_jamo(c)) {
762 hangul_ic_flush_internal(hic);
767 hangul_buffer_push(&hic->buffer, c);
771 static inline ucschar
772 hangul_ic_pop(HangulInputContext *hic)
774 return hangul_buffer_pop(&hic->buffer);
777 static inline ucschar
778 hangul_ic_peek(HangulInputContext *hic)
780 return hangul_buffer_peek(&hic->buffer);
784 hangul_ic_save_preedit_string(HangulInputContext *hic)
786 if (hic->output_mode == HANGUL_OUTPUT_JAMO) {
787 hangul_buffer_get_jamo_string(&hic->buffer,
789 N_ELEMENTS(hic->preedit_string));
791 hangul_buffer_get_string(&hic->buffer,
793 N_ELEMENTS(hic->preedit_string));
798 hangul_ic_append_commit_string(HangulInputContext *hic, ucschar ch)
802 for (i = 0; i < N_ELEMENTS(hic->commit_string); i++) {
803 if (hic->commit_string[i] == 0)
807 if (i + 1 < N_ELEMENTS(hic->commit_string)) {
808 hic->commit_string[i++] = ch;
809 hic->commit_string[i] = 0;
814 hangul_ic_save_commit_string(HangulInputContext *hic)
816 ucschar *string = hic->commit_string;
817 int len = N_ELEMENTS(hic->commit_string);
826 if (hic->output_mode == HANGUL_OUTPUT_JAMO) {
827 hangul_buffer_get_jamo_string(&hic->buffer, string, len);
829 hangul_buffer_get_string(&hic->buffer, string, len);
832 hangul_buffer_clear(&hic->buffer);
836 hangul_ic_choseong_to_jongseong(HangulInputContext* hic, ucschar cho)
838 ucschar jong = hangul_choseong_to_jongseong(cho);
839 if (hangul_is_jongseong_conjoinable(jong)) {
842 /* 옛글 조합 규칙을 사용하는 자판의 경우에는 종성이 conjoinable
844 if (hic->keyboard->combination == &hangul_combination_full) {
853 hangul_ic_process_jamo(HangulInputContext *hic, ucschar ch)
858 if (!hangul_is_jamo(ch) && ch > 0) {
859 hangul_ic_save_commit_string(hic);
860 hangul_ic_append_commit_string(hic, ch);
864 if (hic->buffer.jongseong) {
865 if (hangul_is_choseong(ch)) {
866 jong = hangul_ic_choseong_to_jongseong(hic, ch);
867 combined = hangul_combination_combine(hic->keyboard->combination,
868 hic->buffer.jongseong, jong);
869 if (hangul_is_jongseong(combined)) {
870 if (!hangul_ic_push(hic, combined)) {
871 if (!hangul_ic_push(hic, ch)) {
876 hangul_ic_save_commit_string(hic);
877 if (!hangul_ic_push(hic, ch)) {
881 } else if (hangul_is_jungseong(ch)) {
883 pop = hangul_ic_pop(hic);
884 peek = hangul_ic_peek(hic);
886 if (hangul_is_jongseong(peek)) {
887 ucschar choseong = hangul_jongseong_get_diff(peek,
888 hic->buffer.jongseong);
890 hangul_ic_save_commit_string(hic);
891 if (!hangul_ic_push(hic, ch)) {
895 hic->buffer.jongseong = peek;
896 hangul_ic_save_commit_string(hic);
897 hangul_ic_push(hic, choseong);
898 if (!hangul_ic_push(hic, ch)) {
903 hic->buffer.jongseong = 0;
904 hangul_ic_save_commit_string(hic);
905 hangul_ic_push(hic, hangul_jongseong_to_choseong(pop));
906 if (!hangul_ic_push(hic, ch)) {
913 } else if (hic->buffer.jungseong) {
914 if (hangul_is_choseong(ch)) {
915 if (hic->buffer.choseong) {
916 jong = hangul_ic_choseong_to_jongseong(hic, ch);
917 if (hangul_is_jongseong(jong)) {
918 if (!hangul_ic_push(hic, jong)) {
919 if (!hangul_ic_push(hic, ch)) {
924 hangul_ic_save_commit_string(hic);
925 if (!hangul_ic_push(hic, ch)) {
930 if (!hangul_ic_push(hic, ch)) {
931 if (!hangul_ic_push(hic, ch)) {
936 } else if (hangul_is_jungseong(ch)) {
937 combined = hangul_combination_combine(hic->keyboard->combination,
938 hic->buffer.jungseong, ch);
939 if (hangul_is_jungseong(combined)) {
940 if (!hangul_ic_push(hic, combined)) {
944 hangul_ic_save_commit_string(hic);
945 if (!hangul_ic_push(hic, ch)) {
952 } else if (hic->buffer.choseong) {
953 if (hangul_is_choseong(ch)) {
954 combined = hangul_combination_combine(hic->keyboard->combination,
955 hic->buffer.choseong, ch);
956 if (!hangul_ic_push(hic, combined)) {
957 if (!hangul_ic_push(hic, ch)) {
962 if (!hangul_ic_push(hic, ch)) {
963 if (!hangul_ic_push(hic, ch)) {
969 if (!hangul_ic_push(hic, ch)) {
974 hangul_ic_save_preedit_string(hic);
978 hangul_ic_flush_internal(hic);
983 hangul_ic_process_jaso(HangulInputContext *hic, ucschar ch)
985 if (hangul_is_choseong(ch)) {
986 if (hic->buffer.choseong == 0) {
987 if (!hangul_ic_push(hic, ch)) {
988 if (!hangul_ic_push(hic, ch)) {
993 ucschar choseong = 0;
994 if (hangul_is_choseong(hangul_ic_peek(hic))) {
995 choseong = hangul_combination_combine(hic->keyboard->combination,
996 hic->buffer.choseong, ch);
999 if (!hangul_ic_push(hic, choseong)) {
1000 if (!hangul_ic_push(hic, choseong)) {
1005 hangul_ic_save_commit_string(hic);
1006 if (!hangul_ic_push(hic, ch)) {
1011 } else if (hangul_is_jungseong(ch)) {
1012 if (hic->buffer.jungseong == 0) {
1013 if (!hangul_ic_push(hic, ch)) {
1014 if (!hangul_ic_push(hic, ch)) {
1019 ucschar jungseong = 0;
1020 if (hangul_is_jungseong(hangul_ic_peek(hic))) {
1021 jungseong = hangul_combination_combine(hic->keyboard->combination,
1022 hic->buffer.jungseong, ch);
1025 if (!hangul_ic_push(hic, jungseong)) {
1026 if (!hangul_ic_push(hic, jungseong)) {
1031 hangul_ic_save_commit_string(hic);
1032 if (!hangul_ic_push(hic, ch)) {
1033 if (!hangul_ic_push(hic, ch)) {
1039 } else if (hangul_is_jongseong(ch)) {
1040 if (hic->buffer.jongseong == 0) {
1041 if (!hangul_ic_push(hic, ch)) {
1042 if (!hangul_ic_push(hic, ch)) {
1047 ucschar jongseong = 0;
1048 if (hangul_is_jongseong(hangul_ic_peek(hic))) {
1049 jongseong = hangul_combination_combine(hic->keyboard->combination,
1050 hic->buffer.jongseong, ch);
1053 if (!hangul_ic_push(hic, jongseong)) {
1054 if (!hangul_ic_push(hic, jongseong)) {
1059 hangul_ic_save_commit_string(hic);
1060 if (!hangul_ic_push(hic, ch)) {
1061 if (!hangul_ic_push(hic, ch)) {
1067 } else if (ch > 0) {
1068 hangul_ic_save_commit_string(hic);
1069 hangul_ic_append_commit_string(hic, ch);
1071 hangul_ic_save_commit_string(hic);
1075 hangul_ic_save_preedit_string(hic);
1080 hangul_ic_process_romaja(HangulInputContext *hic, int ascii, ucschar ch)
1085 if (!hangul_is_jamo(ch) && ch > 0) {
1086 hangul_ic_save_commit_string(hic);
1087 hangul_ic_append_commit_string(hic, ch);
1091 if (isupper(ascii)) {
1092 hangul_ic_save_commit_string(hic);
1095 if (hic->buffer.jongseong) {
1096 if (ascii == 'x' || ascii == 'X') {
1098 hangul_ic_save_commit_string(hic);
1099 if (!hangul_ic_push(hic, ch)) {
1102 } else if (hangul_is_choseong(ch) || hangul_is_jongseong(ch)) {
1103 if (hangul_is_jongseong(ch))
1106 jong = hangul_ic_choseong_to_jongseong(hic, ch);
1107 combined = hangul_combination_combine(hic->keyboard->combination,
1108 hic->buffer.jongseong, jong);
1109 if (hangul_is_jongseong(combined)) {
1110 if (!hangul_ic_push(hic, combined)) {
1111 if (!hangul_ic_push(hic, ch)) {
1116 hangul_ic_save_commit_string(hic);
1117 if (!hangul_ic_push(hic, ch)) {
1121 } else if (hangul_is_jungseong(ch)) {
1122 if (hic->buffer.jongseong == 0x11bc) {
1123 hangul_ic_save_commit_string(hic);
1124 hic->buffer.choseong = 0x110b;
1125 hangul_ic_push(hic, ch);
1128 pop = hangul_ic_pop(hic);
1129 peek = hangul_ic_peek(hic);
1131 if (hangul_is_jungseong(peek)) {
1132 if (pop == 0x11aa) {
1133 hic->buffer.jongseong = 0x11a8;
1136 hic->buffer.jongseong = 0;
1138 hangul_ic_save_commit_string(hic);
1139 hangul_ic_push(hic, hangul_jongseong_to_choseong(pop));
1140 if (!hangul_ic_push(hic, ch)) {
1144 ucschar choseong = 0, jongseong = 0;
1145 hangul_jongseong_decompose(hic->buffer.jongseong,
1146 &jongseong, &choseong);
1147 hic->buffer.jongseong = jongseong;
1148 hangul_ic_save_commit_string(hic);
1149 hangul_ic_push(hic, choseong);
1150 if (!hangul_ic_push(hic, ch)) {
1158 } else if (hic->buffer.jungseong) {
1159 if (hangul_is_choseong(ch)) {
1160 if (hic->buffer.choseong) {
1161 jong = hangul_ic_choseong_to_jongseong(hic, ch);
1162 if (hangul_is_jongseong(jong)) {
1163 if (!hangul_ic_push(hic, jong)) {
1164 if (!hangul_ic_push(hic, ch)) {
1169 hangul_ic_save_commit_string(hic);
1170 if (!hangul_ic_push(hic, ch)) {
1175 if (!hangul_ic_push(hic, ch)) {
1176 if (!hangul_ic_push(hic, ch)) {
1181 } else if (hangul_is_jungseong(ch)) {
1182 combined = hangul_combination_combine(hic->keyboard->combination,
1183 hic->buffer.jungseong, ch);
1184 if (hangul_is_jungseong(combined)) {
1185 if (!hangul_ic_push(hic, combined)) {
1189 hangul_ic_save_commit_string(hic);
1190 hic->buffer.choseong = 0x110b;
1191 if (!hangul_ic_push(hic, ch)) {
1195 } else if (hangul_is_jongseong(ch)) {
1196 if (!hangul_ic_push(hic, ch)) {
1197 if (!hangul_ic_push(hic, ch)) {
1204 } else if (hic->buffer.choseong) {
1205 if (hangul_is_choseong(ch)) {
1206 combined = hangul_combination_combine(hic->keyboard->combination,
1207 hic->buffer.choseong, ch);
1208 if (combined == 0) {
1209 hic->buffer.jungseong = 0x1173;
1210 hangul_ic_flush_internal(hic);
1211 if (!hangul_ic_push(hic, ch)) {
1215 if (!hangul_ic_push(hic, combined)) {
1216 if (!hangul_ic_push(hic, ch)) {
1221 } else if (hangul_is_jongseong(ch)) {
1222 hic->buffer.jungseong = 0x1173;
1223 hangul_ic_save_commit_string(hic);
1224 if (ascii == 'x' || ascii == 'X')
1226 if (!hangul_ic_push(hic, ch)) {
1230 if (!hangul_ic_push(hic, ch)) {
1231 if (!hangul_ic_push(hic, ch)) {
1237 if (ascii == 'x' || ascii == 'X') {
1241 if (!hangul_ic_push(hic, ch)) {
1244 if (hic->buffer.choseong == 0 && hic->buffer.jungseong != 0)
1245 hic->buffer.choseong = 0x110b;
1249 hangul_ic_save_preedit_string(hic);
1253 hangul_ic_flush_internal(hic);
1259 * @brief 키 입력을 처리하여 실제로 한글 조합을 하는 함수
1260 * @param hic @ref HangulInputContext 오브젝트
1261 * @param ascii 키 이벤트
1262 * @return @ref HangulInputContext가 이 키를 사용했으면 true,
1265 * ascii 값으로 주어진 키 이벤트를 받아서 내부의 한글 조합 상태를
1266 * 변화시키고, preedit, commit 스트링을 저장한다.
1268 * libhangul의 키 이벤트 프로세스는 ASCII 코드 값을 기준으로 처리한다.
1269 * 이 키 값은 US Qwerty 자판 배열에서의 키 값에 해당한다.
1270 * 따라서 유럽어 자판을 사용하는 경우에는 해당 키의 ASCII 코드를 직접
1271 * 전달하면 안되고, 그 키가 US Qwerty 자판이었을 경우에 발생할 수 있는
1272 * ASCII 코드 값을 주어야 한다.
1273 * 또한 ASCII 코드 이므로 Shift 상태는 대문자로 전달이 된다.
1274 * Capslock이 눌린 경우에는 대소문자를 뒤바꾸어 보내주지 않으면
1275 * 마치 Shift가 눌린 것 처럼 동작할 수 있으므로 주의한다.
1276 * preedit, commit 스트링은 hangul_ic_get_preedit_string(),
1277 * hangul_ic_get_commit_string() 함수를 이용하여 구할 수 있다.
1279 * 이 함수의 사용법에 대한 설명은 @ref hangulicusage 부분을 참조한다.
1281 * @remarks 이 함수는 @ref HangulInputContext의 상태를 변화 시킨다.
1284 hangul_ic_process(HangulInputContext *hic, int ascii)
1291 hic->preedit_string[0] = 0;
1292 hic->commit_string[0] = 0;
1294 c = hangul_keyboard_get_value(hic->keyboard, ascii);
1295 if (hic->on_translate != NULL)
1296 hic->on_translate(hic, ascii, &c, hic->on_translate_data);
1298 if (hangul_keyboard_get_type(hic->keyboard) == HANGUL_KEYBOARD_TYPE_JAMO)
1299 return hangul_ic_process_jamo(hic, c);
1300 else if (hangul_keyboard_get_type(hic->keyboard) == HANGUL_KEYBOARD_TYPE_JASO)
1301 return hangul_ic_process_jaso(hic, c);
1303 return hangul_ic_process_romaja(hic, ascii, c);
1308 * @brief 현재 상태의 preedit string을 구하는 함수
1309 * @param hic preedit string을 구하고자하는 입력 상태 object
1310 * @return UCS4 preedit 스트링, 이 스트링은 @a hic 내부의 데이터이므로
1311 * 수정하거나 free해서는 안된다.
1313 * 이 함수는 @a hic 내부의 현재 상태의 preedit string을 리턴한다.
1314 * 따라서 hic가 다른 키 이벤트를 처리하고 나면 그 내용이 바뀔 수 있다.
1316 * @remarks 이 함수는 @ref HangulInputContext의 상태를 변화 시키지 않는다.
1319 hangul_ic_get_preedit_string(HangulInputContext *hic)
1324 return hic->preedit_string;
1329 * @brief 현재 상태의 commit string을 구하는 함수
1330 * @param hic commit string을 구하고자하는 입력 상태 object
1331 * @return UCS4 commit 스트링, 이 스트링은 @a hic 내부의 데이터이므로
1332 * 수정하거나 free해서는 안된다.
1334 * 이 함수는 @a hic 내부의 현재 상태의 commit string을 리턴한다.
1335 * 따라서 hic가 다른 키 이벤트를 처리하고 나면 그 내용이 바뀔 수 있다.
1337 * @remarks 이 함수는 @ref HangulInputContext의 상태를 변화 시키지 않는다.
1340 hangul_ic_get_commit_string(HangulInputContext *hic)
1345 return hic->commit_string;
1350 * @brief @ref HangulInputContext를 초기상태로 되돌리는 함수
1351 * @param hic @ref HangulInputContext를 가리키는 포인터
1353 * 이 함수는 @a hic가 가리키는 @ref HangulInputContext의 상태를
1354 * 처음 상태로 되돌린다. preedit 스트링, commit 스트링, flush 스트링이
1355 * 없어지고, 입력되었던 키에 대한 기록이 없어진다.
1356 * 영어 상태로 바뀌는 것이 아니다.
1358 * 비교: hangul_ic_flush()
1360 * @remarks 이 함수는 @ref HangulInputContext의 상태를 변화 시킨다.
1363 hangul_ic_reset(HangulInputContext *hic)
1368 hic->preedit_string[0] = 0;
1369 hic->commit_string[0] = 0;
1370 hic->flushed_string[0] = 0;
1372 hangul_buffer_clear(&hic->buffer);
1375 /* append current preedit to the commit buffer.
1376 * this function does not clear previously made commit string. */
1378 hangul_ic_flush_internal(HangulInputContext *hic)
1380 hic->preedit_string[0] = 0;
1382 hangul_ic_save_commit_string(hic);
1383 hangul_buffer_clear(&hic->buffer);
1388 * @brief @ref HangulInputContext의 입력 상태를 완료하는 함수
1389 * @param hic @ref HangulInputContext를 가리키는 포인터
1390 * @return 조합 완료된 스트링, 스트링의 길이가 0이면 조합 완료된 스트링이
1393 * 이 함수는 @a hic가 가리키는 @ref HangulInputContext의 입력 상태를 완료한다.
1394 * 조합중이던 스트링을 완성하여 리턴한다. 그리고 입력 상태가 초기 상태로
1395 * 되돌아 간다. 조합중이던 글자를 강제로 commit하고 싶을때 사용하는 함수다.
1396 * 보통의 경우 입력 framework에서 focus가 나갈때 이 함수를 불러서 마지막
1397 * 상태를 완료해야 조합중이던 글자를 잃어버리지 않게 된다.
1399 * 비교: hangul_ic_reset()
1401 * @remarks 이 함수는 @ref HangulInputContext의 상태를 변화 시킨다.
1404 hangul_ic_flush(HangulInputContext *hic)
1409 // get the remaining string and clear the buffer
1410 hic->preedit_string[0] = 0;
1411 hic->commit_string[0] = 0;
1412 hic->flushed_string[0] = 0;
1414 if (hic->output_mode == HANGUL_OUTPUT_JAMO) {
1415 hangul_buffer_get_jamo_string(&hic->buffer, hic->flushed_string,
1416 N_ELEMENTS(hic->flushed_string));
1418 hangul_buffer_get_string(&hic->buffer, hic->flushed_string,
1419 N_ELEMENTS(hic->flushed_string));
1422 hangul_buffer_clear(&hic->buffer);
1424 return hic->flushed_string;
1429 * @brief @ref HangulInputContext가 backspace 키를 처리하도록 하는 함수
1430 * @param hic @ref HangulInputContext를 가리키는 포인터
1431 * @return @a hic가 키를 사용했으면 true, 사용하지 않았으면 false
1433 * 이 함수는 @a hic가 가리키는 @ref HangulInputContext의 조합중이던 글자를
1434 * 뒤에서부터 하나 지우는 기능을 한다. backspace 키를 눌렀을 때 발생하는
1435 * 동작을 한다. 따라서 이 함수를 부르고 나면 preedit string이 바뀌므로
1438 * @remarks 이 함수는 @ref HangulInputContext의 상태를 변화 시킨다.
1441 hangul_ic_backspace(HangulInputContext *hic)
1448 hic->preedit_string[0] = 0;
1449 hic->commit_string[0] = 0;
1451 ret = hangul_buffer_backspace(&hic->buffer);
1453 hangul_ic_save_preedit_string(hic);
1459 * @brief @ref HangulInputContext가 조합중인 글자를 가지고 있는지 확인하는 함수
1460 * @param hic @ref HangulInputContext를 가리키는 포인터
1462 * @ref HangulInputContext가 조합중인 글자가 있으면 true를 리턴한다.
1464 * @remarks 이 함수는 @ref HangulInputContext의 상태를 변화 시키지 않는다.
1467 hangul_ic_is_empty(HangulInputContext *hic)
1469 return hangul_buffer_is_empty(&hic->buffer);
1474 * @brief @ref HangulInputContext가 조합중인 초성을 가지고 있는지 확인하는 함수
1475 * @param hic @ref HangulInputContext를 가리키는 포인터
1477 * @ref HangulInputContext가 조합중인 글자가 초성이 있으면 true를 리턴한다.
1479 * @remarks 이 함수는 @ref HangulInputContext의 상태를 변화 시키지 않는다.
1482 hangul_ic_has_choseong(HangulInputContext *hic)
1484 return hangul_buffer_has_choseong(&hic->buffer);
1489 * @brief @ref HangulInputContext가 조합중인 중성을 가지고 있는지 확인하는 함수
1490 * @param hic @ref HangulInputContext를 가리키는 포인터
1492 * @ref HangulInputContext가 조합중인 글자가 중성이 있으면 true를 리턴한다.
1494 * @remarks 이 함수는 @ref HangulInputContext의 상태를 변화 시키지 않는다.
1497 hangul_ic_has_jungseong(HangulInputContext *hic)
1499 return hangul_buffer_has_jungseong(&hic->buffer);
1504 * @brief @ref HangulInputContext가 조합중인 종성을 가지고 있는지 확인하는 함수
1505 * @param hic @ref HangulInputContext를 가리키는 포인터
1507 * @ref HangulInputContext가 조합중인 글자가 종성이 있으면 true를 리턴한다.
1509 * @remarks 이 함수는 @ref HangulInputContext의 상태를 변화 시키지 않는다.
1512 hangul_ic_has_jongseong(HangulInputContext *hic)
1514 return hangul_buffer_has_jongseong(&hic->buffer);
1518 hangul_ic_set_output_mode(HangulInputContext *hic, int mode)
1523 if (!hic->use_jamo_mode_only)
1524 hic->output_mode = mode;
1528 hangul_ic_connect_translate (HangulInputContext* hic,
1529 HangulOnTranslate callback,
1533 hic->on_translate = callback;
1534 hic->on_translate_data = user_data;
1539 hangul_ic_connect_transition(HangulInputContext* hic,
1540 HangulOnTransition callback,
1544 hic->on_transition = callback;
1545 hic->on_transition_data = user_data;
1549 void hangul_ic_connect_callback(HangulInputContext* hic, const char* event,
1550 void* callback, void* user_data)
1552 if (hic == NULL || event == NULL)
1555 if (strcasecmp(event, "translate") == 0) {
1556 hic->on_translate = (HangulOnTranslate)callback;
1557 hic->on_translate_data = user_data;
1558 } else if (strcasecmp(event, "transition") == 0) {
1559 hic->on_transition = (HangulOnTransition)callback;
1560 hic->on_transition_data = user_data;
1565 hangul_ic_set_keyboard(HangulInputContext *hic, const HangulKeyboard* keyboard)
1567 if (hic == NULL || keyboard == NULL)
1570 hic->keyboard = keyboard;
1573 static const HangulKeyboard*
1574 hangul_ic_get_keyboard_by_id(const char* id)
1579 /* hangul_keyboards 테이블은 id 순으로 정렬되어 있지 않으므로
1580 * binary search를 할수 없고 linear search를 한다. */
1581 n = hangul_ic_get_n_keyboards();
1582 for (i = 0; i < n; ++i) {
1583 const HangulKeyboard* keyboard = hangul_keyboards[i];
1584 if (strcmp(id, keyboard->id) == 0) {
1594 * @brief @ref HangulInputContext의 자판 배열을 바꾸는 함수
1595 * @param hic @ref HangulInputContext 오브젝트
1596 * @param id 선택하고자 하는 자판, 아래와 같은 값을 선택할 수 있다.
1598 * @li "32" 세벌식 자판으로 두벌식의 배열을 가진 자판.
1599 * 두벌식 사용자가 쉽게 세벌식 테스트를 할 수 있다.
1600 * shift를 누르면 자음이 종성으로 동작한다.
1605 * @li "ro" 로마자 방식 자판
1608 * 이 함수는 @ref HangulInputContext의 자판을 @a id로 지정된 것으로 변경한다.
1610 * @remarks 이 함수는 @ref HangulInputContext의 내부 조합 상태에는 영향을
1611 * 미치지 않는다. 따라서 입력 중간에 자판을 변경하더라도 조합 상태는 유지된다.
1614 hangul_ic_select_keyboard(HangulInputContext *hic, const char* id)
1616 const HangulKeyboard* keyboard;
1624 keyboard = hangul_ic_get_keyboard_by_id(id);
1625 if (keyboard != NULL) {
1626 hic->keyboard = keyboard;
1628 hic->keyboard = &hangul_keyboard_2;
1633 hangul_ic_set_combination(HangulInputContext *hic,
1634 const HangulCombination* combination)
1640 * @brief @ref HangulInputContext 오브젝트를 생성한다.
1641 * @param keyboard 사용하고자 하는 키보드, 사용 가능한 값에 대해서는
1642 * hangul_ic_select_keyboard() 함수 설명을 참조한다.
1643 * @return 새로 생성된 @ref HangulInputContext에 대한 포인터
1645 * 이 함수는 한글 조합 기능을 제공하는 @ref HangulInputContext 오브젝트를
1646 * 생성한다. 생성할때 지정한 자판은 나중에 hangul_ic_select_keyboard() 함수로
1648 * 더이상 사용하지 않을 때에는 hangul_ic_delete() 함수로 삭제해야 한다.
1651 hangul_ic_new(const char* keyboard)
1653 HangulInputContext *hic;
1655 hic = malloc(sizeof(HangulInputContext));
1659 hic->preedit_string[0] = 0;
1660 hic->commit_string[0] = 0;
1661 hic->flushed_string[0] = 0;
1663 hic->on_translate = NULL;
1664 hic->on_translate_data = NULL;
1666 hic->on_transition = NULL;
1667 hic->on_transition_data = NULL;
1669 hic->use_jamo_mode_only = FALSE;
1671 hangul_ic_set_output_mode(hic, HANGUL_OUTPUT_SYLLABLE);
1672 hangul_ic_select_keyboard(hic, keyboard);
1674 hangul_buffer_clear(&hic->buffer);
1681 * @brief @ref HangulInputContext를 삭제하는 함수
1682 * @param hic @ref HangulInputContext 오브젝트
1684 * @a hic가 가리키는 @ref HangulInputContext 오브젝트의 메모리를 해제한다.
1685 * hangul_ic_new() 함수로 생성된 모든 @ref HangulInputContext 오브젝트는
1686 * 이 함수로 메모리해제를 해야 한다.
1687 * 메모리 해제 과정에서 상태 변화는 일어나지 않으므로 마지막 입력된
1688 * 조합중이던 내용은 사라지게 된다.
1691 hangul_ic_delete(HangulInputContext *hic)
1700 hangul_ic_get_n_keyboards()
1702 return N_ELEMENTS(hangul_keyboards);
1706 hangul_ic_get_keyboard_id(unsigned index_)
1708 if (index_ < N_ELEMENTS(hangul_keyboards)) {
1709 return hangul_keyboards[index_]->id;
1716 hangul_ic_get_keyboard_name(unsigned index_)
1719 static bool isGettextInitialized = false;
1720 if (!isGettextInitialized) {
1721 isGettextInitialized = true;
1722 bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
1723 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
1727 if (index_ < N_ELEMENTS(hangul_keyboards)) {
1728 return _(hangul_keyboards[index_]->name);
1736 * @brief 주어진 hic가 transliteration method인지 판별
1737 * @param hic 상태를 알고자 하는 HangulInputContext 포인터
1738 * @return hic가 transliteration method인 경우 true를 리턴, 아니면 false
1740 * 이 함수는 @a hic 가 transliteration method인지 판별하는 함수다.
1741 * 이 함수가 false를 리턴할 경우에는 process 함수에 keycode를 넘기기 전에
1742 * 키보드 자판 배열에 독립적인 값으로 변환한 후 넘겨야 한다.
1743 * 그렇지 않으면 유럽어 자판과 한국어 자판을 같이 쓸때 한글 입력이 제대로
1747 hangul_ic_is_transliteration(HangulInputContext *hic)
1754 type = hangul_keyboard_get_type(hic->keyboard);
1755 if (type == HANGUL_KEYBOARD_TYPE_ROMAJA)