summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Lantinga <slouken@libsdl.org>2013-05-18 12:48:50 -0700
committerSam Lantinga <slouken@libsdl.org>2013-05-18 12:48:50 -0700
commit3beb70c4bcbd2ea9ef4e69cbcfe75c6650fbea50 (patch)
tree7650e89e5bb0e5e9de89c0ca6856d6d8012468ff
parentd99b036e0845c70f56e257ebe03bd3f824d0d02b (diff)
Added mobile application events, with implementations for iOS and Android
-rw-r--r--README.iOS63
-rw-r--r--android-project/src/org/libsdl/app/SDLActivity.java16
-rw-r--r--include/SDL_events.h36
-rw-r--r--src/core/android/SDL_android.cpp20
-rw-r--r--src/events/SDL_events.c22
-rw-r--r--src/events/SDL_events_c.h1
-rw-r--r--src/events/SDL_quit.c10
-rw-r--r--src/video/uikit/SDL_uikitappdelegate.m52
-rw-r--r--src/video/uikit/SDL_uikitopengles.m6
9 files changed, 182 insertions, 44 deletions
diff --git a/README.iOS b/README.iOS
index a1844a3fb8..60f3391dc8 100644
--- a/README.iOS
+++ b/README.iOS
@@ -55,6 +55,69 @@ Here is a more manual method:
5. Delete the contents of main.m and program your app as a regular SDL program instead. You may replace main.m with your own main.c, but you must tell XCode not to use the project prefix file, as it includes Objective-C code.
==============================================================================
+Notes -- Application events
+==============================================================================
+
+On iOS the application goes through a fixed life cycle and you will get
+notifications of state changes via application events. When these events
+are delivered you must handle them in an event callback because the OS may
+not give you any processing time after the events are delivered.
+
+e.g.
+
+int HandleAppEvents(void *userdata, SDL_Event *event)
+{
+ switch (event->type)
+ {
+ case SDL_APP_TERMINATING:
+ /* Terminate the app.
+ Shut everything down before returning from this function.
+ */
+ return 0;
+ case SDL_APP_LOWMEMORY:
+ /* You will get this when your app is paused and iOS wants more memory.
+ Release as much memory as possible.
+ */
+ return 0;
+ case SDL_APP_WILLENTERBACKGROUND:
+ /* Prepare your app to go into the background. Stop loops, etc.
+ This gets called when the user hits the home button, or gets a call.
+ */
+ return 0;
+ case SDL_APP_DIDENTERBACKGROUND:
+ /* This will get called if the user accepted whatever sent your app to the background.
+ If the user got a phone call and canceled it, you'll instead get an SDL_APP_DIDENTERFOREGROUND event and restart your loops.
+ When you get this, you have 5 seconds to save all your state or the app will be terminated.
+ Your app is NOT active at this point.
+ */
+ return 0;
+ case SDL_APP_WILLENTERFOREGROUND:
+ /* This call happens when your app is coming back to the foreground.
+ Restore all your state here.
+ */
+ return 0;
+ case SDL_APP_DIDENTERFOREGROUND:
+ /* Restart your loops here.
+ Your app is interactive and getting CPU again.
+ */
+ return 0;
+ default:
+ /* No special processing, add it to the event queue */
+ return 1;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ SDL_SetEventFilter(HandleAppEvents, NULL);
+
+ ... run your main loop
+
+ return 0;
+}
+
+
+==============================================================================
Notes -- Accelerometer as Joystick
==============================================================================
diff --git a/android-project/src/org/libsdl/app/SDLActivity.java b/android-project/src/org/libsdl/app/SDLActivity.java
index fed65c510b..04bbeff15a 100644
--- a/android-project/src/org/libsdl/app/SDLActivity.java
+++ b/android-project/src/org/libsdl/app/SDLActivity.java
@@ -78,17 +78,26 @@ public class SDLActivity extends Activity {
}
// Events
- /*protected void onPause() {
+ @Override
+ protected void onPause() {
Log.v("SDL", "onPause()");
super.onPause();
// Don't call SDLActivity.nativePause(); here, it will be called by SDLSurface::surfaceDestroyed
}
+ @Override
protected void onResume() {
Log.v("SDL", "onResume()");
super.onResume();
// Don't call SDLActivity.nativeResume(); here, it will be called via SDLSurface::surfaceChanged->SDLActivity::startApp
- }*/
+ }
+
+ @Override
+ public void onLowMemory() {
+ Log.v("SDL", "onLowMemory()");
+ super.onLowMemory();
+ SDLActivity.nativeLowMemory();
+ }
@Override
protected void onDestroy() {
@@ -180,6 +189,7 @@ public class SDLActivity extends Activity {
// C functions we call
public static native void nativeInit();
+ public static native void nativeLowMemory();
public static native void nativeQuit();
public static native void nativePause();
public static native void nativeResume();
@@ -600,8 +610,6 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
public void onDraw(Canvas canvas) {}
-
-
// Key events
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
diff --git a/include/SDL_events.h b/include/SDL_events.h
index 6e6a5c8dcf..65ba6b1583 100644
--- a/include/SDL_events.h
+++ b/include/SDL_events.h
@@ -61,6 +61,32 @@ typedef enum
/* Application events */
SDL_QUIT = 0x100, /**< User-requested quit */
+ /* These application events have special meaning on iOS, see README.iOS for details */
+ SDL_APP_TERMINATING, /**< The application is being terminated by the OS
+ Called on iOS in applicationWillTerminate()
+ Called on Android in onDestroy()
+ */
+ SDL_APP_LOWMEMORY, /**< The application is low on memory, free memory if possible.
+ Called on iOS in applicationDidReceiveMemoryWarning()
+ Called on Android in onLowMemory()
+ */
+ SDL_APP_WILLENTERBACKGROUND, /**< The application is about to enter the background
+ Called on iOS in applicationWillResignActive()
+ Called on Android in onPause()
+ */
+ SDL_APP_DIDENTERBACKGROUND, /**< The application did enter the background and may not get CPU for some time
+ Called on iOS in applicationDidEnterBackground()
+ Called on Android in onPause()
+ */
+ SDL_APP_WILLENTERFOREGROUND, /**< The application is about to enter the foreground
+ Called on iOS in applicationWillEnterForeground()
+ Called on Android in onResume()
+ */
+ SDL_APP_DIDENTERFOREGROUND, /**< The application is now interactive
+ Called on iOS in applicationDidBecomeActive()
+ Called on Android in onResume()
+ */
+
/* Window events */
SDL_WINDOWEVENT = 0x200, /**< Window state change */
SDL_SYSWMEVENT, /**< System specific event */
@@ -109,7 +135,7 @@ typedef enum
/* Drag and drop events */
SDL_DROPFILE = 0x1000, /**< The system requests a file open */
-
+
/** Events ::SDL_USEREVENT through ::SDL_LASTEVENT are for your use,
* and should be allocated with SDL_RegisterEvents()
*/
@@ -427,6 +453,14 @@ typedef struct SDL_QuitEvent
Uint32 timestamp;
} SDL_QuitEvent;
+/**
+ * \brief OS Specific event
+ */
+typedef struct SDL_OSEvent
+{
+ Uint32 type; /**< ::SDL_QUIT */
+ Uint32 timestamp;
+} SDL_OSEvent;
/**
* \brief A user-defined event type (event.user.*)
diff --git a/src/core/android/SDL_android.cpp b/src/core/android/SDL_android.cpp
index ceb3555b0d..cf0e0c1292 100644
--- a/src/core/android/SDL_android.cpp
+++ b/src/core/android/SDL_android.cpp
@@ -181,12 +181,20 @@ extern "C" void Java_org_libsdl_app_SDLActivity_onNativeAccel(
bHasNewData = true;
}
+// Low memory
+extern "C" void Java_org_libsdl_app_SDLActivity_nativeLowMemory(
+ JNIEnv* env, jclass cls)
+{
+ SDL_SendAppEvent(SDL_APP_LOWMEMORY);
+}
+
// Quit
extern "C" void Java_org_libsdl_app_SDLActivity_nativeQuit(
JNIEnv* env, jclass cls)
{
// Inject a SDL_QUIT event
SDL_SendQuit();
+ SDL_SendAppEvent(SDL_APP_TERMINATING);
}
// Pause
@@ -199,12 +207,20 @@ extern "C" void Java_org_libsdl_app_SDLActivity_nativePause(
SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
}
+
+ __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativePause()");
+ SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND);
+ SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND);
}
// Resume
extern "C" void Java_org_libsdl_app_SDLActivity_nativeResume(
JNIEnv* env, jclass cls)
{
+ __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeResume()");
+ SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND);
+ SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND);
+
if (Android_Window) {
/* Signal the resume semaphore so the event loop knows to resume and restore the GL Context
* We can't restore the GL Context here because it needs to be done on the SDL main thread
@@ -616,7 +632,9 @@ static int Android_JNI_FileOpen(SDL_RWops* ctx)
if (false) {
fallback:
- __android_log_print(ANDROID_LOG_DEBUG, "SDL", "Falling back to legacy InputStream method for opening file");
+ // Disabled log message because of spam on the Nexus 7
+ //__android_log_print(ANDROID_LOG_DEBUG, "SDL", "Falling back to legacy InputStream method for opening file");
+
/* Try the old method using InputStream */
ctx->hidden.androidio.assetFileDescriptorRef = NULL;
diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c
index ecba3bb5b8..d6bb22e06a 100644
--- a/src/events/SDL_events.c
+++ b/src/events/SDL_events.c
@@ -111,6 +111,7 @@ SDL_StopEventLoop(void)
SDL_event_watchers = tmp->next;
SDL_free(tmp);
}
+ SDL_EventOK = NULL;
}
/* This function (and associated calls) may be called more than once */
@@ -133,8 +134,7 @@ SDL_StartEventLoop(void)
}
#endif /* !SDL_THREADS_DISABLED */
- /* No filter to start with, process most event types */
- SDL_EventOK = NULL;
+ /* Process most event types */
SDL_EventState(SDL_TEXTINPUT, SDL_DISABLE);
SDL_EventState(SDL_TEXTEDITING, SDL_DISABLE);
SDL_EventState(SDL_SYSWMEVENT, SDL_DISABLE);
@@ -365,7 +365,9 @@ int
SDL_PushEvent(SDL_Event * event)
{
SDL_EventWatcher *curr;
+
event->common.timestamp = SDL_GetTicks();
+
if (SDL_EventOK && !SDL_EventOK(SDL_EventOKParam, event)) {
return 0;
}
@@ -516,8 +518,20 @@ SDL_RegisterEvents(int numevents)
return event_base;
}
-/* This is a generic event handler.
- */
+int
+SDL_SendAppEvent(SDL_EventType eventType)
+{
+ int posted;
+
+ posted = 0;
+ if (SDL_GetEventState(eventType) == SDL_ENABLE) {
+ SDL_Event event;
+ event.type = eventType;
+ posted = (SDL_PushEvent(&event) > 0);
+ }
+ return (posted);
+}
+
int
SDL_SendSysWMEvent(SDL_SysWMmsg * message)
{
diff --git a/src/events/SDL_events_c.h b/src/events/SDL_events_c.h
index 7d6ab3b19d..f365ee3edd 100644
--- a/src/events/SDL_events_c.h
+++ b/src/events/SDL_events_c.h
@@ -36,6 +36,7 @@ extern int SDL_StartEventLoop(void);
extern void SDL_StopEventLoop(void);
extern void SDL_QuitInterrupt(void);
+extern int SDL_SendAppEvent(SDL_EventType eventType);
extern int SDL_SendSysWMEvent(SDL_SysWMmsg * message);
extern int SDL_QuitInit(void);
diff --git a/src/events/SDL_quit.c b/src/events/SDL_quit.c
index 8c1dcf5740..5e9f8ad851 100644
--- a/src/events/SDL_quit.c
+++ b/src/events/SDL_quit.c
@@ -114,15 +114,7 @@ SDL_QuitQuit(void)
int
SDL_SendQuit(void)
{
- int posted;
-
- posted = 0;
- if (SDL_GetEventState(SDL_QUIT) == SDL_ENABLE) {
- SDL_Event event;
- event.type = SDL_QUIT;
- posted = (SDL_PushEvent(&event) > 0);
- }
- return (posted);
+ return SDL_SendAppEvent(SDL_QUIT);
}
/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/video/uikit/SDL_uikitappdelegate.m b/src/video/uikit/SDL_uikitappdelegate.m
index 5337f619e9..11e74025ed 100644
--- a/src/video/uikit/SDL_uikitappdelegate.m
+++ b/src/video/uikit/SDL_uikitappdelegate.m
@@ -228,42 +228,48 @@ static void SDL_IdleTimerDisabledChanged(const char *name, const char *oldValue,
- (void)applicationWillTerminate:(UIApplication *)application
{
- SDL_SendQuit();
- /* hack to prevent automatic termination. See SDL_uikitevents.m for details */
- longjmp(*(jump_env()), 1);
+ SDL_SendAppEvent(SDL_APP_TERMINATING);
}
-- (void) applicationWillResignActive:(UIApplication*)application
+- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
{
- //NSLog(@"%@", NSStringFromSelector(_cmd));
+ SDL_SendAppEvent(SDL_APP_LOWMEMORY);
+}
- // Send every window on every screen a MINIMIZED event.
+- (void) applicationWillResignActive:(UIApplication*)application
+{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
- if (!_this) {
- return;
+ if (_this) {
+ SDL_Window *window;
+ for (window = _this->windows; window != nil; window = window->next) {
+ SDL_SendWindowEvent(window, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
+ SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
+ }
}
+ SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND);
+}
- SDL_Window *window;
- for (window = _this->windows; window != nil; window = window->next) {
- SDL_SendWindowEvent(window, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
- SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
- }
+- (void) applicationDidEnterBackground:(UIApplication*)application
+{
+ SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND);
+}
+
+- (void) applicationWillEnterForeground:(UIApplication*)application
+{
+ SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND);
}
- (void) applicationDidBecomeActive:(UIApplication*)application
{
- //NSLog(@"%@", NSStringFromSelector(_cmd));
+ SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND);
- // Send every window on every screen a RESTORED event.
SDL_VideoDevice *_this = SDL_GetVideoDevice();
- if (!_this) {
- return;
- }
-
- SDL_Window *window;
- for (window = _this->windows; window != nil; window = window->next) {
- SDL_SendWindowEvent(window, SDL_WINDOWEVENT_FOCUS_GAINED, 0, 0);
- SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESTORED, 0, 0);
+ if (_this) {
+ SDL_Window *window;
+ for (window = _this->windows; window != nil; window = window->next) {
+ SDL_SendWindowEvent(window, SDL_WINDOWEVENT_FOCUS_GAINED, 0, 0);
+ SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESTORED, 0, 0);
+ }
}
}
diff --git a/src/video/uikit/SDL_uikitopengles.m b/src/video/uikit/SDL_uikitopengles.m
index f3efab2a7a..4fc1a2773c 100644
--- a/src/video/uikit/SDL_uikitopengles.m
+++ b/src/video/uikit/SDL_uikitopengles.m
@@ -91,8 +91,10 @@ void UIKit_GL_SwapWindow(_THIS, SDL_Window * window)
}
[data->view swapBuffers];
- /* we need to let the event cycle run, or the OS won't update the OpenGL view! */
- SDL_PumpEvents();
+ /* You need to pump events in order for the OS to make changes visible.
+ We don't pump events here because we don't want iOS application events
+ (low memory, terminate, etc.) to happen inside low level rendering.
+ */
}
SDL_GLContext UIKit_GL_CreateContext(_THIS, SDL_Window * window)