Merged a cleaned up version of Jiang's code changes from Google Summer of Code 2009
authorSam Lantinga <slouken@libsdl.org>
Sat, 19 Sep 2009 13:29:40 +0000
changeset 3280 00cace2d9080
parent 3279 fd207dce9f94
child 3281 2d3a9f229ba1
Merged a cleaned up version of Jiang's code changes from Google Summer of Code 2009
build-scripts/showrev.sh
include/SDL_events.h
include/SDL_keyboard.h
src/SDL_compat.c
src/events/SDL_keyboard.c
src/events/SDL_keyboard_c.h
src/video/SDL_sysvideo.h
src/video/SDL_video.c
src/video/cocoa/SDL_cocoaevents.m
src/video/cocoa/SDL_cocoakeyboard.h
src/video/cocoa/SDL_cocoakeyboard.m
src/video/cocoa/SDL_cocoamodes.m
src/video/cocoa/SDL_cocoavideo.h
src/video/cocoa/SDL_cocoavideo.m
test/Makefile.in
test/configure.in
test/testime.c
--- a/build-scripts/showrev.sh	Sat Sep 19 12:48:52 2009 +0000
+++ b/build-scripts/showrev.sh	Sat Sep 19 13:29:40 2009 +0000
@@ -9,4 +9,7 @@
     (svnversion -c 2>/dev/null || svnversion .) | \
         sed -e 's,\([0-9]*\)[A-Z]*,\1,' \
             -e 's,[0-9]*:\([0-9]*\)[A-Z]*,\1,'
+else
+     cd $srcdir
+     git svn info | grep Revision | awk '{ print $2 }'
 fi
--- a/include/SDL_events.h	Sat Sep 19 12:48:52 2009 +0000
+++ b/include/SDL_events.h	Sat Sep 19 13:29:40 2009 +0000
@@ -60,6 +60,7 @@
     SDL_WINDOWEVENT,            /**< Window state change */
     SDL_KEYDOWN,                /**< Keys pressed */
     SDL_KEYUP,                  /**< Keys released */
+    SDL_TEXTEDITING,            /**< Keyboard text editing (composition) */
     SDL_TEXTINPUT,              /**< Keyboard text input */
     SDL_MOUSEMOTION,            /**< Mouse moved */
     SDL_MOUSEBUTTONDOWN,        /**< Mouse button pressed */
@@ -97,6 +98,7 @@
     SDL_KEYDOWNMASK = SDL_EVENTMASK(SDL_KEYDOWN),
     SDL_KEYUPMASK = SDL_EVENTMASK(SDL_KEYUP),
     SDL_KEYEVENTMASK = SDL_EVENTMASK(SDL_KEYDOWN) | SDL_EVENTMASK(SDL_KEYUP),
+    SDL_TEXTEDITINGMASK = SDL_EVENTMASK(SDL_TEXTEDITING),
     SDL_TEXTINPUTMASK = SDL_EVENTMASK(SDL_TEXTINPUT),
     SDL_MOUSEMOTIONMASK = SDL_EVENTMASK(SDL_MOUSEMOTION),
     SDL_MOUSEBUTTONDOWNMASK = SDL_EVENTMASK(SDL_MOUSEBUTTONDOWN),
@@ -149,6 +151,20 @@
 } SDL_KeyboardEvent;
 
 /**
+ * \struct SDL_TextEditingEvent
+ *
+ * \brief Keyboard text editing event structure (event.edit.*)
+ */
+#define SDL_TEXTEDITINGEVENT_TEXT_SIZE (32)
+typedef struct SDL_TextEditingEvent
+{
+    Uint8 type;                                 /**< SDL_TEXTEDITING */
+    char text[SDL_TEXTEDITINGEVENT_TEXT_SIZE];  /**< The editing text */
+    int start;                                  /**< The start cursor of selected editing text */
+    int length;                                 /**< The length of selected editing text */
+} SDL_TextEditingEvent;
+
+/**
  * \struct SDL_TextInputEvent
  *
  * \brief Keyboard text input event structure (event.text.*)
@@ -350,6 +366,7 @@
     Uint8 type;                     /**< Event type, shared with all events */
     SDL_WindowEvent window;         /**< Window event data */
     SDL_KeyboardEvent key;          /**< Keyboard event data */
