Merge branch 'android_improvements'

This commit is contained in:
Jonathan De Wachter 2014-07-24 10:05:09 +02:00
commit 3f69629d0c
8 changed files with 230 additions and 131 deletions

View File

@ -42,6 +42,7 @@
#include <SFML/System/Sleep.hpp>
#include <SFML/System/Thread.hpp>
#include <SFML/System/Lock.hpp>
#include <SFML/System/Err.hpp>
#include <android/window.h>
#include <android/native_activity.h>
@ -94,12 +95,6 @@ void* main(ActivityStates* states)
// Initialize the thread before giving the hand
initializeMain(states);
{
Lock lock(states->mutex);
states->initialized = true;
}
sleep(seconds(0.5));
::main(0, NULL);
@ -119,6 +114,80 @@ void* main(ActivityStates* states)
} // namespace sf
////////////////////////////////////////////////////////////
void goToFullscreenMode(ANativeActivity* activity)
{
// Hide the status bar
ANativeActivity_setWindowFlags(activity, AWINDOW_FLAG_FULLSCREEN,
AWINDOW_FLAG_FULLSCREEN);
// Hide the navigation bar
JavaVM* lJavaVM = activity->vm;
JNIEnv* lJNIEnv = activity->env;
jobject objectActivity = activity->clazz;
jclass classActivity = lJNIEnv->GetObjectClass(objectActivity);
jmethodID methodGetWindow = lJNIEnv->GetMethodID(classActivity, "getWindow", "()Landroid/view/Window;");
jobject objectWindow = lJNIEnv->CallObjectMethod(objectActivity, methodGetWindow);
jclass classWindow = lJNIEnv->FindClass("android/view/Window");
jmethodID methodGetDecorView = lJNIEnv->GetMethodID(classWindow, "getDecorView", "()Landroid/view/View;");
jobject objectDecorView = lJNIEnv->CallObjectMethod(objectWindow, methodGetDecorView);
jclass classView = lJNIEnv->FindClass("android/view/View");
jfieldID FieldSYSTEM_UI_FLAG_LOW_PROFILE = lJNIEnv->GetStaticFieldID(classView, "SYSTEM_UI_FLAG_LOW_PROFILE", "I");
jint SYSTEM_UI_FLAG_LOW_PROFILE = lJNIEnv->GetStaticIntField(classView, FieldSYSTEM_UI_FLAG_LOW_PROFILE);
jfieldID FieldSYSTEM_UI_FLAG_FULLSCREEN = lJNIEnv->GetStaticFieldID(classView, "SYSTEM_UI_FLAG_FULLSCREEN", "I");
jint SYSTEM_UI_FLAG_FULLSCREEN = lJNIEnv->GetStaticIntField(classView, FieldSYSTEM_UI_FLAG_FULLSCREEN);
//jfieldID FieldSYSTEM_UI_FLAG_IMMERSIVE_STICKY = lJNIEnv->GetStaticFieldID(classView, "SYSTEM_UI_FLAG_IMMERSIVE_STICKY", "I");
//jint SYSTEM_UI_FLAG_IMMERSIVE_STICKY = lJNIEnv->GetStaticIntField(classView, FieldSYSTEM_UI_FLAG_IMMERSIVE_STICKY);
jmethodID methodsetSystemUiVisibility = lJNIEnv->GetMethodID(classView, "setSystemUiVisibility", "(I)V");
lJNIEnv->CallVoidMethod(objectDecorView, methodsetSystemUiVisibility, SYSTEM_UI_FLAG_LOW_PROFILE | SYSTEM_UI_FLAG_FULLSCREEN | 0x00001000);
}
////////////////////////////////////////////////////////////
void getScreenSizeInPixels(ANativeActivity* activity, int* width, int* height)
{
// Perform the following java code:
//
// DisplayMetrics dm = new DisplayMetrics();
// getWindowManager().getDefaultDisplay().getMetrics(dm);
JavaVM* lJavaVM = activity->vm;
JNIEnv* lJNIEnv = activity->env;
jobject objectActivity = activity->clazz;
jclass classActivity = lJNIEnv->GetObjectClass(objectActivity);
jclass classDisplayMetrics = lJNIEnv->FindClass("android/util/DisplayMetrics");
jmethodID initDisplayMetrics = lJNIEnv->GetMethodID(classDisplayMetrics, "<init>", "()V");
jobject objectDisplayMetrics = lJNIEnv->NewObject(classDisplayMetrics, initDisplayMetrics);
jmethodID methodGetWindowManager = lJNIEnv->GetMethodID(classActivity, "getWindowManager", "()Landroid/view/WindowManager;");
jobject objectWindowManager = lJNIEnv->CallObjectMethod(objectActivity, methodGetWindowManager);
jclass classWindowManager = lJNIEnv->FindClass("android/view/WindowManager");
jmethodID methodGetDefaultDisplay = lJNIEnv->GetMethodID(classWindowManager, "getDefaultDisplay", "()Landroid/view/Display;");
jobject objectDisplay = lJNIEnv->CallObjectMethod(objectWindowManager, methodGetDefaultDisplay);
jclass classDisplay = lJNIEnv->FindClass("android/view/Display");
jmethodID methodGetMetrics = lJNIEnv->GetMethodID(classDisplay, "getMetrics", "(Landroid/util/DisplayMetrics;)V");
lJNIEnv->CallVoidMethod(objectDisplay, methodGetMetrics, objectDisplayMetrics);
jfieldID fieldWidthPixels = lJNIEnv->GetFieldID(classDisplayMetrics, "widthPixels", "I");
jfieldID fieldHeightPixels = lJNIEnv->GetFieldID(classDisplayMetrics, "heightPixels", "I");
*width = lJNIEnv->GetIntField(objectDisplayMetrics, fieldWidthPixels);
*height = lJNIEnv->GetIntField(objectDisplayMetrics, fieldHeightPixels);
}
////////////////////////////////////////////////////////////
static void onStart(ANativeActivity* activity)
{
@ -132,11 +201,14 @@ static void onResume(ANativeActivity* activity)
sf::priv::ActivityStates* states = sf::priv::retrieveStates(activity);
sf::Lock lock(states->mutex);
if (states->fullscreen)
goToFullscreenMode(activity);
// Send an event to warn people the activity has been resumed
sf::Event event;
event.type = sf::Event::MouseEntered;
states->pendingEvents.push_back(event);
states->forwardEvent(event);
}
@ -151,7 +223,7 @@ static void onPause(ANativeActivity* activity)
sf::Event event;
event.type = sf::Event::MouseLeft;
states->pendingEvents.push_back(event);
states->forwardEvent(event);
}
@ -178,7 +250,7 @@ static void onDestroy(ANativeActivity* activity)
sf::Event event;
event.type = sf::Event::Closed;
states->pendingEvents.push_back(event);
states->forwardEvent(event);
}
}
@ -219,7 +291,7 @@ static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* wind
// Notify SFML mechanism
sf::Event event;
event.type = sf::Event::GainedFocus;
states->pendingEvents.push_back(event);
states->forwardEvent(event);
// Wait for the event to be taken into account by SFML
states->updated = false;
@ -244,7 +316,7 @@ static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* wi
// Notify SFML mechanism
sf::Event event;
event.type = sf::Event::LostFocus;
states->pendingEvents.push_back(event);
states->forwardEvent(event);
// Wait for the event to be taken into account by SFML
states->updated = false;
@ -320,7 +392,7 @@ static void onContentRectChanged(ANativeActivity* activity, const ARect* rect)
event.size.width = ANativeWindow_getWidth(states->window);
event.size.height = ANativeWindow_getHeight(states->window);
states->pendingEvents.push_back(event);
states->forwardEvent(event);
}
@ -345,14 +417,6 @@ static void onLowMemory(ANativeActivity* activity)
}
////////////////////////////////////////////////////////////
int dummyProcessEvent(int fd, int events, void* data)
{
// Do nothing
return 0;
}
////////////////////////////////////////////////////////////
void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, size_t savedStateSize)
{
@ -372,12 +436,8 @@ void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, size_
states->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
// As the input queue will be created before the SFML window, we need to use
// this dummy function that will be replaced later by the first created
// SFML window
states->processEvent = dummyProcessEvent;
if (savedState != NULL) {
if (savedState != NULL)
{
states->savedState = malloc(savedStateSize);
states->savedStateSize = savedStateSize;
memcpy(states->savedState, savedState, savedStateSize);
@ -421,38 +481,14 @@ void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, size_
ANativeActivity_setWindowFlags(activity, AWINDOW_FLAG_KEEP_SCREEN_ON,
AWINDOW_FLAG_KEEP_SCREEN_ON);
// Hide the status bar
ANativeActivity_setWindowFlags(activity, AWINDOW_FLAG_FULLSCREEN,
AWINDOW_FLAG_FULLSCREEN);
// Hide the navigation bar
JavaVM* lJavaVM = activity->vm;
JNIEnv* lJNIEnv = activity->env;
jobject objectActivity = activity->clazz;
jclass classActivity = lJNIEnv->GetObjectClass(objectActivity);
jmethodID methodGetWindow = lJNIEnv->GetMethodID(classActivity, "getWindow", "()Landroid/view/Window;");
jobject objectWindow = lJNIEnv->CallObjectMethod(objectActivity, methodGetWindow);
jclass classWindow = lJNIEnv->FindClass("android/view/Window");
jmethodID methodGetDecorView = lJNIEnv->GetMethodID(classWindow, "getDecorView", "()Landroid/view/View;");
jobject objectDecorView = lJNIEnv->CallObjectMethod(objectWindow, methodGetDecorView);
jclass classView = lJNIEnv->FindClass("android/view/View");
jfieldID FieldSYSTEM_UI_FLAG_HIDE_NAVIGATION = lJNIEnv->GetStaticFieldID(classView, "SYSTEM_UI_FLAG_HIDE_NAVIGATION", "I");
jint SYSTEM_UI_FLAG_HIDE_NAVIGATION = lJNIEnv->GetStaticIntField(classView, FieldSYSTEM_UI_FLAG_HIDE_NAVIGATION);
jfieldID FieldSYSTEM_UI_FLAG_FULLSCREEN = lJNIEnv->GetStaticFieldID(classView, "SYSTEM_UI_FLAG_FULLSCREEN", "I");
jint SYSTEM_UI_FLAG_FULLSCREEN = lJNIEnv->GetStaticIntField(classView, FieldSYSTEM_UI_FLAG_FULLSCREEN);
jmethodID methodsetSystemUiVisibility = lJNIEnv->GetMethodID(classView, "setSystemUiVisibility", "(I)V");
lJNIEnv->CallVoidMethod(objectDecorView, methodsetSystemUiVisibility, SYSTEM_UI_FLAG_HIDE_NAVIGATION | SYSTEM_UI_FLAG_FULLSCREEN);
// Initialize the display
eglInitialize(states->display, NULL, NULL);
getScreenSizeInPixels(activity, &states->screenSize.x, &states->screenSize.y);
// Redirect error messages to logcat
sf::err().rdbuf(&states->logcat);
// Launch the main thread
sf::Thread* thread = new sf::Thread(sf::priv::main, states);
thread->launch();

View File

@ -28,6 +28,29 @@
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Android/Activity.hpp>
#include <android/log.h>
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_INFO, "sfml-error", __VA_ARGS__))
LogcatStream::LogcatStream() :
std::streambuf()
{
// Nothing to do
}
std::streambuf::int_type LogcatStream::overflow (std::streambuf::int_type c)
{
if (c == "\n"[0])
{
m_message.push_back(c);
LOGE(m_message.c_str());
m_message.clear();
}
m_message.push_back(c);
return traits_type::not_eof(c);
}
namespace sf
{

View File

@ -36,7 +36,19 @@
#include <EGL/egl.h>
#include <vector>
#include <map>
#include <string>
#include <fstream>
class SFML_SYSTEM_API LogcatStream : public std::streambuf
{
public:
LogcatStream();
std::streambuf::int_type overflow (std::streambuf::int_type c);
private:
std::string m_message;
};
namespace sf
{
@ -59,7 +71,7 @@ struct ActivityStates
Mutex mutex;
std::vector<Event> pendingEvents;
void (*forwardEvent)(const Event& event);
int (*processEvent)(int fd, int events, void* data);
std::map<int, Vector2i> touchEvents;
@ -68,10 +80,16 @@ struct ActivityStates
bool mainOver;
Vector2i screenSize;
bool initialized;
bool terminated;
bool fullscreen;
bool updated;
LogcatStream logcat;
};
SFML_SYSTEM_API ActivityStates* getActivity(ActivityStates* initializedStates=NULL, bool reset=false);

View File

@ -90,7 +90,7 @@ if(SFML_OS_WINDOWS)
list(APPEND SYSTEM_EXT_LIBS winmm)
endif()
if(SFML_OS_ANDROID)
list(APPEND SYSTEM_EXT_LIBS android)
list(APPEND SYSTEM_EXT_LIBS android log)
endif()
# define the sfml-system target

View File

@ -55,20 +55,7 @@ VideoMode VideoModeImpl::getDesktopMode()
priv::ActivityStates* states = priv::getActivity(NULL);
Lock lock(states->mutex);
// Wait for a window if there's none
while (!states->window)
{
states->mutex.unlock();
sleep(milliseconds(10));
states->mutex.lock();
}
// Get size from the window
Vector2i size;
size.x = ANativeWindow_getWidth(states->window);
size.y = ANativeWindow_getHeight(states->window);
return VideoMode(size.x, size.y);
return VideoMode(states->screenSize.x, states->screenSize.y);
}
} // namespace priv

