/** * IM-process a character. This function simply looks up the language from * IM and calls the appropriate im_event_<lang>() language-specific IM event * handler. im_event_c() is called by default if no language-specific * function is specified for the specified language. * * @param im IM-processed data to return to the caller function. * @param ks SDL_keysym typed on the keyboard. * * @return The number of characters in im->s that should not be committed. * In other words, the returned number of characters at the end of * im->s should be overwritten the next time im_read is called. * * @see im_event_c() * @see im_event_fns */ int im_read(IM_DATA* im, SDL_keysym ks) { // im_event_fp = NULL; int redraw = 0; /* Sanity check */ if(im->lang < 0 || im->lang >= NUM_LANGS) { fprintf(stderr, "im->lang out of range (%d), using default\n", im->lang); im->lang = LANG_DEFAULT; } /* Function pointer to the language-specific im_event_* function */ im_event_fp = im_event_fns[im->lang]; /* Run the language-specific IM or run the default C IM */ if(im_event_fp) redraw = (*im_event_fp)(im, ks); else redraw = im_event_c(im, ks); #ifdef IM_DEBUG wprintf(L"* [%8ls] [%8ls] %2d %2d (%2d)\n", im->s, im->buf, wcslen(im->s), wcslen(im->buf), im->redraw); #endif return redraw; }
/** * Thai IM. * * @see im_read */ static int im_event_th(IM_DATA* im, SDL_keysym ks) { static const char* lang_file = IMDIR "th.im"; enum { SEC_ENGLISH, SEC_THAI, SEC_TOTAL }; static CHARMAP cm; /* Handle event requests */ switch(im->request) { case 0: break; case IM_REQ_FREE: /* Free allocated resources */ charmap_free(&cm); /* go onto full reset */ case IM_REQ_RESET_FULL: /* Full reset */ cm.section = SEC_ENGLISH; im->tip_text = im_tip_text[IM_TIP_ENGLISH]; /* go onto soft reset */ case IM_REQ_RESET_SOFT: /* Soft reset */ im->s[0] = L'\0'; im->buf[0] = L'\0'; im->redraw = 0; cm.match_count = 0; cm.match_is_final = 0; cm.match_state = &cm.sections[cm.section]; cm.match_state_prev = &cm.sections[cm.section]; break; case IM_REQ_INIT: /* Initialization */ charmap_init(&cm); if(charmap_load(&cm, lang_file)) { fprintf(stderr, "Unable to load %s, defaulting to im_event_c\n", lang_file); im->lang = LANG_DEFAULT; return im_event_c(im, ks); } im_fullreset(im); #ifdef DEBUG printf("IM: Loaded '%s'\n", lang_file); #endif break; } if(im->request != IM_REQ_TRANSLATE) return 0; /* Discard redraw characters, so they can be redrawn */ if((int)wcslen(im->s) < im->redraw) im->redraw = wcslen(im->s); wcs_lshift(im->s, (wcslen(im->s) - im->redraw) ); /* Handle keys */ switch(ks.sym) { /* Keys to ignore */ case SDLK_NUMLOCK: case SDLK_CAPSLOCK: case SDLK_SCROLLOCK: case SDLK_LSHIFT: case SDLK_RSHIFT: case SDLK_LCTRL: case SDLK_RCTRL: case SDLK_LALT: case SDLK_LMETA: case SDLK_RMETA: case SDLK_LSUPER: case SDLK_RSUPER: case SDLK_MODE: case SDLK_COMPOSE: break; /* Right-Alt mapped to mode-switch */ case SDLK_RALT: cm.section = (++cm.section % SEC_TOTAL); /* Change section */ im_softreset(im); /* Soft reset */ /* Set tip text */ switch(cm.section) { case SEC_ENGLISH: im->tip_text = im_tip_text[IM_TIP_ENGLISH]; break; case SEC_THAI: im->tip_text = im_tip_text[IM_TIP_THAI]; break; } break; /* Enter finalizes previous redraw */ case SDLK_RETURN: if(im->redraw <= 0) { im->s[0] = L'\r'; im->s[1] = L'\0'; } im->buf[0] = L'\0'; im->redraw = 0; break; /* Actual character processing */ default: /* English mode */ if(cm.section == SEC_ENGLISH) { im->s[0] = ks.unicode; im->s[1] = L'\0'; im->buf[0] = L'\0'; } /* Thai mode */ else { wchar_t u = ks.unicode; im->s[0] = L'\0'; /* Zero-out output string */ wcsncat(im->buf, &u, 1); /* Copy new character */ /* Translate the characters */ im->redraw = 0; while(1) { const wchar_t* us = charmap_search(&cm, im->buf); #ifdef IM_DEBUG wprintf(L" [%8ls] [%8ls] %2d %2d\n", im->s, im->buf, wcslen(im->s), wcslen(im->buf)); #endif /* Match was found? */ if(us && wcslen(us)) { #ifdef IM_DEBUG wprintf(L" 1\n"); #endif wcscat(im->s, us); /* Final match */ if(cm.match_is_final) { wcs_lshift(im->buf, cm.match_count); cm.match_count = 0; cm.match_is_final = 0; } /* May need to be overwritten next time */ else { im->redraw += wcslen(us); break; } } /* No match, but more data is in the buffer */ else if(wcslen(im->buf) > 0) { /* If the input character has no state, it's its own state */ if(cm.match_count == 0) { #ifdef IM_DEBUG wprintf(L" 2a\n"); #endif wcsncat(im->s, im->buf, 1); wcs_lshift(im->buf, 1); cm.match_is_final = 0; } /* If the matched characters didn't consume all, it's own state */ else if((size_t)cm.match_count != wcslen(im->buf)) { #ifdef IM_DEBUG wprintf(L" 2b (%2d)\n", cm.match_count); #endif wcsncat(im->s, im->buf, 1); wcs_lshift(im->buf, 1); cm.match_is_final = 0; } /* Otherwise it's just a part of a future input */ else { #ifdef IM_DEBUG wprintf(L" 2c (%2d)\n", cm.match_count); #endif wcscat(im->s, im->buf); cm.match_is_final = 0; im->redraw += wcslen(im->buf); break; } } /* No match and no more data in the buffer */ else { #ifdef IM_DEBUG wprintf(L" 3\n"); #endif break; } /* Is this the end? */ if(cm.match_is_final) break; } } } return im->redraw; }
/** * Korean IM. * * @see im_read */ static int im_event_ko(IM_DATA* im, SDL_keysym ks) { static const char* lang_file = IMDIR "ko.im"; enum { SEC_ENGLISH, SEC_HANGUL, SEC_TOTAL }; static CHARMAP cm; /* Handle event requests */ switch(im->request) { case 0: break; case IM_REQ_FREE: /* Free allocated resources */ charmap_free(&cm); /* go onto full reset */ case IM_REQ_RESET_FULL: /* Full reset */ cm.section = SEC_ENGLISH; im->tip_text = im_tip_text[IM_TIP_ENGLISH]; /* go onto soft reset */ case IM_REQ_RESET_SOFT: /* Soft reset */ im->s[0] = L'\0'; im->buf[0] = L'\0'; im->redraw = 0; cm.match_count = 0; cm.match_is_final = 0; cm.match_state = &cm.sections[cm.section]; cm.match_state_prev = &cm.sections[cm.section]; break; case IM_REQ_INIT: /* Initialization */ charmap_init(&cm); if(charmap_load(&cm, lang_file)) { fprintf(stderr, "Unable to load %s, defaulting to im_event_c\n", lang_file); im->lang = LANG_DEFAULT; return im_event_c(im, ks); } im_fullreset(im); #ifdef DEBUG printf("IM: Loaded '%s'\n", lang_file); #endif break; } if(im->request != IM_REQ_TRANSLATE) return 0; /* Discard redraw characters, so they can be redrawn */ if((int)wcslen(im->s) < im->redraw) im->redraw = wcslen(im->s); wcs_lshift(im->s, (wcslen(im->s) - im->redraw) ); /* Handle keys */ switch(ks.sym) { /* Keys to ignore */ case SDLK_NUMLOCK: case SDLK_CAPSLOCK: case SDLK_SCROLLOCK: case SDLK_LSHIFT: case SDLK_RSHIFT: case SDLK_LCTRL: case SDLK_RCTRL: case SDLK_LMETA: case SDLK_RMETA: case SDLK_LSUPER: case SDLK_RSUPER: case SDLK_MODE: case SDLK_COMPOSE: break; /* Right-Alt mapped to mode-switch */ case SDLK_LALT: case SDLK_RALT: cm.section = (++cm.section % SEC_TOTAL); /* Change section */ im_softreset(im); /* Soft reset */ /* Set tip text */ switch(cm.section) { case SEC_ENGLISH: im->tip_text = im_tip_text[IM_TIP_ENGLISH]; break; case SEC_HANGUL: im->tip_text = im_tip_text[IM_TIP_HANGUL]; break; } break; /* Backspace removes only a single buffered character */ case SDLK_BACKSPACE: /* Delete one buffered character */ if(wcslen(im->buf) > 0) { wcs_pull(im->buf, 1); if(im->redraw > 0) im->redraw--; ks.unicode = L'\0'; } /* continue processing: */ /* Actual character processing */ default: /* English mode */ if(cm.section == SEC_ENGLISH) { im->s[0] = ks.unicode; im->s[1] = L'\0'; im->buf[0] = L'\0'; } /* Hangul mode */ else { wchar_t u = ks.unicode; wchar_t* bp = im->buf; im->s[0] = L'\0'; /* Zero-out output string */ wcsncat(bp, &u, 1); /* Copy new character */ /* Translate the characters */ im->redraw = 0; while(1) { const wchar_t* us = charmap_search(&cm, bp); #ifdef IM_DEBUG wprintf(L" [%8ls] [%8ls] %2d %2d\n", im->s, im->buf, wcslen(im->s), wcslen(im->buf)); #endif /* Match was found? */ if(us && wcslen(us)) { /* Final match */ if(cm.match_is_final) { /* Batchim may carry over to the next character */ if(cm.match_state->flag == 'b') { wchar_t next_char = bp[cm.match_count]; /* If there is no more buffer, output it */ if(cm.match_stats & MATCH_STAT_NOMOBUF) { #ifdef IM_DEBUG wprintf(L" 1a\n"); #endif wcscat(im->s, us); /* Output */ im->redraw += wcslen(us); /* May need to re-eval next time */ bp += cm.match_count; /* Keep buffer data for re-eval*/ cm.match_count = 0; cm.match_is_final = 0; } /* If there is buffer data but it's not vowel, finalize it */ else if(!im_event_ko_isvowel(&cm, next_char)) { #ifdef IM_DEBUG wprintf(L" 1b\n"); #endif wcscat(im->s, us); /* Output */ wcs_lshift(bp, cm.match_count); cm.match_count = 0; cm.match_is_final = 0; } /* If there is buffer and it's vowel, re-eval */ else { #ifdef IM_DEBUG wprintf(L" 1c\n"); #endif us = cm.match_state_prev->output; wcscat(im->s, us); /* Output */ cm.match_count--; /* Matched all but one */ cm.match_is_final = 0; wcs_lshift(bp, cm.match_count); } } /* No batchim - this is final */ else { #ifdef IM_DEBUG wprintf(L" 1d\n"); #endif wcscat(im->s, us); wcs_lshift(bp, cm.match_count); cm.match_count = 0; cm.match_is_final = 0; } } /* May need to be overwritten next time */ else { #ifdef IM_DEBUG wprintf(L" 1e\n"); #endif wcscat(im->s, us); im->redraw += wcslen(us); break; } } /* No match, but more data is in the buffer */ else if(wcslen(bp) > 0) { /* If the input character has no state, it's its own state */ if(cm.match_count == 0) { #ifdef IM_DEBUG wprintf(L" 2a\n"); #endif wcsncat(im->s, bp, 1); wcs_lshift(bp, 1); cm.match_is_final = 0; } /* If the matched characters didn't consume all, it's own state */ else if((size_t)cm.match_count != wcslen(bp)) { #ifdef IM_DEBUG wprintf(L" 2b (%2d)\n", cm.match_count); #endif wcsncat(im->s, bp, 1); wcs_lshift(bp, 1); cm.match_is_final = 0; } /* Otherwise it's just a part of a future input */ else { #ifdef IM_DEBUG wprintf(L" 2c (%2d)\n", cm.match_count); #endif wcscat(im->s, bp); cm.match_is_final = 0; im->redraw += wcslen(bp); break; } } /* No match and no more data in the buffer */ else { #ifdef IM_DEBUG wprintf(L" 3\n"); #endif break; } /* Is this the end? */ if(cm.match_is_final) break; } } } return im->redraw; }