+    SDL_TextEditingEvent edit;      /**< Text editing event data */
     SDL_TextInputEvent text;        /**< Text input event data */
     SDL_MouseMotionEvent motion;    /**< Mouse motion event data */
     SDL_MouseButtonEvent button;    /**< Mouse button event data */
--- a/include/SDL_keyboard.h	Sat Sep 19 12:48:52 2009 +0000
+++ b/include/SDL_keyboard.h	Sat Sep 19 13:29:40 2009 +0000
@@ -154,6 +154,34 @@
  */
 extern DECLSPEC const char *SDLCALL SDL_GetKeyName(SDLKey key);
 
+/**
+ * \fn void SDL_StartTextInput(void)
+ *
+ * \brief Start accepting Unicode text input events.
+ *
+ * \sa SDL_StopTextInput()
+ * \sa SDL_SetTextInputRect()
+ */
+extern DECLSPEC void SDLCALL SDL_StartTextInput(void);
+
+/**
+ * \fn void SDL_StopTextInput(void)
+ *
+ * \brief Stop receiving any text input events.
+ *
+ * \sa SDL_StartTextInput()
+ */
+extern DECLSPEC void SDLCALL SDL_StopTextInput(void);
+
+/**
+ * \fn void SDL_SetTextInputRect(SDL_Rect *rect)
+ *
+ * \brief Set the rectangle used to type Unicode text inputs.
+ *
+ * \sa SDL_StartTextInput()
+ */
+extern DECLSPEC void SDLCALL SDL_SetTextInputRect(SDL_Rect *rect);
+
 
 /* Ends C function definitions when using C++ */
 #ifdef __cplusplus
--- a/src/SDL_compat.c	Sat Sep 19 12:48:52 2009 +0000
+++ b/src/SDL_compat.c	Sat Sep 19 13:29:40 2009 +0000
@@ -40,6 +40,7 @@
 static Uint32 SDL_VideoFlags = 0;
 static char *wm_title = NULL;
 static SDL_Surface *SDL_VideoIcon;
+static int SDL_enabled_UNICODE = 0;
 
 char *
 SDL_AudioDriverName(char *namebuf, int maxlen)
@@ -1720,7 +1721,19 @@
 int
 SDL_EnableUNICODE(int enable)
 {
-    return SDL_EventState(SDL_TEXTINPUT, enable);
+    int previous = SDL_enabled_UNICODE;
+
+    switch (enable) {
+    case 1:
+        SDL_enabled_UNICODE = 1;
+        SDL_StartTextInput();
+        break;
+    case 0:
+        SDL_enabled_UNICODE = 0;
+        SDL_StopTextInput();
+        break;
+    }
+    return previous;
 }
 
 /* vi: set ts=4 sw=4 expandtab: */
--- a/src/events/SDL_keyboard.c	Sat Sep 19 12:48:52 2009 +0000
+++ b/src/events/SDL_keyboard.c	Sat Sep 19 13:29:40 2009 +0000
@@ -852,6 +852,24 @@
     return (posted);
 }
 
+int
+SDL_SendEditingText(const char *text, int start, int length)
+{
+    int posted;
+
+    /* Post the event, if desired */
+    posted = 0;
+    if (SDL_ProcessEvents[SDL_TEXTEDITING] == SDL_ENABLE) {
+        SDL_Event event;
+        event.edit.type = SDL_TEXTEDITING;
+        event.edit.start = start;
+        event.edit.length = length;
+        SDL_strlcpy(event.edit.text, text, SDL_arraysize(event.text.text));
+        posted = (SDL_PushEvent(&event) > 0);
+    }
+    return (posted);
+}
+
 void
 SDL_KeyboardQuit(void)
 {
--- a/src/events/SDL_keyboard_c.h	Sat Sep 19 12:48:52 2009 +0000
+++ b/src/events/SDL_keyboard_c.h	Sat Sep 19 13:29:40 2009 +0000
@@ -81,6 +81,9 @@
 /* Send keyboard text input for a keyboard at an index */
 extern int SDL_SendKeyboardText(int index, const char *text);
 
+/* Send editing text for selected range from start to end */
+extern int SDL_SendEditingText(const char *text, int start, int end);
+
 /* Shutdown the keyboard subsystem */
 extern void SDL_KeyboardQuit(void);
 
--- a/src/video/SDL_sysvideo.h	Sat Sep 19 12:48:52 2009 +0000
+++ b/src/video/SDL_sysvideo.h	Sat Sep 19 13:29:40 2009 +0000
@@ -277,6 +277,11 @@
     /* Suspend the screensaver */
     void (*SuspendScreenSaver) (_THIS);
 
+    /* Text input */
+    void (*StartTextInput) (_THIS);
+    void (*StopTextInput) (_THIS);
+    void (*SetTextInputRect) (_THIS, SDL_Rect *rect);
+
     /* * * */
     /* Data common to all drivers */
     SDL_bool suspend_screensaver;
--- a/src/video/SDL_video.c	Sat Sep 19 12:48:52 2009 +0000
+++ b/src/video/SDL_video.c	Sat Sep 19 13:29:40 2009 +0000
@@ -3233,4 +3233,32 @@
     return (_this->GetWindowWMInfo(_this, window, info));
 }
 