View File

@ -44,24 +44,36 @@ namespace sf
{
namespace priv
{
WindowImplAndroid* WindowImplAndroid::singleInstance = NULL;
////////////////////////////////////////////////////////////
WindowImplAndroid::WindowImplAndroid(WindowHandle handle)
: m_size(0, 0)
, m_windowBeingCreated(false)
, m_windowBeingDestroyed(false)
{
}
////////////////////////////////////////////////////////////
WindowImplAndroid::WindowImplAndroid(VideoMode mode, const String& title, unsigned long style, const ContextSettings& settings)
: m_size(mode.width, mode.height)
, m_windowBeingCreated(false)
, m_windowBeingDestroyed(false)
{
ActivityStates* states = getActivity(NULL);
Lock lock(states->mutex);
// Replace our dummy process event function with the actual one
AInputQueue_detachLooper(states->inputQueue);
AInputQueue_attachLooper(states->inputQueue, states->looper, 1, processEvent, NULL);
if (style& Style::Fullscreen)
states->fullscreen = true;
// Register the actual process event function so it can be set automatically later by the OS
WindowImplAndroid::singleInstance = this;
states->forwardEvent = forwardEvent;
// Register process event callback
states->processEvent = processEvent;
states->initialized = true;
}
@ -84,30 +96,25 @@ WindowHandle WindowImplAndroid::getSystemHandle() const
////////////////////////////////////////////////////////////
void WindowImplAndroid::processEvents()
{
ActivityStates* states = getActivity(NULL);
Lock lock(states->mutex);
// Process incoming OS events
ALooper_pollAll(0, NULL, NULL, NULL);
while (!states->pendingEvents.empty())
ActivityStates* states = getActivity(NULL);
sf::Lock lock(states->mutex);
if (m_windowBeingCreated)
{
Event tempEvent = states->pendingEvents.back();
states->pendingEvents.pop_back();
if (tempEvent.type == Event::GainedFocus)
{
states->context->createSurface(states->window);
states->updated = true;
}
else if (tempEvent.type == Event::LostFocus)
{
states->context->destroySurface();
states->updated = true;
}
pushEvent(tempEvent);
states->context->createSurface(states->window);
m_windowBeingCreated = false;
}
if (m_windowBeingDestroyed)
{
states->context->destroySurface();
m_windowBeingDestroyed = false;
}
states->updated = true;
}
@ -129,13 +136,7 @@ void WindowImplAndroid::setPosition(const Vector2i& position)
////////////////////////////////////////////////////////////
Vector2u WindowImplAndroid::getSize() const
{
ActivityStates* states = getActivity(NULL);
Lock lock(states->mutex);
int32_t width = ANativeWindow_getWidth(states->window);
int32_t height = ANativeWindow_getHeight(states->window);
return Vector2u(static_cast<unsigned int>(width), static_cast<unsigned int>(height));
return m_size;
}
@ -180,6 +181,26 @@ void WindowImplAndroid::setKeyRepeatEnabled(bool enabled)
}
////////////////////////////////////////////////////////////
void WindowImplAndroid::forwardEvent(const Event& event)
{
ActivityStates* states = getActivity(NULL);
if (event.type == Event::GainedFocus)
{
WindowImplAndroid::singleInstance->m_size.x = ANativeWindow_getWidth(states->window);
WindowImplAndroid::singleInstance->m_size.y = ANativeWindow_getHeight(states->window);
WindowImplAndroid::singleInstance->m_windowBeingCreated = true;
}
else if (event.type == Event::LostFocus)
{
WindowImplAndroid::singleInstance->m_windowBeingDestroyed = true;
}
WindowImplAndroid::singleInstance->pushEvent(event);
}
////////////////////////////////////////////////////////////
int WindowImplAndroid::processEvent(int fd, int events, void* data)
{
@ -302,7 +323,7 @@ void WindowImplAndroid::processScrollEvent(AInputEvent* _event, ActivityStates*
event.mouseWheel.x = AMotionEvent_getX(_event, 0);
event.mouseWheel.y = AMotionEvent_getY(_event, 0);
states->pendingEvents.push_back(event);
forwardEvent(event);
// Dettach this thread from the JVM
lJavaVM->DetachCurrentThread();
@ -325,14 +346,14 @@ void WindowImplAndroid::processKeyEvent(AInputEvent* _event, ActivityStates* sta
event.key.control = false;
event.key.shift = metakey & AMETA_SHIFT_ON;
states->pendingEvents.push_back(event);
forwardEvent(event);
if (int unicode = getUnicode(_event))
{
Event event;
event.type = Event::TextEntered;
event.text.unicode = unicode;
states->pendingEvents.push_back(event);
forwardEvent(event);
}
}
@ -378,7 +399,7 @@ void WindowImplAndroid::processMotionEvent(AInputEvent* _event, ActivityStates*
states->touchEvents[id] = Vector2i(event.touch.x, event.touch.y);
}
states->pendingEvents.push_back(event);
forwardEvent(event);
}
}
@ -442,7 +463,7 @@ void WindowImplAndroid::processPointerEvent(bool isDown, AInputEvent* _event, Ac
}
}
states->pendingEvents.push_back(event);
forwardEvent(event);
}

View File

@ -154,6 +154,9 @@ public :
////////////////////////////////////////////////////////////
virtual void setKeyRepeatEnabled(bool enabled);
static void forwardEvent(const Event& event);
static WindowImplAndroid* singleInstance;
protected :
////////////////////////////////////////////////////////////
@ -200,6 +203,10 @@ private:
///
////////////////////////////////////////////////////////////
static int getUnicode(AInputEvent* event);
Vector2u m_size;
bool m_windowBeingCreated;
bool m_windowBeingDestroyed;
};
} // namespace priv

View File

@ -109,9 +109,16 @@ void Window::create(VideoMode mode, const String& title, Uint32 style, const Con
}
}
// Check validity of style
if ((style & Style::Close) || (style & Style::Resize))
style |= Style::Titlebar;
// Check validity of style according to the underlying platform
#if defined(SFML_SYSTEM_IOS) || defined(SFML_SYSTEM_ANDROID)
if (style & Style::Fullscreen)
style &= ~Style::Titlebar;
else
style |= Style::Titlebar;
#else
if ((style & Style::Close) || (style & Style::Resize))
style |= Style::Titlebar;
#endif
// Recreate the window implementation
m_impl = priv::WindowImpl::create(mode, title, style, settings);