[Tux4kids-commits] r1691 - in tuxtype/trunk: . src
David Bruce
dbruce-guest at alioth.debian.org
Wed Dec 23 03:30:30 UTC 2009
Author: dbruce-guest
Date: 2009-12-23 03:30:29 +0000 (Wed, 23 Dec 2009)
New Revision: 1691
Added:
tuxtype/trunk/src/input_methods.c
tuxtype/trunk/src/input_methods.h
Modified:
tuxtype/trunk/configure.ac
tuxtype/trunk/src/Makefile.am
Log:
Added Tux Paint's im.c and im.h (renamed to input_methods.c/h)
Modified: tuxtype/trunk/configure.ac
===================================================================
--- tuxtype/trunk/configure.ac 2009-12-20 03:29:49 UTC (rev 1690)
+++ tuxtype/trunk/configure.ac 2009-12-23 03:30:29 UTC (rev 1691)
@@ -202,18 +202,22 @@
MINGW32_PACKAGE_DATA_DIR="data"
AC_SUBST(MINGW32_PACKAGE_DATA_DIR)
+dnl FIXME this isn't a good place on a well-administered Windows system
# Where to keep modifiable shared data files:
MINGW32_PACKAGE_VAR_DIR="data"
AC_SUBST(MINGW32_PACKAGE_VAR_DIR)
+dnl FIXME this isn't a good place on a well-administered Windows system
# Where to keep machine-wide (i.e for all users) config files:
MINGW32_PACKAGE_CONF_DIR="data"
AC_SUBST(MINGW32_PACKAGE_CONF_DIR)
+# Location of input method files:
+MINGW32_PACKAGE_IM_DIR="data/input_methods"
+AC_SUBST(MINGW32_PACKAGE_IM_DIR)
-
#AM_CONDITIONAL(BUILD_MINGW32, test "$native_win32" = yes)
if test $native_win32 = yes; then
Modified: tuxtype/trunk/src/Makefile.am
===================================================================
--- tuxtype/trunk/src/Makefile.am 2009-12-20 03:29:49 UTC (rev 1690)
+++ tuxtype/trunk/src/Makefile.am 2009-12-23 03:30:29 UTC (rev 1691)
@@ -19,10 +19,12 @@
DATA_PREFIX=@MINGW32_PACKAGE_DATA_DIR@
VAR_PREFIX=@MINGW32_PACKAGE_VAR_DIR@
CONF_PREFIX=@MINGW32_PACKAGE_CONF_DIR@
+ IM_PREFIX=@MINGW32_PACKAGE_IM_DIR@
else
DATA_PREFIX=${pkgdatadir}
VAR_PREFIX=${pkglocalstatedir}
CONF_PREFIX=${pkgsysconfdir}
+ IM_PREFIX=${pkgdatadir}/input_methods
endif
@@ -34,12 +36,14 @@
-DDATA_PREFIX=\"$(DATA_PREFIX)\" \
-DVAR_PREFIX=\"$(VAR_PREFIX)\" \
-DCONF_PREFIX=\"$(CONF_PREFIX)\" \
+ -DIM_PREFIX=\"$(IM_PREFIX)\" \
-DDEBUG \
-DVERSION=\"@NAME_VERSION@\" -D$(SOUND)SOUND
AM_CPPFLAGS = -DLOCALEDIR=\"$(localedir)\" \
-DLOCALSTATEDIR=\"$(pkglocalstatedir)\" \
-DSYSCONFDIRDIR=\"$(pkgsysconfdir)\" \
+ -DIMDIR=\"$(pkgdatadir)/input_methods\" \
-I../intl -I$(top_srcdir)/intl
if BUILD_MINGW32
@@ -61,6 +65,7 @@
audio.c \
convert_utf.c \
editor.c \
+ input_methods.c \
laser.c \
loaders.c \
main.c \
@@ -89,11 +94,12 @@
funcs.h \
gettext.h \
globals.h \
+ input_methods.h \
laser.h \
mysetenv.h \
pixels.h \
playgame.h \
- scandir.h
+ scandir.h \
scripting.h \
SDL_extras.h \
snow.h \
Added: tuxtype/trunk/src/input_methods.c
===================================================================
--- tuxtype/trunk/src/input_methods.c (rev 0)
+++ tuxtype/trunk/src/input_methods.c 2009-12-23 03:30:29 UTC (rev 1691)
@@ -0,0 +1,1780 @@
+/*
+ input_methods.c - renamed from Tux Paint's im.c and very lightly modified
+ by David Bruce <davidstuartbruce at gmail.com> for use in Tux Typing and other
+ Tux4Kids programs - 2009-2010. Adaptation for Tux Typing assisted by Mark
+ K. Kim. Revised version licensed under GPLv2 or later as described below.
+
+ The upstream "pristine" version of this file can be obtained from
+ http://www.tux4kids.org
+*/
+
+/*
+ im.c
+
+ Input method handling
+ Copyright (c)2007 by Mark K. Kim and others
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ (See COPYING.txt)
+
+ $Id: im.c,v 1.12 2008/06/27 02:35:26 dolphin6k Exp $
+*/
+
+/*
+* See the LANGUAGE-SPECIFIC IM FUNCTIONS section for instructions on adding
+* support for new languages.
+*
+* This file is called IM (Input Method), but it's actually an Input Translator.
+* This implementation was sort of necessary in order to work without having to
+* modify SDL.
+*
+* Basically, to read in text in foreign language, read Keysym off of SDL and
+* pass to im_read. im_read will translate the text and pass the unicode string
+* back to you. But before all this is done, be sure to create the IM_DATA
+* structure and initialize it with the proper language translator you want to use.
+*/
+
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+//#include "im.h"
+#include "input_methods.h"
+
+
+
+/* ***************************************************************************
+* I18N GETTEXT
+*/
+
+#ifndef gettext_noop
+#define gettext_noop(s) (s)
+#endif
+
+
+enum {
+ IM_TIP_NONE,
+ IM_TIP_ENGLISH,
+ IM_TIP_HIRAGANA,
+ IM_TIP_KATAKANA,
+ IM_TIP_HANGUL,
+ IM_TIP_THAI,
+ IM_TIP_ZH_TW,
+ NUM_IM_TIPS
+};
+
+
+static const char* const im_tip_text[NUM_IM_TIPS] =
+{
+ NULL,
+
+ // Input Method: English mode
+ gettext_noop("English"),
+
+ // Input Method: Japanese Romanized Hiragana mode
+ gettext_noop("Hiragana"),
+
+ // Input Method: Japanese Romanized Katakana mode
+ gettext_noop("Katakana"),
+
+ // Input Method: Korean Hangul 2-Bul mode
+ gettext_noop("Hangul"),
+
+ // Input Method: Thai mode
+ gettext_noop("Thai"),
+
+ // Input Method: Traditional Chinese mode
+ gettext_noop("ZH_TW")
+};
+
+
+/* ***************************************************************************
+* CONSTANTS
+*/
+
+/* #define IM_DEBUG 1 */
+
+#define MAX_SECTIONS 8 /* Maximum numbers of sections in *.im file */
+#define MAX_UNICODE_SEQ 16 /* Output of state machine, including NUL */
+#define INITIAL_SMSIZE 8 /* Initial num of transitions in STATE_MACHINE */
+
+#ifndef LANG_DEFAULT
+#define LANG_DEFAULT (LANG_EN)
+#endif
+
+
+/**
+* Event types that im_event_*() functions need to handle.
+*/
+enum {
+ IM_REQ_TRANSLATE, /* The ever-more important IM translation request */
+ IM_REQ_INIT, /* Initialization request */
+ IM_REQ_RESET_SOFT, /* Soft reset request */
+ IM_REQ_RESET_FULL, /* Full reset request */
+ IM_REQ_FREE, /* Free resources */
+ NUM_IM_REQUESTS
+};
+
+
+/**
+* Match statuses.
+*/
+enum {
+ MATCH_STAT_NONE = 0x00,
+ MATCH_STAT_NOMOSTATES = 0x01,
+ MATCH_STAT_NOMOBUF = 0x02,
+};
+
+
+/* ***************************************************************************
+* TYPES
+*/
+
+/**
+* All im_event_*() functions have this type.
+*/
+typedef int (*IM_EVENT_FN)(IM_DATA*, SDL_keysym); /* IM_EVENT_FN type */
+
+
+/**
+* State Machine key-value pair for transition control. When the "key"
+* is pressed, transition is made to "state".
+*
+* @see STATE_MACHINE
+*/
+typedef struct SM_WITH_KEY {
+ char key;
+ struct STATE_MACHINE* state;
+} SM_WITH_KEY;
+
+
+/**
+* A State Machine is used to map key strokes to the unicode output.
+* A single State Machine has a possible output (the unicode) and pointers
+* to next states. The "next state" is determined by the key stroke
+* pressed by the user - this key is looked up in SM_WITH_KEY and
+* its next state determined by the STATE_MACHINE pointer in SM_WITH_KEY.
+*
+* The number of possible transitions to the next state is dynamically
+* adjustable using the parameter next_maxsize. The actual storage in
+* use can be determined via next_size.
+*
+* @see SM_WITH_KEY
+*/
+typedef struct STATE_MACHINE {
+ wchar_t output[MAX_UNICODE_SEQ];
+ char flag;
+
+ SM_WITH_KEY* next; /* Possible transitions */
+ size_t next_maxsize; /* Potential size of the next pointer */
+ size_t next_size; /* Used size of the next pointer */
+} STATE_MACHINE;
+
+
+/**
+* A Character Map loads the *.im file, which may have several "sections".
+* Each section has its own state machine, and the C code determines which
+* section is used in determining which STATE_MACHINE to use for the
+* key mapping.
+*/
+typedef struct {
+ STATE_MACHINE sections[MAX_SECTIONS];
+ int section;
+
+ /* These variables get populated when a search is performed */
+ int match_count; /* How many char seq was used for output */
+ int match_is_final; /* T/F - tells if match is final */
+ int match_stats; /* Statistics gathering */
+ STATE_MACHINE* match_state;
+ STATE_MACHINE* match_state_prev;
+} CHARMAP;
+
+
+/* ***************************************************************************
+* STATIC GLOBALS
+*/
+
+/**
+* Global initialization flag.
+*/
+static int im_initialized = 0;
+
+/** This was moved out of im_read() because it is also used in im_init()
+* for debugging
+*/
+static IM_EVENT_FN im_event_fp = NULL;
+
+
+/** Moved from Tux Paint's i18n.c: */
+const char *lang_prefixes[NUM_LANGS] = {
+ "af",
+ "ar",
+ "ast",
+ "az",
+ "be",
+ "bg",
+ "bo",
+ "br",
+ "ca",
+ "cs",
+ "cy",
+ "da",
+ "de",
+ "el",
+ "en",
+ "en_AU",
+ "en_CA",
+ "en_GB",
+ "en_ZA",
+ "eo",
+ "es",
+ "es_MX",
+ "et",
+ "eu",
+ "fi",
+ "fo",
+ "fr",
+ "ga",
+ "gd",
+ "gl",
+ "gos",
+ "gu",
+ "he",
+ "hi",
+ "hr",
+ "hu",
+ "tlh",
+ "id",
+ "is",
+ "it",
+ "ja",
+ "ka",
+ "km",
+ "ko",
+ "ku",
+ "lt",
+ "lv",
+ "mk",
+ "ms",
+ "nb",
+ "nl",
+ "nn",
+ "nr",
+ "oc",
+ "oj",
+ "pl",
+ "pt_BR",
+ "pt_PT",
+ "ro",
+ "ru",
+ "rw",
+ "shs",
+ "sk",
+ "sl",
+ "son",
+ "sq",
+ "sr",
+ "sv",
+ "sw",
+ "ta",
+ "te",
+ "th",
+ "tl",
+ "tr",
+ "twi",
+ "uk",
+ "ve",
+ "vi",
+ "wa",
+ "wo",
+ "xh",
+ "zh_CN",
+ "zh_TW",
+ "zw"
+};
+
+
+/**
+* Language-specific IM event-handler function pointers. This lookup table
+* is initialized in im_init(). Every support language should have a pointer
+* mapped here.
+*
+* @see im_init()
+* @see im_read()
+*/
+static IM_EVENT_FN im_event_fns[NUM_LANGS];
+
+
+/* ***************************************************************************
+* UTILITY FUNCTIONS
+*/
+
+#define MIN(a,b) ((a)<=(b) ? (a) : (b))
+#define IN_RANGE(a,v,b) ( (a)<=(v) && (v)<(b) )
+#define ARRAYLEN(a) ( sizeof(a)/sizeof(*(a)) )
+
+
+static void wcs_lshift(wchar_t* s, size_t count)
+{
+ wchar_t* dest = s;
+ wchar_t* src = s+count;
+ size_t len = wcslen(src)+1; /* Copy over all src string + NUL */
+
+ memmove(dest, src, len * sizeof(wchar_t));
+}
+
+
+/**
+* Pull out "count" characters from the back.
+*/
+static void wcs_pull(wchar_t* s, size_t count)
+{
+ int peg = (int)wcslen(s) - (int)count;
+ if(peg < 0) peg = 0;
+
+ s[peg] = L'\0';
+}
+
+
+/* ***************************************************************************
+* STATE_MACHINE FUNCTIONS
+*/
+
+/**
+* Compare two SM_WITH_KEY, return appropriate result.
+*/
+static int swk_compare(const void* swk1, const void* swk2)
+{
+ SM_WITH_KEY* sk1 = (SM_WITH_KEY*)swk1;
+ SM_WITH_KEY* sk2 = (SM_WITH_KEY*)swk2;
+
+ return (sk1->key) - (sk2->key);
+}
+
+
+/**
+* Initialize the State Machine.
+*/
+static int sm_init(STATE_MACHINE* sm)
+{
+ memset(sm, 0, sizeof(STATE_MACHINE));
+
+ sm->next = calloc(INITIAL_SMSIZE, sizeof(SM_WITH_KEY));
+ if(!sm->next) {
+ perror("sm_init");
+ return 1;
+ }
+
+ sm->next_maxsize = INITIAL_SMSIZE;
+ return 0;
+}
+
+
+/**
+* Free the State Machine resources.
+*/
+static void sm_free(STATE_MACHINE* sm)
+{
+ if(sm->next) {
+ int i = 0;
+
+ for(i = 0; i < (int)sm->next_maxsize; i++) {
+ STATE_MACHINE* next_state = sm->next[i].state;
+ if(next_state) sm_free(next_state);
+ sm->next[i].state = NULL;
+ }
+
+ free(sm->next);
+ sm->next = NULL;
+ }
+
+ memset(sm, 0, sizeof(STATE_MACHINE));
+}
+
+
+/**
+* Double the storage space of the possible transition states.
+*/
+static int sm_dblspace(STATE_MACHINE* sm)
+{
+ size_t newsize = sm->next_maxsize * 2;
+ SM_WITH_KEY* next = realloc(sm->next, sizeof(SM_WITH_KEY) * newsize);
+
+ if(next == NULL) {
+ perror("sm_dblspace");
+ return 1;
+ }
+
+ sm->next = next;
+ sm->next_maxsize = newsize;
+ return 0;
+}
+
+
+/**
+* Search the state machine's transition keys, return pointer to the next state.
+* Return NULL if none is found. The search is done only at 1 level, and does
+* not recurse deep.
+*/
+static STATE_MACHINE* sm_search_shallow(STATE_MACHINE* sm, char key)
+{
+ SM_WITH_KEY smk = { key, NULL };
+ SM_WITH_KEY* smk_found;
+
+ smk_found = bsearch(
+ &smk, sm->next, sm->next_size, sizeof(SM_WITH_KEY), swk_compare);
+
+ if(!smk_found) return NULL;
+ return smk_found->state;
+}
+
+
+/**
+* Search the state machine's transition keys, return the unicode output of the
+* last state found. The search is done deep, recursing until no more match
+* can be found.
+*
+* @param start Starting point of the state transition. Constant.
+* @param key The key string to look for. Constant.
+* @param matched The number of character strings matched. Return on output.
+* @param end The last state found. Return on output.
+* @param penult The penultimate state found.
+*
+* @return Found unicode character sequence output of the last state.
+*/
+static const wchar_t* sm_search(STATE_MACHINE* start, wchar_t* key, int* matched, STATE_MACHINE** penult, STATE_MACHINE** end)
+{
+ STATE_MACHINE* sm = sm_search_shallow(start, (char)*key);
+ const wchar_t* unicode;
+
+ /* No match - stop recursion */
+ if(!sm) {
+ *matched = 0;
+ *end = start;
+
+ return start->output;
+ }
+
+ /* Match - recurse */
+ *penult = start;
+ unicode = sm_search(sm, key+1, matched, penult, end);
+ (*matched)++;
+
+ return unicode;
+}
+
+
+/**
+* Sort the state machine's transition keys so it can be binary-searched.
+* The sort is done only at 1 level, and does not recurse deep.
+*/
+static void sm_sort_shallow(STATE_MACHINE* sm)
+{
+ qsort(sm->next, sm->next_size, sizeof(SM_WITH_KEY), swk_compare);
+}
+
+
+/**
+* Add a single sequence-to-unicode path to the state machine.
+*/
+static int sm_add(STATE_MACHINE* sm, char* seq, const wchar_t* unicode, char flag)
+{
+ STATE_MACHINE* sm_found = sm_search_shallow(sm, seq[0]);
+
+ /* Empty sequence */
+ if(seq[0] == '\0') {
+ if(wcslen(sm->output)) {
+ size_t i;
+
+ fprintf(stderr, "Unicode sequence ");
+ for(i = 0; i < wcslen(sm->output); i++) fprintf(stderr, "%04X ", (int)sm->output[i]);
+ fprintf(stderr, " already defined, overriding with ");
+ for(i = 0; i < wcslen(unicode); i++) fprintf(stderr, "%04X ", (int)unicode[i]);
+ fprintf(stderr, "\n");
+ }
+ wcscpy(sm->output, unicode);
+ sm->flag = flag;
+ return 0;
+ }
+
+ /* The key doesn't exist yet */
+ if(!sm_found) {
+ int index = (int)sm->next_size;
+ SM_WITH_KEY* next = &sm->next[index];
+
+ /* Add the key */
+ next->key = seq[0];
+ next->state = malloc(sizeof(STATE_MACHINE));
+ if(!next->state) {
+ perror("sm_add");
+ return 1;
+ }
+ sm_init(next->state);
+
+ /* Increase store for next time, if necessary */
+ if(++(sm->next_size) >= sm->next_maxsize) {
+ if(sm_dblspace(sm)) {
+ fprintf(stderr, "Memory expansion failure\n");
+ return 1;
+ }
+ }
+
+ sm_found = next->state;
+ }
+
+ /* Recurse */
+ sm_add(sm_found, seq+1, unicode, flag);
+
+ /* Sort the states */
+ sm_sort_shallow(sm);
+
+ return 0;
+}
+
+
+
+/* ***************************************************************************
+* CHARMAP FUNCTIONS
+*/
+
+/**
+* Initialize the character map table.
+*/
+static int charmap_init(CHARMAP* cm)
+{
+ int error_code = 0;
+ int i = 0;
+
+ memset(cm, 0, sizeof(CHARMAP));
+
+ for(i = 0; i < MAX_SECTIONS; i++) {
+ error_code += sm_init(&cm->sections[i]);
+ }
+
+ return error_code;
+}
+
+
+/**
+* Add a character-sequence-to-unicode mapping to the character map.
+*
+* @param cm Character map to which to add the mapping.
+* @param section The section of the character map to add the mapping.
+* @param seq The character sequence to which to add the mapping.
+* @param unicode The unicode of the character sequence.
+* @param flag The flag associated with this state, if any.
+*
+* @return 0 if no error, 1 if error.
+*/
+static int charmap_add(CHARMAP* cm, int section, char* seq, const wchar_t* unicode, char* flag)
+{
+ if(section >= MAX_SECTIONS) {
+ fprintf(stderr, "Section count exceeded\n");
+ return 1;
+ }
+
+ /* For now, we only utilize one-character flags */
+ if(strlen(flag) > 1) {
+ fprintf(stderr, "%04X: Multi-character flag, truncated.\n", (int)unicode);
+ }
+
+ return sm_add(&cm->sections[section], seq, unicode, flag[0]);
+}
+
+
+/**
+* Load the character map table from a file.
+*
+* @param cm Character Map to load the table into.
+* @param path The path of the file to load.
+* @return Zero if the file is loaded fine, nonzero otherwise.
+*/
+static int charmap_load(CHARMAP* cm, const char* path)
+{
+ FILE* is = NULL;
+ int section = 0;
+ int error_code = 0;
+
+ /* Open */
+ is = fopen(path, "rt");
+ if(!is) {
+ perror("path");
+ return 1;
+ }
+
+ /* Load */
+ while(!feof(is)) {
+ wchar_t unicode[MAX_UNICODE_SEQ];
+ int ulen = 0;
+
+ char buf[256];
+ char flag[256];
+
+ int scanned = 0;
+
+ /* Scan a single token first */
+ scanned = fscanf(is, "%255s", buf);
+ if(scanned < 0) break;
+ if(scanned == 0) {
+ fprintf(stderr, "%s: Character map syntax error\n", path);
+ return 1;
+ }
+
+ /* Handle the first argument */
+ if(strcmp(buf, "section") == 0) { /* Section division */
+ section++;
+ continue;
+ }
+ else if(buf[0] == '#') { /* Comment */
+ fscanf(is, "%*[^\n]");
+ continue;
+ }
+ else {
+ char* bp = buf;
+ int u;
+
+ do {
+ if(sscanf(bp, "%x", &u) == 1) { /* Unicode */
+ unicode[ulen++] = u;
+ }
+ else {
+ fprintf(stderr, "%s: Syntax error at '%s'\n", path, buf);
+ return 1;
+ }
+
+ bp = strchr(bp, ':');
+ if(bp) bp++;
+ } while(bp && ulen < MAX_UNICODE_SEQ-1);
+ unicode[ulen] = L'\0';
+ }
+
+ /* Scan some more */
+ scanned = fscanf(is, "%255s\t%255s", buf, flag);
+ if(scanned < 0) break;
+
+ /* Input count checking */
+ switch(scanned) {
+ case 0: case 1:
+ fprintf(stderr, "%s: Character map syntax error\n", path);
+ return 1;
+
+ default:
+ if(charmap_add(cm, section, buf, unicode, flag)) {
+ size_t i = 0;
+
+#ifndef __BEOS__
+#if defined __GLIBC__ && __GLIBC__ == 2 && __GLIBC_MINOR__ >=2 || __GLIBC__ > 2 || __APPLE__
+ fwprintf(stderr, L"Unable to add sequence '%ls', unicode ", buf);
+ for(i = 0; i < wcslen(unicode); i++) fwprintf(stderr, L"%04X ", (int)unicode[i]);
+ fwprintf(stderr, L"in section %d\n", section);
+#endif
+#endif
+
+ error_code = 1;
+ }
+ }
+ }
+
+ /* Close */
+ fclose(is);
+
+ return error_code;
+}
+
+
+/**
+* Free the resources used by a character map.
+*/
+static void charmap_free(CHARMAP* cm)
+{
+ int i;
+
+ for(i = 0; i < MAX_SECTIONS; i++) {
+ sm_free(&cm->sections[i]);
+ }
+
+ memset(cm, 0, sizeof(CHARMAP));
+}
+
+
+/**
+* Search for a matching character string in the character map.
+*/
+static const wchar_t* charmap_search(CHARMAP* cm, wchar_t* s)
+{
+ STATE_MACHINE* start;
+ const wchar_t* unicode;
+ int section;
+
+ /* Determine the starting state based on the charmap's active section */
+ section = cm->section;
+ if(!IN_RANGE(0, section, (int)ARRAYLEN(cm->sections))) section = 0;
+ start = &cm->sections[section];
+
+ cm->match_state = NULL;
+ cm->match_state_prev = NULL;
+ unicode = sm_search(start, s, &cm->match_count, &cm->match_state_prev, &cm->match_state);
+
+ /**
+ * Determine whether the match is final. A match is considered to be final
+ * in two cases: (1)if the last state mached has no exit paths, or (2)if we
+ * did not consume all of the search string. (1) is obvious - if there are
+ * no more states to transition to, then the unicode we find is the final
+ * code. (2) means we reached the final state that can be the only
+ * interpretation of the input string, so it must be the final state.
+ * If neither of these is true, that means further input from the user
+ * may allow us to get to a different state, so we have not reached the
+ * final state we possibly can.
+ */
+ cm->match_is_final = 0;
+ if(cm->match_count < (int)wcslen(s)) {
+ cm->match_is_final = 1;
+ }
+
+ /* Statistics */
+ cm->match_stats = MATCH_STAT_NONE;
+ if(cm->match_state->next_size == 0) {
+ cm->match_is_final = 1;
+ cm->match_stats |= MATCH_STAT_NOMOSTATES;
+ }
+ if(cm->match_count == (int)wcslen(s)) {
+ cm->match_stats |= MATCH_STAT_NOMOBUF;
+ }
+
+ return unicode;
+}
+
+
+/* ***************************************************************************
+* LANGUAGE-SPECIFIC IM FUNCTIONS
+*
+* If you want to add a new language support, add the main code to this
+* section. More specifically, do the following:
+*
+* 1) Add im_event_<lang>() function to this section. Use the existing
+* im_event_* functions as models, and feel free to use the state-machine
+* character map engine (CHARMAP struct) but do not feel obligated to
+* do so. The CHARMAP engine exists for the programmer's benefit, to
+* make it easier to support complex languages.
+*
+* 2) Update the im_init() functions so that it initializes im_event_fns[]
+* with a pointer to your im_event_<lang>() function.
+*
+* 3) Create <lang>.im in the "im" directory, if you use the CHARMAP engine.
+* Your code is what loads this file so you should already know to do this
+* step if you have already written a working im_event_<lang>() function
+* that uses CHARMAP, but I explicitly write out this instruction for
+* those trying to figure out the relationship of <lang>.im to this IM
+* framework.
+*
+* 4) Increase MAX_SECTION if your language needs more sections in <lang>.im
+*
+* 5) Increase INITIAL_SMSIZE if your <lang>.im is huginormous and takes too
+* long to load. I can't think of any reason why this would happen unless
+* you're writing a Chinese IM with a significant characters of the
+* language represented, but the code as-is is somewhat lacking when it
+* comes to writing a Chinese IM (need some way to show a dropdown box
+* from the main app - same problem with Korean Hanja and Japanese Kanji
+* inputs, but this isn't meant to be a complex IM framework so I think
+* we're safe for Hanja and Kanji.) Do this with caution because
+* changing INITIAL_SMSIZE will affect the memory consumption of all IM
+* functions.
+*/
+
+/**
+* Default C IM event handler.
+*
+* @see im_read
+*/
+static int im_event_c(IM_DATA* im, SDL_keysym ks)
+{
+ /* Handle event requests */
+ im->s[0] = L'\0';
+ if(im->request != IM_REQ_TRANSLATE) return 0;
+
+ /* Handle key stroke */
+ switch(ks.sym) {
+ case SDLK_BACKSPACE: im->s[0] = L'\b'; break;
+ case SDLK_TAB: im->s[0] = L'\t'; break;
+ case SDLK_RETURN: im->s[0] = L'\r'; break;
+ default: im->s[0] = ks.unicode;
+ }
+ im->s[1] = L'\0';
+ im->buf[0] = L'\0';
+
+ return 0;
+}
+
+/**
+* Chinese Traditional IM.
+*
+* @see im_read
+*/
+static int im_event_zh_tw(IM_DATA* im, SDL_keysym ks)
+{
+ static const char* lang_file = IMDIR "zh_tw.im";
+ enum { SEC_ENGLISH, SEC_ZH_TW, 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;
+
+ /* Left-Alt & Right-Alt mapped to mode-switch */
+ case SDLK_RALT: case SDLK_LALT:
+ 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_ZH_TW: im->tip_text = im_tip_text[IM_TIP_ZH_TW]; 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;
+}
+
+
+/**
+* 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;
+}
+
+
+/**
+* Japanese IM.
+*
+* @see im_read
+*/
+static int im_event_ja(IM_DATA* im, SDL_keysym ks)
+{
+ static const char* lang_file = IMDIR "ja.im";
+ enum { SEC_ENGLISH, SEC_HIRAGANA, SEC_KATAKANA, 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_HIRAGANA: im->tip_text = im_tip_text[IM_TIP_HIRAGANA]; break;
+ case SEC_KATAKANA: im->tip_text = im_tip_text[IM_TIP_KATAKANA]; 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';
+ }
+ /* Hiragana and Katakana modes */
+ 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 helper function to tell whether a character typed will produce
+* a vowel.
+*
+* @see im_event_ko
+*/
+static int im_event_ko_isvowel(CHARMAP* cm, wchar_t c)
+{
+ STATE_MACHINE *start, *next;
+ const wchar_t* unicode;
+ int section;
+
+ /* Determine the starting state based on the charmap's active section */
+ section = cm->section;
+ if(!IN_RANGE(0, section, (int)ARRAYLEN(cm->sections))) section = 0;
+ start = &cm->sections[section];
+
+ next = sm_search_shallow(start, (char)c);
+ unicode = next ? next->output : NULL;
+
+ return (unicode && wcslen(unicode) == 1 && 0x314F <= unicode[0] && unicode[0] <= 0x3163);
+}
+
+
+/**
+* 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;
+}
+
+
+/* ***************************************************************************
+* OTHER STATIC IM FUNCTIONS
+*/
+
+/**
+* Generic event handler that calls the appropriate language handler.
+* im->request should have the event ID.
+*/
+static void im_event(IM_DATA* im)
+{
+ SDL_keysym ks;
+
+ ks.sym = 0;
+ ks.unicode = 0;
+
+ im_read(im, ks);
+}
+
+
+/**
+* Make an event request and call the event handler.
+*/
+static void im_request(IM_DATA* im, int request)
+{
+ im->request = request;
+ im_event(im);
+ im->request = IM_REQ_TRANSLATE;
+}
+
+
+/* ***************************************************************************
+* PUBLIC IM FUNCTIONS
+*/
+
+/**
+* Initialize the IM_DATA structure.
+*
+* @param im IM_DATA structure to initialize.
+* @param lang LANG_* defined constant to initialize the structure with.
+*/
+void im_init(IM_DATA* im, int lang)
+{
+ /* Free already allocated resources if initialized before */
+ if(im_initialized) {
+ im_free(im);
+ }
+
+ /* Initialize */
+ memset(im, 0, sizeof(IM_DATA));
+ im->lang = lang;
+
+ /* Setup static globals */
+ if(!im_initialized) {
+ /* ADD NEW LANGUAGE SUPPORT HERE */
+ im_event_fns[LANG_JA] = &im_event_ja;
+ im_event_fns[LANG_KO] = &im_event_ko;
+ im_event_fns[LANG_TH] = &im_event_th;
+ im_event_fns[LANG_ZH_TW] = &im_event_zh_tw;
+
+ im_initialized = 1;
+ }
+
+ #ifdef DEBUG
+ assert(0 <= im->lang && im->lang < NUM_LANGS);
+ if(im_event_fp) printf("Initializing IM for %s...\n", lang_prefixes[im->lang]);
+ #endif
+
+ /* Initialize the individual IM */
+ im_request(im, IM_REQ_INIT);
+}
+
+
+void im_softreset(IM_DATA* im)
+{
+ im->s[0] = L'\0';
+ im->buf[0] = L'\0';
+
+ im_request(im, IM_REQ_RESET_SOFT);
+}
+
+
+void im_fullreset(IM_DATA* im)
+{
+ im->s[0] = L'\0';
+ im->buf[0] = L'\0';
+
+ im_request(im, IM_REQ_RESET_FULL);
+}
+
+
+/**
+* Free any allocated resources.
+*/
+void im_free(IM_DATA* im)
+{
+ im_request(im, IM_REQ_FREE);
+}
+
+
+/**
+* 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;
+}
+
+
+/* vim:ts=2:et
+*/
Added: tuxtype/trunk/src/input_methods.h
===================================================================
--- tuxtype/trunk/src/input_methods.h (rev 0)
+++ tuxtype/trunk/src/input_methods.h 2009-12-23 03:30:29 UTC (rev 1691)
@@ -0,0 +1,167 @@
+/*
+ input_methods.h - renamed from Tux Paint's im.h and very lightly modified
+ by David Bruce <davidstuartbruce at gmail.com> for use in Tux Typing and other
+ Tux4Kids programs - 2009-2010. Adaptation for Tux Typing assisted by Mark
+ K. Kim. Revised version licensed under GPLv2 or later as described below.
+
+ The upstream "pristine" version of this file can be obtained from
+ http://www.tux4kids.org
+*/
+
+/*
+ im.h
+
+ Input method handling
+ Copyright (c) 2007 by Mark K. Kim and others
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ (See COPYING.txt)
+
+ $Id: im.h,v 1.3 2007/05/06 04:00:20 vindaci Exp $
+*/
+
+//#ifndef TUXPAINT_IM_H
+//#define TUXPAINT_IM_H
+#ifndef INPUT_METHODS_H
+#define INPUT_METHODS_H
+
+#include "SDL.h"
+//#include "i18n.h"
+
+
+/* ***************************************************************************
+* TYPES
+*/
+
+typedef struct IM_DATA {
+ int lang; /* Language used in sequence translation */
+ wchar_t s[16]; /* Characters that should be displayed */
+ const char* tip_text; /* Tip text, read-only please */
+
+ /* For use by language-specific im_event_<lang> calls. PRIVATE! */
+ wchar_t buf[8]; /* Buffered characters */
+ int redraw; /* Redraw this many characters next time */
+ int request; /* Event request */
+} IM_DATA;
+
+
+/* ***************************************************************************
+* FUNCTIONS
+*/
+
+void im_init(IM_DATA* im, int lang); /* Initialize IM */
+void im_fullreset(IM_DATA* im); /* Full Reset IM */
+void im_softreset(IM_DATA* im); /* Soft Reset IM */
+void im_free(IM_DATA* im); /* Free IM resources */
+
+int im_read(IM_DATA* im, SDL_keysym ks);
+
+
+/* Language enum copied from Tux Paint's i18n.h (same authors) to */
+/* avoid need to separately include that file: */
+/* Possible languages: */
+
+enum
+{
+ LANG_AF, /* Afrikaans */
+ LANG_AR, /* Arabic */
+ LANG_AST, /* Asturian */
+ LANG_AZ, /* Azerbaijani */
+ LANG_BE, /* Belarusian */
+ LANG_BG, /* Bulgarian */
+ LANG_BO, /* Tibetan */
+ LANG_BR, /* Breton */
+ LANG_CA, /* Catalan */
+ LANG_CS, /* Czech */
+ LANG_CY, /* Welsh */
+ LANG_DA, /* Danish */
+ LANG_DE, /* German */
+ LANG_EL, /* Greek */
+ LANG_EN, /* English (American) (DEFAULT) */
+ LANG_EN_AU, /* English (Australian) */
+ LANG_EN_CA, /* English (Canadian) */
+ LANG_EN_GB, /* English (British) */
+ LANG_EN_ZA, /* English (South African) */
+ LANG_EO, /* Esperanto */
+ LANG_ES, /* Spanish */
+ LANG_ES_MX, /* Spanish (Mexican) */
+ LANG_ET, /* Estonian */
+ LANG_EU, /* Basque */
+ LANG_FI, /* Finnish */
+ LANG_FO, /* Faroese */
+ LANG_FR, /* French */
+ LANG_GA, /* Irish Gaelic */
+ LANG_GD, /* Scottish Gaelic */
+ LANG_GL, /* Galician */
+ LANG_GR, /* Gronings */
+ LANG_GU, /* Gujarati */
+ LANG_HE, /* Hebrew */
+ LANG_HI, /* Hindi */
+ LANG_HR, /* Croatian */
+ LANG_HU, /* Hungarian */
+ LANG_I_KLINGON_ROMANIZED, /* Klingon (Romanized) */
+ LANG_ID, /* Indonesian */
+ LANG_IS, /* Icelandic */
+ LANG_IT, /* Italian */
+ LANG_JA, /* Japanese */
+ LANG_KA, /* Georgian */
+ LANG_KM, /* Khmer */
+ LANG_KO, /* Korean */
+ LANG_KU, /* Kurdish */
+ LANG_LT, /* Lithuanian */
+ LANG_LV, /* Latvian */
+ LANG_MK, /* Macedonian */
+ LANG_MS, /* Malay */
+ LANG_NB, /* Norwegian Bokmal */
+ LANG_NL, /* Dutch */
+ LANG_NN, /* Norwegian Nynorsk */
+ LANG_NR, /* Ndebele */
+ LANG_OC, /* Occitan */
+ LANG_OJ, /* Ojibway */
+ LANG_PL, /* Polish */
+ LANG_PT_BR, /* Portuguese (Brazilian) */
+ LANG_PT_PT, /* Portuguese (Portugal) */
+ LANG_RO, /* Romanian */
+ LANG_RU, /* Russian */
+ LANG_RW, /* Kinyarwanda */
+ LANG_SHS, /* Shuswap */
+ LANG_SK, /* Slovak */
+ LANG_SL, /* Slovenian */
+ LANG_SON, /* Songhay */
+ LANG_SQ, /* Albanian */
+ LANG_SR, /* Serbian */
+ LANG_SV, /* Swedish */
+ LANG_SW, /* Swahili */
+ LANG_TA, /* Tamil */
+ LANG_TE, /* Telugu */
+ LANG_TH, /* Thai */
+ LANG_TL, /* Tagalog */
+ LANG_TR, /* Turkish */
+ LANG_TWI, /* Twi */
+ LANG_UK, /* Ukrainian */
+ LANG_VE, /* Venda */
+ LANG_VI, /* Vietnamese */
+ LANG_WA, /* Walloon */
+ LANF_WO, /* Wolof */
+ LANG_XH, /* Xhosa */
+ LANG_ZH_CN, /* Chinese (Simplified) */
+ LANG_ZH_TW, /* Chinese (Traditional) */
+ LANG_ZAM, /* Zapotec (Miahuatlan) */
+ NUM_LANGS
+};
+#endif /* TUXPAINT_IM_H */
+
+/* vim:ts=8
+*/
More information about the Tux4kids-commits
mailing list