+void
+SDL_StartTextInput(void)
+{
+    if (_this->StartTextInput) {
+        _this->StartTextInput(_this);
+    }
+    SDL_EventState(SDL_TEXTINPUT, SDL_ENABLE);
+    SDL_EventState(SDL_TEXTEDITING, SDL_ENABLE);
+}
+
+void
+SDL_StopTextInput(void)
+{
+    if (_this->StopTextInput) {
+        _this->StopTextInput(_this);
+    }
+    SDL_EventState(SDL_TEXTINPUT, SDL_DISABLE);
+    SDL_EventState(SDL_TEXTEDITING, SDL_DISABLE);
+}
+
+void
+SDL_SetTextInputRect(SDL_Rect *rect)
+{
+    if (_this->SetTextInputRect) {
+        _this->SetTextInputRect(_this, rect);
+    }
+}
+
 /* vi: set ts=4 sw=4 expandtab: */
--- a/src/video/cocoa/SDL_cocoaevents.m	Sat Sep 19 12:48:52 2009 +0000
+++ b/src/video/cocoa/SDL_cocoaevents.m	Sat Sep 19 13:29:40 2009 +0000
@@ -193,6 +193,10 @@
             Cocoa_HandleKeyEvent(_this, event);
             /* Fall through to pass event to NSApp; er, nevermind... */
             /* FIXME: Find a way to stop the beeping, using delegate */
+
+            /* Add to support system-wide keyboard shortcuts like CMD+Space */
+            if (([event modifierFlags] & NSCommandKeyMask) || [event type] == NSFlagsChanged)
+               [NSApp sendEvent: event];
             break;
         default:
             [NSApp sendEvent:event];
--- a/src/video/cocoa/SDL_cocoakeyboard.h	Sat Sep 19 12:48:52 2009 +0000
+++ b/src/video/cocoa/SDL_cocoakeyboard.h	Sat Sep 19 13:29:40 2009 +0000
@@ -28,6 +28,10 @@
 extern void Cocoa_HandleKeyEvent(_THIS, NSEvent * event);
 extern void Cocoa_QuitKeyboard(_THIS);
 
+extern void Cocoa_StartTextInput(_THIS);
+extern void Cocoa_StopTextInput(_THIS);
+extern void Cocoa_SetTextInputRect(_THIS, SDL_Rect *rect);
+
 #endif /* _SDL_cocoakeyboard_h */
 
 /* vi: set ts=4 sw=4 expandtab: */
--- a/src/video/cocoa/SDL_cocoakeyboard.m	Sat Sep 19 12:48:52 2009 +0000
+++ b/src/video/cocoa/SDL_cocoakeyboard.m	Sat Sep 19 13:29:40 2009 +0000
@@ -28,6 +28,8 @@
 
 #include <Carbon/Carbon.h>
 
+//#define DEBUG_IME NSLog
+#define DEBUG_IME
 
 #ifndef NX_DEVICERCTLKEYMASK
     #define NX_DEVICELCTLKEYMASK    0x00000001
@@ -54,14 +56,142 @@
     #define NX_DEVICERCTLKEYMASK    0x00002000
 #endif
 
