Merged a cleaned up version of Jiang's code changes from Google Summer of Code 2009
--- 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;
+}