-@interface SDLTranslatorResponder : NSTextView
+@interface SDLTranslatorResponder : NSView <NSTextInput>
 {
+    NSString *_markedText;
+    NSRange   _markedRange;
+    NSRange   _selectedRange;
+    SDL_Rect  _inputRect;
+    int       _keyboard;
 }
 - (void) doCommandBySelector:(SEL)myselector;
+- (void) setInputRect:(SDL_Rect *) rect;
+- (void) setKeyboard:(int) keyboard;
 @end
 
 @implementation SDLTranslatorResponder
-- (void) doCommandBySelector:(SEL) myselector {}
+
+- (void) setKeyboard:(int) keyboard
+{
+    _keyboard = keyboard;
+}
+
+- (void) setInputRect:(SDL_Rect *) rect
+{
+    _inputRect = *rect;
+}
+
+- (void) insertText:(id) aString
+{
+    const char *str;
+
+    DEBUG_IME(@"insertText: %@", aString);
+
+    /* Could be NSString or NSAttributedString, so we have
+     * to test and convert it before return as SDL event */
+    if ([aString isKindOfClass: [NSAttributedString class]])
+        str = [[aString string] UTF8String];
+    else
+        str = [aString UTF8String];
+
+    SDL_SendKeyboardText(_keyboard, str);
+}
+
+- (void) doCommandBySelector:(SEL) myselector
+{
+    [super doCommandBySelector: myselector];
+}
+
+- (BOOL) hasMarkedText
+{
+    return _markedText != nil;
+}
+
+- (NSRange) markedRange
+{
+    return _markedRange;
+}
+
+- (NSRange) selectedRange
+{
+    return _selectedRange;
+}
+
+- (void) setMarkedText:(id) aString
+         selectedRange:(NSRange) selRange
+{
+    if ([aString isKindOfClass: [NSAttributedString class]])
+        aString = [aString string];
+
+    if ([aString length] == 0)
+    {
+        [self unmarkText];
+        return;
+    }
+
+    if (_markedText != aString)
+    {
+        [_markedText release];
+        _markedText = [aString retain];
+    }
+
+    _selectedRange = selRange;
+    _markedRange = NSMakeRange(0, [aString length]);
+
+    SDL_SendEditingText([aString UTF8String], selRange.location, selRange.length);
+
+    DEBUG_IME(@"setMarkedText: %@, (%d, %d)", _markedText,
+          selRange.location, selRange.length);
+}
+
+- (void) unmarkText
+{
+    [_markedText release];
+    _markedText = nil;
+}
+
+- (NSRect) firstRectForCharacterRange: (NSRange) theRange
+{
+    float windowHeight = [[self window] frame].size.height;
+    NSRect rect = NSMakeRect(_inputRect.x, windowHeight - _inputRect.y - _inputRect.h,
+                             _inputRect.w, _inputRect.h);
+
+    DEBUG_IME(@"firstRectForCharacterRange: (%d, %d): windowHeight = %g, rect = %@",
+            theRange.location, theRange.length, windowHeight,
+            NSStringFromRect(rect));
+    rect.origin = [[self window] convertBaseToScreen: rect.origin];
+
+    return rect;
+}
+
+- (NSAttributedString *) attributedSubstringFromRange: (NSRange) theRange
+{
+    DEBUG_IME(@"attributedSubstringFromRange: (%d, %d)", theRange.location, theRange.length);
+    return nil;
+}
+
+- (NSInteger) conversationIdentifier
+{
+    return (NSInteger) self;
+}
+
+// This method returns the index for character that is 
+// nearest to thePoint.  thPoint is in screen coordinate system.
+- (NSUInteger) characterIndexForPoint:(NSPoint) thePoint
+{
+    DEBUG_IME(@"characterIndexForPoint: (%g, %g)", thePoint.x, thePoint.y);
+    return 0;
+}
+
+// This method is the key to attribute extension. 
+// We could add new attributes through this method.
+// NSInputServer examines the return value of this
+// method & constructs appropriate attributed string.
+- (NSArray *) validAttributesForMarkedText
+{
+    return [NSArray array];
+}
+
 @end
 
 /* This is the original behavior, before support was added for 
@@ -478,12 +608,7 @@
 {
     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
     SDL_Keyboard keyboard;
-    NSAutoreleasePool *pool;
 
-    pool = [[NSAutoreleasePool alloc] init];
-    data->fieldEdit = [[SDLTranslatorResponder alloc] initWithFrame:NSMakeRect(0.0, 0.0, 0.0, 0.0)];
-    [pool release];
-    
     SDL_zero(keyboard);
     data->keyboard = SDL_AddKeyboard(&keyboard, -1);
     UpdateKeymap(data);
@@ -498,6 +623,47 @@
 }
 
 void
+Cocoa_StartTextInput(_THIS)
+{
+    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    NSView *parentView = [[NSApp keyWindow] contentView];
+
+    data->fieldEdit = [[SDLTranslatorResponder alloc] initWithFrame:NSMakeRect(0.0, 0.0, 0.0, 0.0)];
+    [data->fieldEdit setKeyboard: data->keyboard];
+
+    if (! [[data->fieldEdit superview] isEqual: parentView])
+    {
+        // DEBUG_IME(@"add fieldEdit to window contentView");
+        [data->fieldEdit removeFromSuperview];
+        [parentView addSubview: data->fieldEdit];
+        [[NSApp keyWindow] makeFirstResponder: data->fieldEdit];
+    }
+
+    [pool release];
+}
+
+void
+Cocoa_StopTextInput(_THIS)
+{
+    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
+
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    [data->fieldEdit removeFromSuperview];
+    [data->fieldEdit release];
+    data->fieldEdit = nil;
+    [pool release];
+}
+
+void
+Cocoa_SetTextInputRect(_THIS, SDL_Rect *rect)
+{
+    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
+
+    [data->fieldEdit setInputRect: rect];
+}
+
+void
 Cocoa_HandleKeyEvent(_THIS, NSEvent *event)
 {
     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
@@ -533,11 +699,13 @@
         if (SDL_EventState(SDL_TEXTINPUT, SDL_QUERY)) {
             /* FIXME CW 2007-08-16: only send those events to the field editor for which we actually want text events, not e.g. esc or function keys. Arrow keys in particular seem to produce crashes sometimes. */
             [data->fieldEdit interpretKeyEvents:[NSArray arrayWithObject:event]];
+#if 0
             text = [[event characters] UTF8String];
             if(text && *text) {
                 SDL_SendKeyboardText(data->keyboard, text);
                 [data->fieldEdit setString:@""];
             }
+#endif
         }
         break;
     case NSKeyUp:
@@ -559,10 +727,6 @@
     NSAutoreleasePool *pool;
 
     SDL_DelKeyboard(data->keyboard);
-
-    pool = [[NSAutoreleasePool alloc] init];
-    [data->fieldEdit release];
-    [pool release];
 }
 
 /* vi: set ts=4 sw=4 expandtab: */
--- a/src/video/cocoa/SDL_cocoamodes.m	Sat Sep 19 12:48:52 2009 +0000
+++ b/src/video/cocoa/SDL_cocoamodes.m	Sat Sep 19 13:29:40 2009 +0000
@@ -250,6 +250,8 @@
         CGReleaseDisplayFadeReservation(fade_token);
     }
 
+    [[NSApp mainWindow] makeKeyAndOrderFront: nil];
+
 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1050
     /* 
         There is a bug in Cocoa where NSScreen doesn't synchronize
--- a/src/video/cocoa/SDL_cocoavideo.h	Sat Sep 19 12:48:52 2009 +0000
+++ b/src/video/cocoa/SDL_cocoavideo.h	Sat Sep 19 13:29:40 2009 +0000
@@ -41,6 +41,8 @@
 
 /* Private display data */
 
+@class SDLTranslatorResponder;
+
 typedef struct SDL_VideoData
 {
     SInt32 osversion;
@@ -48,7 +50,7 @@
     int mouse;
     int keyboard;
     void *key_layout;
-    NSText *fieldEdit;
+    SDLTranslatorResponder *fieldEdit;
     Uint32 screensaver_activity;
 } SDL_VideoData;
 
--- a/src/video/cocoa/SDL_cocoavideo.m	Sat Sep 19 12:48:52 2009 +0000
+++ b/src/video/cocoa/SDL_cocoavideo.m	Sat Sep 19 13:29:40 2009 +0000
@@ -102,6 +102,10 @@
     device->GL_DeleteContext = Cocoa_GL_DeleteContext;
 #endif
 
+    device->StartTextInput = Cocoa_StartTextInput;
+    device->StopTextInput = Cocoa_StopTextInput;
+    device->SetTextInputRect = Cocoa_SetTextInputRect;
+
     device->free = Cocoa_DeleteDevice;
 
     return device;
--- a/test/Makefile.in	Sat Sep 19 12:48:52 2009 +0000
+++ b/test/Makefile.in	Sat Sep 19 13:29:40 2009 +0000
@@ -7,7 +7,7 @@
 CFLAGS  = @CFLAGS@
 LIBS	= @LIBS@
 
-TARGETS = checkkeys$(EXE) graywin$(EXE) loopwave$(EXE) testresample$(EXE) testaudioinfo$(EXE) testmultiaudio$(EXE) testpower$(EXE) testalpha$(EXE) testbitmap$(EXE) testblitspeed$(EXE) testcursor$(EXE) testintersections$(EXE) testdraw2$(EXE) testdyngl$(EXE) testdyngles$(EXE) testerror$(EXE) testfile$(EXE) testgamma$(EXE) testgl$(EXE) testgl2$(EXE) testgles$(EXE) testhread$(EXE) testiconv$(EXE) testjoystick$(EXE) testkeys$(EXE) testlock$(EXE) testoverlay2$(EXE) testoverlay$(EXE) testpalette$(EXE) testplatform$(EXE) testsem$(EXE) testsprite$(EXE) testsprite2$(EXE) testtimer$(EXE) testver$(EXE) testvidinfo$(EXE) testwin$(EXE) testwm$(EXE) testwm2$(EXE) threadwin$(EXE) torturethread$(EXE) testloadso$(EXE) testhaptic$(EXE) testmmousetablet$(EXE) testatomic$(EXE)
+TARGETS = checkkeys$(EXE) graywin$(EXE) loopwave$(EXE) testresample$(EXE) testaudioinfo$(EXE) testmultiaudio$(EXE) testpower$(EXE) testalpha$(EXE) testbitmap$(EXE) testblitspeed$(EXE) testcursor$(EXE) testintersections$(EXE) testdraw2$(EXE) testdyngl$(EXE) testdyngles$(EXE) testerror$(EXE) testfile$(EXE) testgamma$(EXE) testgl$(EXE) testgl2$(EXE) testgles$(EXE) testhread$(EXE) testiconv$(EXE) testjoystick$(EXE) testkeys$(EXE) testlock$(EXE) testoverlay2$(EXE) testoverlay$(EXE) testpalette$(EXE) testplatform$(EXE) testsem$(EXE) testsprite$(EXE) testsprite2$(EXE) testtimer$(EXE) testver$(EXE) testvidinfo$(EXE) testwin$(EXE) testwm$(EXE) testwm2$(EXE) threadwin$(EXE) torturethread$(EXE) testloadso$(EXE) testhaptic$(EXE) testmmousetablet$(EXE) testatomic$(EXE) testime$(EXE)
 
 all: Makefile $(TARGETS)
 
@@ -149,6 +149,9 @@
 testatomic$(EXE): $(srcdir)/testatomic.c
 	$(CC) -o $@ $? $(CFLAGS) $(LIBS)
  
+testime$(EXE): $(srcdir)/testime.c
+	$(CC) -o $@ $? $(CFLAGS) $(LIBS) @SDL_TTF_LIB@
+
 clean:
 	rm -f $(TARGETS)
 
--- a/test/configure.in	Sat Sep 19 12:48:52 2009 +0000
+++ b/test/configure.in	Sat Sep 19 13:29:40 2009 +0000
@@ -128,5 +128,23 @@
 
 AC_SUBST(GLLIB)
 
+dnl Check for SDL_ttf
+AC_MSG_CHECKING(for SDL_ttf)
+have_SDL_ttf=no
+AC_TRY_COMPILE([
+ #include "SDL_ttf.h"
+],[
+],[
+have_SDL_ttf=yes
+])
+AC_MSG_RESULT($have_SDL_ttf)
+
+if test x$have_SDL_ttf = xyes; then
+    CFLAGS="$CFLAGS -DHAVE_SDL_TTF"
+    SDL_TTF_LIB="-lSDL_ttf"
+fi
+
+AC_SUBST(SDL_TTF_LIB)
+
 dnl Finally create all the generated files
 AC_OUTPUT([Makefile])
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/testime.c	Sat Sep 19 13:29:40 2009 +0000
@@ -0,0 +1,275 @@
+/* A simple program to test the Input Method support in the SDL library (1.3+) */
+ 
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "SDL.h"
+#ifdef HAVE_SDL_TTF
+#include "SDL_ttf.h"
+#endif
+ 
+#define DEFAULT_PTSIZE  30
+#define DEFAULT_FONT    "/System/Library/Fonts/华文细黑.ttf"
+#define MAX_TEXT_LENGTH 256
+
+SDL_Surface *screen;
+
+#ifdef HAVE_SDL_TTF
+TTF_Font *font;
+#endif
+SDL_Rect textRect, markedRect;
+Uint32 lineColor, backColor;
+SDL_Color textColor = { 0, 0, 0 };
+char text[MAX_TEXT_LENGTH], *markedText;
+
+void usage()
+{
+    printf("usage: testime [--font fontfile] [--fullscreen]\n");
+    exit(0);
+}
+
+void InitVideo(int argc, char *argv[])
+{
+    int width = 500, height = 250;
+    int flags = SDL_HWSURFACE;
+    const char *fontname = DEFAULT_FONT;
+    int fullscreen = 0;
+
+    for (argc--, argv++; argc > 0; argc--, argv++)
+    {
+        if (strcmp(argv[0], "--help") == 0)
+            usage();
+
+        else if (strcmp(argv[0], "--fullscreen") == 0)
+            fullscreen = 1;
+
+        else if (strcmp(argv[0], "--font") == 0)
+        {
+            argc--;
+            argv++;
+
+            if (argc > 0)
+                fontname = argv[0];
+            else
+                usage();
+        }
+    }
+
+    SDL_putenv("SDL_VIDEO_WINDOW_POS=center");
+    if (SDL_Init(SDL_INIT_VIDEO) < 0)
+    {
+        fprintf(stderr, "Unable to init SDL: %s\n", TTF_GetError());
+        exit(-1);
+    }
+
+#ifdef HAVE_SDL_TTF
+    /* Initialize fonts */
+    TTF_Init();
+
+    font = TTF_OpenFont(fontname, DEFAULT_PTSIZE);
+    if (! font)
+    {
+        fprintf(stderr, "Failed to find font: %s\n", SDL_GetError());
+        exit(-1);
+    }
+#endif
+
+    printf("Using font: %s\n", fontname);
+    atexit(SDL_Quit);
+
+    if (fullscreen)
+    {
+        SDL_DisplayMode mode;
+        SDL_GetDesktopDisplayMode(&mode);
+
+        width = mode.w;
+        height = mode.h;
+        fprintf(stderr, "%dx%d\n", width, height);
+        flags |= SDL_FULLSCREEN;
+    }
+
+    /* Create window */
+    screen = SDL_SetVideoMode(width, height, 32, flags);
+    if (screen == NULL)
+    {
+        fprintf(stderr, "Unable to set %dx%d video: %s\n",
+                width, height, SDL_GetError());
+        exit(-1);
+    }
+}
+
+void CleanupVideo()
+{
+    SDL_StopTextInput();
+#ifdef HAVE_SDL_TTF
+    TTF_CloseFont(font);
+    TTF_Quit();
+#endif
+}
+
+void InitInput()
+{
+    backColor = SDL_MapRGB(screen->format, 0xFF, 0xFF, 0xFF);
+    lineColor = SDL_MapRGB(screen->format, 0x0, 0x0, 0x0);
+
+    /* Prepare a rect for text input */
+    textRect.x = textRect.y = 100;
+    textRect.w = screen->w - 2 * textRect.x;
+    textRect.h = 50;
+
+    text[0] = 0;
+    markedRect = textRect;
+    markedText = NULL;
+
+    SDL_StartTextInput();
+}
+
+#ifdef HAVE_SDL_TTF
+static void RenderText(SDL_Surface *sur,
+                        TTF_Font *font,
+                        const char *text,
+                        int x, int y,
+                        SDL_Color color)
+{
+    SDL_Surface *textSur = TTF_RenderUTF8_Blended(font, text, color);
+    SDL_Rect dest = { x, y, textSur->w, textSur->h };
+
+    SDL_BlitSurface(textSur, NULL, sur, &dest);
+    SDL_FreeSurface(textSur);
+}
+#endif
+
+void Redraw()
+{
+    int w = 0, h = textRect.h;
+    SDL_Rect cursorRect, underlineRect;
+
+    SDL_FillRect(screen, &textRect, backColor);
+
+#ifdef HAVE_SDL_TTF
+    if (strlen(text))
+    {
+        RenderText(screen, font, text, textRect.x, textRect.y, textColor);
+        TTF_SizeUTF8(font, text, &w, &h);
+    }
+#endif
+
+    markedRect.x = textRect.x + w;
+    markedRect.w = textRect.w - w;
+    if (markedRect.w < 0)
+    {
+        SDL_Flip(screen);
+        // Stop text input because we cannot hold any more characters
+        SDL_StopTextInput();
+        return;
+    }
+
+    cursorRect = markedRect;
+    cursorRect.w = 2;
+    cursorRect.h = h;
+
+    SDL_FillRect(screen, &markedRect, backColor);
+    if (markedText)
+    {
+#ifdef HAVE_SDL_TTF
+        RenderText(screen, font, markedText, markedRect.x, markedRect.y, textColor);
+        TTF_SizeUTF8(font, markedText, &w, &h);
+#endif
+
+        underlineRect = markedRect;
+        underlineRect.y += (h - 2);
+        underlineRect.h = 2;
+        underlineRect.w = w;
+
+        cursorRect.x += w + 1;
+
+        SDL_FillRect(screen, &underlineRect, lineColor);
+    }
+
+    SDL_FillRect(screen, &cursorRect, lineColor);
+
+    SDL_Flip(screen);
+
+    SDL_SetTextInputRect(&markedRect);
+}
+
+void
+HotKey_ToggleFullScreen(void)
+{
+    SDL_Surface *screen;
+
+    screen = SDL_GetVideoSurface();
+    if (SDL_WM_ToggleFullScreen(screen)) {
+        printf("Toggled fullscreen mode - now %s\n",
+               (screen->flags & SDL_FULLSCREEN) ? "fullscreen" : "windowed");
+    } else {
+        printf("Unable to toggle fullscreen mode\n");
+    }
+}
+
+int main(int argc, char *argv[])
+{
+    InitVideo(argc, argv);
+    InitInput();
+    Redraw();
+
+    SDL_Event event;
+    int done = 0;
+
+    while (! done && SDL_WaitEvent(&event))
+    {
+        switch (event.type)
+        {
+        case SDL_KEYDOWN:
+            if (event.key.keysym.sym == SDLK_ESCAPE) {
+                done = 1;
+                break;
+            }
+
+            fprintf(stderr,
+                    "Keyboard %d: scancode 0x%08X = %s, keycode 0x%08X = %s\n",
+                    event.key.which, event.key.keysym.scancode,
+                    SDL_GetScancodeName(event.key.keysym.scancode),
+                    event.key.keysym.sym, SDL_GetKeyName(event.key.keysym.sym));
+            break;
+
+        case SDL_TEXTINPUT:
+            if (strlen(event.text.text) == 0 || event.text.text[0] == '\n' ||
+                markedRect.w < 0)
+                break;
+
+            fprintf(stderr, "Keyboard %d: text input \"%s\"\n",
+                    event.text.which, event.text.text);
+
+            if (strlen(text) + strlen(event.text.text) < sizeof(text))
+                strcpy(text + strlen(text), event.text.text);
+
+            fprintf(stderr, "text inputed: %s\n", text);
+
+            // After text inputed, we can clear up markedText because it
+            // is committed
+            markedText = NULL;
+            Redraw();
+            break;
+
+        case SDL_TEXTEDITING:
+            fprintf(stderr, "text editing \"%s\", selected range (%d, %d)\n",
+                    event.edit.text, event.edit.start, event.edit.length);
+
+            markedText = event.edit.text;
+            Redraw();
+            break;
+
+        case SDL_QUIT:
+            done = 1;
+            break;
+
+        default:
+            break;
+        }
+    }
+
+    CleanupVideo();
+    return 0;
+}