diff --git a/.gitignore b/.gitignore index 1547eb88..9c81337e 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ libs* *.gch *.a *.exe +*.so *.spr *.dat *.kdev* diff --git a/CMakeLists.txt b/CMakeLists.txt index bbb1d517..3a66be5b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,12 +36,8 @@ set(executable_SOURCES ) if(ANDROID) - set( sdl_main - android/SDL_android_main.c - ${executable_SOURCES} ) - # add shared library for android - add_library(${PROJECT_NAME} SHARED ${framework_SOURCES} ${client_SOURCES} ${sdl_main}) + add_library(${PROJECT_NAME} SHARED ${framework_SOURCES} ${client_SOURCES} ${executable_SOURCES}) else() # add client executable add_executable(${PROJECT_NAME} ${framework_SOURCES} ${client_SOURCES} ${executable_SOURCES}) diff --git a/android/compile_android_windows.bat b/android/compile_android_windows.bat index 4297001b..24c6a63d 100644 --- a/android/compile_android_windows.bat +++ b/android/compile_android_windows.bat @@ -7,13 +7,12 @@ make cd ../ -ECHO D|xcopy /E /Y %ANDROID_NDK%\libraries\lib\libSDL2.so android\project\libs\armeabi-v7a xcopy /E /Y libs android\project\libs cd android\project -call android update project -p . --name OTClientMob --target android-16 +call android update project -p . --name OTClient --target android-16 call ant debug cd bin -adb install -r OTClientMob-debug.apk \ No newline at end of file +adb install -r OTClient-debug.apk \ No newline at end of file diff --git a/android/compile_android_windows_debug.bat b/android/compile_android_windows_debug.bat new file mode 100644 index 00000000..e1cc1099 --- /dev/null +++ b/android/compile_android_windows_debug.bat @@ -0,0 +1,21 @@ +cd .. +mkdir build_android +cd build_android + +cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=../android/android.toolchain.cmake -DANDROID_ABI=armeabi-v7a -DANDROID_NATIVE_API_LEVEL=android-16 -DANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-4.6 .. +make + +cd ../ + +ECHO D|xcopy /E /Y android\project\jni\libSDL2.so android\project\libs\armeabi-v7a +xcopy /E /Y libs\armeabi-v7a\libotclient.so android\project\jni + +cd android\project + +call android update project -p . --name OTClient --target android-16 +call ndk-build.cmd all NDK_DEBUG=1 +call ant clean +call ant debug + +cd bin +adb install -r OTClient-debug.apk \ No newline at end of file diff --git a/android/project/AndroidManifest.xml b/android/project/AndroidManifest.xml index 3fdd6922..faaf2725 100644 --- a/android/project/AndroidManifest.xml +++ b/android/project/AndroidManifest.xml @@ -1,18 +1,17 @@ - - - + - + android:configChanges="orientation|keyboardHidden" + android:screenOrientation="landscape"> @@ -24,7 +23,7 @@ - + diff --git a/android/project/jni/Android.mk b/android/project/jni/Android.mk index b2f932da..ca720ce3 100644 --- a/android/project/jni/Android.mk +++ b/android/project/jni/Android.mk @@ -3,9 +3,4 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := libotclient LOCAL_SRC_FILES := libotclient.so -include $(PREBUILT_SHARED_LIBRARY) - -include $(CLEAR_VARS) -LOCAL_MODULE := libSDL2 -LOCAL_SRC_FILES := libSDL2.so include $(PREBUILT_SHARED_LIBRARY) \ No newline at end of file diff --git a/android/project/jni/Application.mk b/android/project/jni/Application.mk index 7c63e63d..ead59a67 100644 --- a/android/project/jni/Application.mk +++ b/android/project/jni/Application.mk @@ -1,2 +1,2 @@ -APP_PLATFORM := android-16 +APP_PLATFORM := android-9 APP_ABI := armeabi-v7a diff --git a/android/project/res/values/strings.xml b/android/project/res/values/strings.xml index 39e245a8..5acc519e 100644 --- a/android/project/res/values/strings.xml +++ b/android/project/res/values/strings.xml @@ -1,4 +1,4 @@ - OTClientMob + OTClient diff --git a/android/project/src/com/otclient/mobile/FakeEditText.java b/android/project/src/com/otclient/mobile/FakeEditText.java new file mode 100644 index 00000000..bb691e0c --- /dev/null +++ b/android/project/src/com/otclient/mobile/FakeEditText.java @@ -0,0 +1,52 @@ +package com.otclient.mobile; + +import android.view.KeyEvent; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; + +public class FakeEditText extends View implements View.OnKeyListener { + InputConnection ic; + + public FakeEditText() { + super(MainActivity.getInstance()); + setFocusableInTouchMode(true); + setFocusable(true); + setOnKeyListener(this); + } + + @Override + public boolean onCheckIsTextEditor() { + return true; + } + + @Override // This handles the hardware keyboard input + public boolean onKey(View v, int keyCode, KeyEvent event) { + if (event.isPrintingKey()) { + if (event.getAction() == KeyEvent.ACTION_DOWN) { + ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1); + } + return true; + } + + if (event.getAction() == KeyEvent.ACTION_DOWN) { + onNativeKeyDown(keyCode); + return true; + } else if (event.getAction() == KeyEvent.ACTION_UP) { + onNativeKeyUp(keyCode); + return true; + } + + return false; + } + + @Override + public InputConnection onCreateInputConnection(EditorInfo outAttrs) { + ic = new InputConnectionForNative(this, true); + + return ic; + } + + public static native void onNativeKeyDown(int keyCode); + public static native void onNativeKeyUp(int keyCode); +} \ No newline at end of file diff --git a/android/project/src/com/otclient/mobile/InputConnectionForNative.java b/android/project/src/com/otclient/mobile/InputConnectionForNative.java new file mode 100644 index 00000000..507cc915 --- /dev/null +++ b/android/project/src/com/otclient/mobile/InputConnectionForNative.java @@ -0,0 +1,47 @@ +package com.otclient.mobile; + +import android.view.KeyEvent; +import android.view.View; +import android.view.inputmethod.BaseInputConnection; + +public class InputConnectionForNative extends BaseInputConnection { + + public InputConnectionForNative(View targetView, boolean fullEditor) { + super(targetView, fullEditor); + } + + @Override // This handles the keycodes from soft keyboard + public boolean sendKeyEvent(KeyEvent event) { + int keyCode = event.getKeyCode(); + if (event.getAction() == KeyEvent.ACTION_DOWN) { + if (event.isPrintingKey()) { + commitText(String.valueOf((char) event.getUnicodeChar()), 1); + } + FakeEditText.onNativeKeyDown(keyCode); + return true; + } else if (event.getAction() == KeyEvent.ACTION_UP) { + FakeEditText.onNativeKeyUp(keyCode); + return true; + } + return super.sendKeyEvent(event); + } + + @Override // Typed text + public boolean commitText(CharSequence text, int newCursorPosition) { + nativeCommitText(text.toString(), newCursorPosition); + + return super.commitText(text, newCursorPosition); + } + + @Override // Workaround to capture backspace key + public boolean deleteSurroundingText(int beforeLength, int afterLength) { + if (beforeLength == 1 && afterLength == 0) { + return super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)) + && super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL)); + } + + return super.deleteSurroundingText(beforeLength, afterLength); + } + + public static native void nativeCommitText(String text, int newCursorPosition); +} diff --git a/android/project/src/com/otclient/mobile/KeyboardSoftHandler.java b/android/project/src/com/otclient/mobile/KeyboardSoftHandler.java new file mode 100644 index 00000000..e2730563 --- /dev/null +++ b/android/project/src/com/otclient/mobile/KeyboardSoftHandler.java @@ -0,0 +1,33 @@ +package com.otclient.mobile; + +import android.content.Context; +import android.view.View; +import android.view.inputmethod.InputMethodManager; + +public class KeyboardSoftHandler{ + private View editText; + + public KeyboardSoftHandler() { + editText = new FakeEditText(); + MainActivity.getInstance() + .addViewToLayout(editText); + } + + public void showKeyboardSoft() { + editText.setVisibility(View.VISIBLE); + editText.requestFocus(); + + InputMethodManager imm = (InputMethodManager) MainActivity + .getInstance().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(editText, 0); + } + + public void hideKeyboardSoft() { + editText.setVisibility(View.GONE); + + InputMethodManager imm = (InputMethodManager) MainActivity + .getInstance().getSystemService( + Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(editText.getWindowToken(), 0); + } +} diff --git a/android/project/src/com/otclient/mobile/MainActivity.java b/android/project/src/com/otclient/mobile/MainActivity.java new file mode 100644 index 00000000..01313def --- /dev/null +++ b/android/project/src/com/otclient/mobile/MainActivity.java @@ -0,0 +1,110 @@ +package com.otclient.mobile; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; +import android.view.KeyEvent; +import android.view.View; +import android.widget.RelativeLayout; +import android.widget.RelativeLayout.LayoutParams; + +public class MainActivity extends Activity { + public static final String APP_TAG = "OTClientMob"; + + private static MainActivity instance; + private RelativeLayout layout; + + private static boolean started; + + static { + started = false; + //android.os.Debug.waitForDebugger(); + System.loadLibrary("otclient"); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + Log.v(APP_TAG, "onCreate()"); + super.onCreate(savedInstanceState); + + initialize(); + } + + private void initialize() { + instance = this; + + layout = new RelativeLayout(this); + RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams( + LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + layout.setLayoutParams(params); + + setContentView(layout); + + if( !started ) { + nativeInit(); + started = true; + } + + NativeFacadeCalls.initialize(); + } + + @Override + protected void onPause() { + Log.v(APP_TAG, "onPause()"); + super.onPause(); + nativePause(); + } + + @Override + protected void onResume() { + Log.v(APP_TAG, "onResume()"); + super.onResume(); + + if( NativeFacadeCalls.isSurfaceReady() ) + nativeResume(); + } + + @Override + protected void onDestroy() { + Log.v(APP_TAG, "onDestroy()"); + super.onDestroy(); + + if(isFinishing()) { + NativeFacadeCalls.destroy(); + destroy(); + nativeDestroy(); + } + } + + private void destroy() { + instance = null; + layout = null; + } + + @Override // Ignore certain special keys so they're handled by Android + public boolean dispatchKeyEvent(KeyEvent event) { + int keyCode = event.getKeyCode(); + if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || + keyCode == KeyEvent.KEYCODE_VOLUME_UP || + keyCode == KeyEvent.KEYCODE_CAMERA || + keyCode == 168 || /* API 11: KeyEvent.KEYCODE_ZOOM_IN */ + keyCode == 169 /* API 11: KeyEvent.KEYCODE_ZOOM_OUT */ + ) { + return false; + } + return super.dispatchKeyEvent(event); + } + + public static MainActivity getInstance() { + return instance; + } + + public void addViewToLayout(View view) { + layout.addView(view); + } + + public native void nativeInit(); + public native void nativePause(); + public native void nativeResume(); + public native void nativeDestroy(); +} diff --git a/android/project/src/com/otclient/mobile/NativeFacadeCalls.java b/android/project/src/com/otclient/mobile/NativeFacadeCalls.java new file mode 100644 index 00000000..4ecfbdd6 --- /dev/null +++ b/android/project/src/com/otclient/mobile/NativeFacadeCalls.java @@ -0,0 +1,45 @@ +package com.otclient.mobile; + +import android.os.Handler; +import android.os.Looper; +import android.view.Surface; + +public class NativeFacadeCalls { + private static NativeSurfaceView nativeSurfaceView; + private static KeyboardSoftHandler keyboardSoftHandler; + private static Handler handler; + + public static void initialize() { + handler = new Handler(Looper.getMainLooper()); + keyboardSoftHandler = new KeyboardSoftHandler(); + nativeSurfaceView = new NativeSurfaceView(); + MainActivity.getInstance().addViewToLayout(nativeSurfaceView); + } + + public static void destroy() { + handler = null; + nativeSurfaceView = null; + keyboardSoftHandler = null; + } + + public static boolean isSurfaceReady() { + return nativeSurfaceView.isSurfaceReady(); + } + + /* + * Static methods called from JNI + */ + + public static Surface getNativeSurface() { + return nativeSurfaceView.getSurface(); + } + + public static void showKeyboardSoft() { + handler.post(new Runnable() { + @Override + public void run() { + keyboardSoftHandler.showKeyboardSoft(); + } + }); + } +} diff --git a/android/project/src/com/otclient/mobile/NativeMainThread.java b/android/project/src/com/otclient/mobile/NativeMainThread.java new file mode 100644 index 00000000..1f703ceb --- /dev/null +++ b/android/project/src/com/otclient/mobile/NativeMainThread.java @@ -0,0 +1,38 @@ +package com.otclient.mobile; + +public class NativeMainThread { + private static final NativeMainThread instance; + private Thread nativeThread; + + static { + instance = new NativeMainThread(); + } + + private NativeMainThread() {} + + public void start() { + if( nativeThread == null ) { + nativeThread = new Thread( + new NativeThread(), "NativeThread" ); + nativeThread.start(); + } + } + + public static NativeMainThread getInstance() { + return instance; + } + + /* + * Native methods implemented on C++ + */ + + public native void nativeStartApp(); + + private class NativeThread implements Runnable { + + @Override + public void run() { + nativeStartApp(); + } + } +} diff --git a/android/project/src/com/otclient/mobile/NativeSurfaceView.java b/android/project/src/com/otclient/mobile/NativeSurfaceView.java new file mode 100644 index 00000000..aa70ae75 --- /dev/null +++ b/android/project/src/com/otclient/mobile/NativeSurfaceView.java @@ -0,0 +1,130 @@ +package com.otclient.mobile; + +import android.util.Log; +import android.view.GestureDetector; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.View; + +public class NativeSurfaceView extends SurfaceView implements + SurfaceHolder.Callback, View.OnTouchListener { + + private Surface surface; + private GestureDetector gestureDetector; + private int currentWidth; + private int currentHeight; + private boolean surfaceReady; + + private final int LONGPRESS_EVENT = 3; + + public NativeSurfaceView() { + super(MainActivity.getInstance()); + getHolder().addCallback(this); + setFocusable(true); + setFocusableInTouchMode(true); + requestFocus(); + setOnTouchListener(this); + + currentWidth = 0; + currentHeight = 0; + surfaceReady = false; + + gestureDetector = new GestureDetector( + new GestureDetector.SimpleOnGestureListener() { + public void onLongPress(MotionEvent event) { + onNativeTouch(LONGPRESS_EVENT, event.getX(), event.getY()); + } + }); + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + Log.d(MainActivity.APP_TAG, "surfaceCreated"); + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, + int height) { + Log.d(MainActivity.APP_TAG, "surfaceChanged"); + + surface = holder.getSurface(); + + currentWidth = width; + currentHeight = height; + surfaceReady = true; + + onNativeResize(width, height); + onNativeSurfaceChanged(); + + NativeMainThread.getInstance().start(); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + Log.d(MainActivity.APP_TAG, "surfaceDestroyed"); + surface = null; + surfaceReady = false; + + onNativeSurfaceDestroyed(); + } + + @Override + public boolean onTouch(View view, MotionEvent event) { + gestureDetector.onTouchEvent(event); + /* Ref: http://developer.android.com/training/gestures/multi.html */ + final int pointerCount = event.getPointerCount(); + int action = event.getActionMasked(); + int i = -1; + float x,y; + + switch(action) { + case MotionEvent.ACTION_MOVE: + for (i = 0; i < pointerCount; i++) { + x = event.getX(i); + y = event.getY(i); + onNativeTouch(action, x, y); + } + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_DOWN: + // Primary pointer up/down, the index is always zero + i = 0; + case MotionEvent.ACTION_POINTER_UP: + case MotionEvent.ACTION_POINTER_DOWN: + // Non primary pointer up/down + if (i == -1) { + i = event.getActionIndex(); + } + + x = event.getX(i); + y = event.getY(i); + onNativeTouch(action, x, y); + break; + + default: + break; + } + + return true; + } + + public Surface getSurface() { + return surface; + } + + public boolean isSurfaceReady() { + return surfaceReady; + } + + /* + * Native methods implemented on C++ + */ + public native void onNativeSurfaceChanged(); + public native void onNativeSurfaceDestroyed(); + public native void onNativeResize(int width, int height); + public native void onNativeTouch(int actionType, float x, float y); +} diff --git a/android/project/src/org/libsdl/app/SDLActivity.java b/android/project/src/org/libsdl/app/SDLActivity.java deleted file mode 100644 index 3946f930..00000000 --- a/android/project/src/org/libsdl/app/SDLActivity.java +++ /dev/null @@ -1,1074 +0,0 @@ -package org.libsdl.app; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -import android.app.*; -import android.content.*; -import android.view.*; -import android.view.inputmethod.BaseInputConnection; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputConnection; -import android.view.inputmethod.InputMethodManager; -import android.widget.AbsoluteLayout; -import android.os.*; -import android.util.Log; -import android.graphics.*; -import android.media.*; -import android.hardware.*; - - -/** - SDL Activity -*/ -public class SDLActivity extends Activity { - private static final String TAG = "SDL"; - - // Keep track of the paused state - public static boolean mIsPaused, mIsSurfaceReady, mHasFocus; - public static boolean mExitCalledFromJava; - - // Main components - protected static SDLActivity mSingleton; - protected static SDLSurface mSurface; - protected static View mTextEdit; - protected static ViewGroup mLayout; - protected static SDLJoystickHandler mJoystickHandler; - - // This is what SDL runs in. It invokes SDL_main(), eventually - protected static Thread mSDLThread; - - // Audio - protected static AudioTrack mAudioTrack; - - // Load the .so - static { - System.loadLibrary("SDL2"); - //System.loadLibrary("SDL2_image"); - //System.loadLibrary("SDL2_mixer"); - //System.loadLibrary("SDL2_net"); - //System.loadLibrary("SDL2_ttf"); - System.loadLibrary("otclient"); - } - - - public static void initialize() { - // The static nature of the singleton and Android quirkyness force us to initialize everything here - // Otherwise, when exiting the app and returning to it, these variables *keep* their pre exit values - mSingleton = null; - mSurface = null; - mTextEdit = null; - mLayout = null; - mJoystickHandler = null; - mSDLThread = null; - mAudioTrack = null; - mExitCalledFromJava = false; - mIsPaused = false; - mIsSurfaceReady = false; - mHasFocus = true; - } - - // Setup - @Override - protected void onCreate(Bundle savedInstanceState) { - Log.v("SDL", "onCreate():" + mSingleton); - super.onCreate(savedInstanceState); - - SDLActivity.initialize(); - // So we can call stuff from static callbacks - mSingleton = this; - - // Set up the surface - mSurface = new SDLSurface(getApplication()); - - if(Build.VERSION.SDK_INT >= 12) { - mJoystickHandler = new SDLJoystickHandler_API12(); - } - else { - mJoystickHandler = new SDLJoystickHandler(); - } - - mLayout = new AbsoluteLayout(this); - mLayout.addView(mSurface); - - setContentView(mLayout); - } - - // Events - @Override - protected void onPause() { - Log.v("SDL", "onPause()"); - super.onPause(); - SDLActivity.handlePause(); - } - - @Override - protected void onResume() { - Log.v("SDL", "onResume()"); - super.onResume(); - SDLActivity.handleResume(); - } - - - @Override - public void onWindowFocusChanged(boolean hasFocus) { - super.onWindowFocusChanged(hasFocus); - Log.v("SDL", "onWindowFocusChanged(): " + hasFocus); - - SDLActivity.mHasFocus = hasFocus; - if (hasFocus) { - SDLActivity.handleResume(); - } - } - - @Override - public void onLowMemory() { - Log.v("SDL", "onLowMemory()"); - super.onLowMemory(); - SDLActivity.nativeLowMemory(); - } - - @Override - protected void onDestroy() { - Log.v("SDL", "onDestroy()"); - // Send a quit message to the application - SDLActivity.mExitCalledFromJava = true; - SDLActivity.nativeQuit(); - - // Now wait for the SDL thread to quit - if (SDLActivity.mSDLThread != null) { - try { - SDLActivity.mSDLThread.join(); - } catch(Exception e) { - Log.v("SDL", "Problem stopping thread: " + e); - } - SDLActivity.mSDLThread = null; - - //Log.v("SDL", "Finished waiting for SDL thread"); - } - - super.onDestroy(); - // Reset everything in case the user re opens the app - SDLActivity.initialize(); - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - int keyCode = event.getKeyCode(); - // Ignore certain special keys so they're handled by Android - if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || - keyCode == KeyEvent.KEYCODE_VOLUME_UP || - keyCode == KeyEvent.KEYCODE_CAMERA || - keyCode == 168 || /* API 11: KeyEvent.KEYCODE_ZOOM_IN */ - keyCode == 169 /* API 11: KeyEvent.KEYCODE_ZOOM_OUT */ - ) { - return false; - } - return super.dispatchKeyEvent(event); - } - - /** Called by onPause or surfaceDestroyed. Even if surfaceDestroyed - * is the first to be called, mIsSurfaceReady should still be set - * to 'true' during the call to onPause (in a usual scenario). - */ - public static void handlePause() { - if (!SDLActivity.mIsPaused && SDLActivity.mIsSurfaceReady) { - SDLActivity.mIsPaused = true; - SDLActivity.nativePause(); - mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, false); - } - } - - /** Called by onResume or surfaceCreated. An actual resume should be done only when the surface is ready. - * Note: Some Android variants may send multiple surfaceChanged events, so we don't need to resume - * every time we get one of those events, only if it comes after surfaceDestroyed - */ - public static void handleResume() { - if (SDLActivity.mIsPaused && SDLActivity.mIsSurfaceReady && SDLActivity.mHasFocus) { - SDLActivity.mIsPaused = false; - SDLActivity.nativeResume(); - mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, true); - } - } - - /* The native thread has finished */ - public static void handleNativeExit() { - SDLActivity.mSDLThread = null; - mSingleton.finish(); - } - - - // Messages from the SDLMain thread - static final int COMMAND_CHANGE_TITLE = 1; - static final int COMMAND_UNUSED = 2; - static final int COMMAND_TEXTEDIT_HIDE = 3; - - protected static final int COMMAND_USER = 0x8000; - - /** - * This method is called by SDL if SDL did not handle a message itself. - * This happens if a received message contains an unsupported command. - * Method can be overwritten to handle Messages in a different class. - * @param command the command of the message. - * @param param the parameter of the message. May be null. - * @return if the message was handled in overridden method. - */ - protected boolean onUnhandledMessage(int command, Object param) { - return false; - } - - /** - * A Handler class for Messages from native SDL applications. - * It uses current Activities as target (e.g. for the title). - * static to prevent implicit references to enclosing object. - */ - protected static class SDLCommandHandler extends Handler { - @Override - public void handleMessage(Message msg) { - Context context = getContext(); - if (context == null) { - Log.e(TAG, "error handling message, getContext() returned null"); - return; - } - switch (msg.arg1) { - case COMMAND_CHANGE_TITLE: - if (context instanceof Activity) { - ((Activity) context).setTitle((String)msg.obj); - } else { - Log.e(TAG, "error handling message, getContext() returned no Activity"); - } - break; - case COMMAND_TEXTEDIT_HIDE: - if (mTextEdit != null) { - mTextEdit.setVisibility(View.GONE); - - InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0); - } - break; - - default: - if ((context instanceof SDLActivity) && !((SDLActivity) context).onUnhandledMessage(msg.arg1, msg.obj)) { - Log.e(TAG, "error handling message, command is " + msg.arg1); - } - } - } - } - - // Handler for the messages - Handler commandHandler = new SDLCommandHandler(); - - // Send a message from the SDLMain thread - boolean sendCommand(int command, Object data) { - Message msg = commandHandler.obtainMessage(); - msg.arg1 = command; - msg.obj = data; - return commandHandler.sendMessage(msg); - } - - // 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(); - public static native void onNativeResize(int x, int y, int format); - public static native int onNativePadDown(int device_id, int keycode); - public static native int onNativePadUp(int device_id, int keycode); - public static native void onNativeJoy(int device_id, int axis, - float value); - public static native void onNativeHat(int device_id, int hat_id, - int x, int y); - public static native void onNativeKeyDown(int keycode); - public static native void onNativeKeyUp(int keycode); - public static native void onNativeKeyboardFocusLost(); - public static native void onNativeTouch(int touchDevId, int pointerFingerId, - int action, float x, - float y, float p); - public static native void onNativeAccel(float x, float y, float z); - public static native void onNativeSurfaceChanged(); - public static native void onNativeSurfaceDestroyed(); - public static native void nativeFlipBuffers(); - public static native int nativeAddJoystick(int device_id, String name, - int is_accelerometer, int nbuttons, - int naxes, int nhats, int nballs); - public static native int nativeRemoveJoystick(int device_id); - - public static void flipBuffers() { - SDLActivity.nativeFlipBuffers(); - } - - public static boolean setActivityTitle(String title) { - // Called from SDLMain() thread and can't directly affect the view - return mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title); - } - - public static boolean sendMessage(int command, int param) { - return mSingleton.sendCommand(command, Integer.valueOf(param)); - } - - public static Context getContext() { - return mSingleton; - } - - /** - * @return result of getSystemService(name) but executed on UI thread. - */ - public Object getSystemServiceFromUiThread(final String name) { - final Object lock = new Object(); - final Object[] results = new Object[2]; // array for writable variables - synchronized (lock) { - runOnUiThread(new Runnable() { - @Override - public void run() { - synchronized (lock) { - results[0] = getSystemService(name); - results[1] = Boolean.TRUE; - lock.notify(); - } - } - }); - if (results[1] == null) { - try { - lock.wait(); - } catch (InterruptedException ex) { - ex.printStackTrace(); - } - } - } - return results[0]; - } - - static class ShowTextInputTask implements Runnable { - /* - * This is used to regulate the pan&scan method to have some offset from - * the bottom edge of the input region and the top edge of an input - * method (soft keyboard) - */ - static final int HEIGHT_PADDING = 15; - - public int x, y, w, h; - - public ShowTextInputTask(int x, int y, int w, int h) { - this.x = x; - this.y = y; - this.w = w; - this.h = h; - } - - @Override - public void run() { - AbsoluteLayout.LayoutParams params = new AbsoluteLayout.LayoutParams( - w, h + HEIGHT_PADDING, x, y); - - if (mTextEdit == null) { - mTextEdit = new DummyEdit(getContext()); - - mLayout.addView(mTextEdit, params); - } else { - mTextEdit.setLayoutParams(params); - } - - mTextEdit.setVisibility(View.VISIBLE); - mTextEdit.requestFocus(); - - InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.showSoftInput(mTextEdit, 0); - } - } - - public static boolean showTextInput(int x, int y, int w, int h) { - // Transfer the task to the main thread as a Runnable - return mSingleton.commandHandler.post(new ShowTextInputTask(x, y, w, h)); - } - - public static Surface getNativeSurface() { - return SDLActivity.mSurface.getNativeSurface(); - } - - // Audio - public static int audioInit(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) { - int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO; - int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT; - int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1); - - Log.v("SDL", "SDL audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer"); - - // Let the user pick a larger buffer if they really want -- but ye - // gods they probably shouldn't, the minimums are horrifyingly high - // latency already - desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize); - - if (mAudioTrack == null) { - mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, - channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM); - - // Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid - // Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java - // Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState() - - if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) { - Log.e("SDL", "Failed during initialization of Audio Track"); - mAudioTrack = null; - return -1; - } - - mAudioTrack.play(); - } - - Log.v("SDL", "SDL audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + (mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer"); - - return 0; - } - - public static void audioWriteShortBuffer(short[] buffer) { - for (int i = 0; i < buffer.length; ) { - int result = mAudioTrack.write(buffer, i, buffer.length - i); - if (result > 0) { - i += result; - } else if (result == 0) { - try { - Thread.sleep(1); - } catch(InterruptedException e) { - // Nom nom - } - } else { - Log.w("SDL", "SDL audio: error return from write(short)"); - return; - } - } - } - - public static void audioWriteByteBuffer(byte[] buffer) { - for (int i = 0; i < buffer.length; ) { - int result = mAudioTrack.write(buffer, i, buffer.length - i); - if (result > 0) { - i += result; - } else if (result == 0) { - try { - Thread.sleep(1); - } catch(InterruptedException e) { - // Nom nom - } - } else { - Log.w("SDL", "SDL audio: error return from write(byte)"); - return; - } - } - } - - public static void audioQuit() { - if (mAudioTrack != null) { - mAudioTrack.stop(); - mAudioTrack = null; - } - } - - // Input - - /** - * @return an array which may be empty but is never null. - */ - public static int[] inputGetInputDeviceIds(int sources) { - int[] ids = InputDevice.getDeviceIds(); - int[] filtered = new int[ids.length]; - int used = 0; - for (int i = 0; i < ids.length; ++i) { - InputDevice device = InputDevice.getDevice(ids[i]); - if ((device != null) && ((device.getSources() & sources) != 0)) { - filtered[used++] = device.getId(); - } - } - return Arrays.copyOf(filtered, used); - } - - // Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance - public static boolean handleJoystickMotionEvent(MotionEvent event) { - return mJoystickHandler.handleMotionEvent(event); - } - - public static void pollInputDevices() { - if (SDLActivity.mSDLThread != null) { - mJoystickHandler.pollInputDevices(); - } - } - -} - -/** - Simple nativeInit() runnable -*/ -class SDLMain implements Runnable { - @Override - public void run() { - // Runs SDL_main() - SDLActivity.nativeInit(); - - //Log.v("SDL", "SDL thread terminated"); - } -} - - -/** - SDLSurface. This is what we draw on, so we need to know when it's created - in order to do anything useful. - - Because of this, that's where we set up the SDL thread -*/ -class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, - View.OnKeyListener, View.OnTouchListener, SensorEventListener { - - // Sensors - protected static SensorManager mSensorManager; - protected static Display mDisplay; - - // Keep track of the surface size to normalize touch events - protected static float mWidth, mHeight; - - // Startup - public SDLSurface(Context context) { - super(context); - getHolder().addCallback(this); - - setFocusable(true); - setFocusableInTouchMode(true); - requestFocus(); - setOnKeyListener(this); - setOnTouchListener(this); - - mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); - mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); - - if(Build.VERSION.SDK_INT >= 12) { - setOnGenericMotionListener(new SDLGenericMotionListener_API12()); - } - - // Some arbitrary defaults to avoid a potential division by zero - mWidth = 1.0f; - mHeight = 1.0f; - } - - public Surface getNativeSurface() { - return getHolder().getSurface(); - } - - // Called when we have a valid drawing surface - @Override - public void surfaceCreated(SurfaceHolder holder) { - Log.v("SDL", "surfaceCreated()"); - holder.setType(SurfaceHolder.SURFACE_TYPE_GPU); - } - - // Called when we lose the surface - @Override - public void surfaceDestroyed(SurfaceHolder holder) { - Log.v("SDL", "surfaceDestroyed()"); - // Call this *before* setting mIsSurfaceReady to 'false' - SDLActivity.handlePause(); - SDLActivity.mIsSurfaceReady = false; - SDLActivity.onNativeSurfaceDestroyed(); - } - - // Called when the surface is resized - @Override - public void surfaceChanged(SurfaceHolder holder, - int format, int width, int height) { - Log.v("SDL", "surfaceChanged()"); - - int sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565 by default - switch (format) { - case PixelFormat.A_8: - Log.v("SDL", "pixel format A_8"); - break; - case PixelFormat.LA_88: - Log.v("SDL", "pixel format LA_88"); - break; - case PixelFormat.L_8: - Log.v("SDL", "pixel format L_8"); - break; - case PixelFormat.RGBA_4444: - Log.v("SDL", "pixel format RGBA_4444"); - sdlFormat = 0x15421002; // SDL_PIXELFORMAT_RGBA4444 - break; - case PixelFormat.RGBA_5551: - Log.v("SDL", "pixel format RGBA_5551"); - sdlFormat = 0x15441002; // SDL_PIXELFORMAT_RGBA5551 - break; - case PixelFormat.RGBA_8888: - Log.v("SDL", "pixel format RGBA_8888"); - sdlFormat = 0x16462004; // SDL_PIXELFORMAT_RGBA8888 - break; - case PixelFormat.RGBX_8888: - Log.v("SDL", "pixel format RGBX_8888"); - sdlFormat = 0x16261804; // SDL_PIXELFORMAT_RGBX8888 - break; - case PixelFormat.RGB_332: - Log.v("SDL", "pixel format RGB_332"); - sdlFormat = 0x14110801; // SDL_PIXELFORMAT_RGB332 - break; - case PixelFormat.RGB_565: - Log.v("SDL", "pixel format RGB_565"); - sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565 - break; - case PixelFormat.RGB_888: - Log.v("SDL", "pixel format RGB_888"); - // Not sure this is right, maybe SDL_PIXELFORMAT_RGB24 instead? - sdlFormat = 0x16161804; // SDL_PIXELFORMAT_RGB888 - break; - default: - Log.v("SDL", "pixel format unknown " + format); - break; - } - - mWidth = width; - mHeight = height; - SDLActivity.onNativeResize(width, height, sdlFormat); - Log.v("SDL", "Window size:" + width + "x"+height); - - // Set mIsSurfaceReady to 'true' *before* making a call to handleResume - SDLActivity.mIsSurfaceReady = true; - SDLActivity.onNativeSurfaceChanged(); - - - if (SDLActivity.mSDLThread == null) { - // This is the entry point to the C app. - // Start up the C app thread and enable sensor input for the first time - - SDLActivity.mSDLThread = new Thread(new SDLMain(), "SDLThread"); - enableSensor(Sensor.TYPE_ACCELEROMETER, true); - SDLActivity.mSDLThread.start(); - - // Set up a listener thread to catch when the native thread ends - new Thread(new Runnable(){ - @Override - public void run(){ - try { - SDLActivity.mSDLThread.join(); - } - catch(Exception e){} - finally{ - // Native thread has finished - if (! SDLActivity.mExitCalledFromJava) { - SDLActivity.handleNativeExit(); - } - } - } - }).start(); - } - } - - // unused - @Override - public void onDraw(Canvas canvas) {} - - - // Key events - @Override - public boolean onKey(View v, int keyCode, KeyEvent event) { - // Dispatch the different events depending on where they come from - // Some SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD - // So, we try to process them as DPAD or GAMEPAD events first, if that fails we try them as KEYBOARD - - if ( (event.getSource() & 0x00000401) != 0 || /* API 12: SOURCE_GAMEPAD */ - (event.getSource() & InputDevice.SOURCE_DPAD) != 0 ) { - if (event.getAction() == KeyEvent.ACTION_DOWN) { - if (SDLActivity.onNativePadDown(event.getDeviceId(), keyCode) == 0) { - return true; - } - } else if (event.getAction() == KeyEvent.ACTION_UP) { - if (SDLActivity.onNativePadUp(event.getDeviceId(), keyCode) == 0) { - return true; - } - } - } - - if( (event.getSource() & InputDevice.SOURCE_KEYBOARD) != 0) { - if (event.getAction() == KeyEvent.ACTION_DOWN) { - //Log.v("SDL", "key down: " + keyCode); - SDLActivity.onNativeKeyDown(keyCode); - return true; - } - else if (event.getAction() == KeyEvent.ACTION_UP) { - //Log.v("SDL", "key up: " + keyCode); - SDLActivity.onNativeKeyUp(keyCode); - return true; - } - } - - return false; - } - - // Touch events - @Override - public boolean onTouch(View v, MotionEvent event) { - /* Ref: http://developer.android.com/training/gestures/multi.html */ - final int touchDevId = event.getDeviceId(); - final int pointerCount = event.getPointerCount(); - int action = event.getActionMasked(); - int pointerFingerId; - int i = -1; - float x,y,p; - - switch(action) { - case MotionEvent.ACTION_MOVE: - for (i = 0; i < pointerCount; i++) { - pointerFingerId = event.getPointerId(i); - x = event.getX(i) / mWidth; - y = event.getY(i) / mHeight; - p = event.getPressure(i); - SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p); - } - break; - - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_DOWN: - // Primary pointer up/down, the index is always zero - i = 0; - case MotionEvent.ACTION_POINTER_UP: - case MotionEvent.ACTION_POINTER_DOWN: - // Non primary pointer up/down - if (i == -1) { - i = event.getActionIndex(); - } - - pointerFingerId = event.getPointerId(i); - x = event.getX(i) / mWidth; - y = event.getY(i) / mHeight; - p = event.getPressure(i); - SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p); - break; - - default: - break; - } - - return true; - } - - // Sensor events - public void enableSensor(int sensortype, boolean enabled) { - // TODO: This uses getDefaultSensor - what if we have >1 accels? - if (enabled) { - mSensorManager.registerListener(this, - mSensorManager.getDefaultSensor(sensortype), - SensorManager.SENSOR_DELAY_GAME, null); - } else { - mSensorManager.unregisterListener(this, - mSensorManager.getDefaultSensor(sensortype)); - } - } - - @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { - // TODO - } - - @Override - public void onSensorChanged(SensorEvent event) { - if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { - float x, y; - switch (mDisplay.getRotation()) { - case Surface.ROTATION_90: - x = -event.values[1]; - y = event.values[0]; - break; - case Surface.ROTATION_270: - x = event.values[1]; - y = -event.values[0]; - break; - case Surface.ROTATION_180: - x = -event.values[1]; - y = -event.values[0]; - break; - default: - x = event.values[0]; - y = event.values[1]; - break; - } - SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH, - y / SensorManager.GRAVITY_EARTH, - event.values[2] / SensorManager.GRAVITY_EARTH - 1); - } - } -} - -/* This is a fake invisible editor view that receives the input and defines the - * pan&scan region - */ -class DummyEdit extends View implements View.OnKeyListener { - InputConnection ic; - - public DummyEdit(Context context) { - super(context); - setFocusableInTouchMode(true); - setFocusable(true); - setOnKeyListener(this); - } - - @Override - public boolean onCheckIsTextEditor() { - return true; - } - - @Override - public boolean onKey(View v, int keyCode, KeyEvent event) { - - // This handles the hardware keyboard input - if (event.isPrintingKey()) { - if (event.getAction() == KeyEvent.ACTION_DOWN) { - ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1); - } - return true; - } - - if (event.getAction() == KeyEvent.ACTION_DOWN) { - SDLActivity.onNativeKeyDown(keyCode); - return true; - } else if (event.getAction() == KeyEvent.ACTION_UP) { - SDLActivity.onNativeKeyUp(keyCode); - return true; - } - - return false; - } - - // - @Override - public boolean onKeyPreIme (int keyCode, KeyEvent event) { - // As seen on StackOverflow: http://stackoverflow.com/questions/7634346/keyboard-hide-event - // FIXME: Discussion at http://bugzilla.libsdl.org/show_bug.cgi?id=1639 - // FIXME: This is not a 100% effective solution to the problem of detecting if the keyboard is showing or not - // FIXME: A more effective solution would be to change our Layout from AbsoluteLayout to Relative or Linear - // FIXME: And determine the keyboard presence doing this: http://stackoverflow.com/questions/2150078/how-to-check-visibility-of-software-keyboard-in-android - // FIXME: An even more effective way would be if Android provided this out of the box, but where would the fun be in that :) - if (event.getAction()==KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) { - if (SDLActivity.mTextEdit != null && SDLActivity.mTextEdit.getVisibility() == View.VISIBLE) { - SDLActivity.onNativeKeyboardFocusLost(); - } - } - return super.onKeyPreIme(keyCode, event); - } - - @Override - public InputConnection onCreateInputConnection(EditorInfo outAttrs) { - ic = new SDLInputConnection(this, true); - - outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI - | 33554432 /* API 11: EditorInfo.IME_FLAG_NO_FULLSCREEN */; - - return ic; - } -} - -class SDLInputConnection extends BaseInputConnection { - - public SDLInputConnection(View targetView, boolean fullEditor) { - super(targetView, fullEditor); - - } - - @Override - public boolean sendKeyEvent(KeyEvent event) { - - /* - * This handles the keycodes from soft keyboard (and IME-translated - * input from hardkeyboard) - */ - int keyCode = event.getKeyCode(); - if (event.getAction() == KeyEvent.ACTION_DOWN) { - if (event.isPrintingKey()) { - commitText(String.valueOf((char) event.getUnicodeChar()), 1); - } - SDLActivity.onNativeKeyDown(keyCode); - return true; - } else if (event.getAction() == KeyEvent.ACTION_UP) { - - SDLActivity.onNativeKeyUp(keyCode); - return true; - } - return super.sendKeyEvent(event); - } - - @Override - public boolean commitText(CharSequence text, int newCursorPosition) { - - nativeCommitText(text.toString(), newCursorPosition); - - return super.commitText(text, newCursorPosition); - } - - @Override - public boolean setComposingText(CharSequence text, int newCursorPosition) { - - nativeSetComposingText(text.toString(), newCursorPosition); - - return super.setComposingText(text, newCursorPosition); - } - - public native void nativeCommitText(String text, int newCursorPosition); - - public native void nativeSetComposingText(String text, int newCursorPosition); - - @Override - public boolean deleteSurroundingText(int beforeLength, int afterLength) { - // Workaround to capture backspace key. Ref: http://stackoverflow.com/questions/14560344/android-backspace-in-webview-baseinputconnection - if (beforeLength == 1 && afterLength == 0) { - // backspace - return super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)) - && super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL)); - } - - return super.deleteSurroundingText(beforeLength, afterLength); - } -} - -/* A null joystick handler for API level < 12 devices (the accelerometer is handled separately) */ -class SDLJoystickHandler { - - public boolean handleMotionEvent(MotionEvent event) { - return false; - } - - public void pollInputDevices() { - } -} - -/* Actual joystick functionality available for API >= 12 devices */ -class SDLJoystickHandler_API12 extends SDLJoystickHandler { - - class SDLJoystick { - public int device_id; - public String name; - public ArrayList axes; - public ArrayList hats; - } - class RangeComparator implements Comparator - { - @Override - public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) { - return arg0.getAxis() - arg1.getAxis(); - } - } - - private ArrayList mJoysticks; - - public SDLJoystickHandler_API12() { - - mJoysticks = new ArrayList(); - } - - @Override - public void pollInputDevices() { - int[] deviceIds = InputDevice.getDeviceIds(); - // It helps processing the device ids in reverse order - // For example, in the case of the XBox 360 wireless dongle, - // so the first controller seen by SDL matches what the receiver - // considers to be the first controller - - for(int i=deviceIds.length-1; i>-1; i--) { - SDLJoystick joystick = getJoystick(deviceIds[i]); - if (joystick == null) { - joystick = new SDLJoystick(); - InputDevice joystickDevice = InputDevice.getDevice(deviceIds[i]); - if( (joystickDevice.getSources() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { - joystick.device_id = deviceIds[i]; - joystick.name = joystickDevice.getName(); - joystick.axes = new ArrayList(); - joystick.hats = new ArrayList(); - - List ranges = joystickDevice.getMotionRanges(); - Collections.sort(ranges, new RangeComparator()); - for (InputDevice.MotionRange range : ranges ) { - if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 ) { - if (range.getAxis() == MotionEvent.AXIS_HAT_X || - range.getAxis() == MotionEvent.AXIS_HAT_Y) { - joystick.hats.add(range); - } - else { - joystick.axes.add(range); - } - } - } - - mJoysticks.add(joystick); - SDLActivity.nativeAddJoystick(joystick.device_id, joystick.name, 0, -1, - joystick.axes.size(), joystick.hats.size()/2, 0); - } - } - } - - /* Check removed devices */ - ArrayList removedDevices = new ArrayList(); - for(int i=0; i < mJoysticks.size(); i++) { - int device_id = mJoysticks.get(i).device_id; - int j; - for (j=0; j < deviceIds.length; j++) { - if (device_id == deviceIds[j]) break; - } - if (j == deviceIds.length) { - removedDevices.add(device_id); - } - } - - for(int i=0; i < removedDevices.size(); i++) { - int device_id = removedDevices.get(i); - SDLActivity.nativeRemoveJoystick(device_id); - for (int j=0; j < mJoysticks.size(); j++) { - if (mJoysticks.get(j).device_id == device_id) { - mJoysticks.remove(j); - break; - } - } - } - } - - protected SDLJoystick getJoystick(int device_id) { - for(int i=0; i < mJoysticks.size(); i++) { - if (mJoysticks.get(i).device_id == device_id) { - return mJoysticks.get(i); - } - } - return null; - } - - @Override - public boolean handleMotionEvent(MotionEvent event) { - if ( (event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) { - int actionPointerIndex = event.getActionIndex(); - int action = event.getActionMasked(); - switch(action) { - case MotionEvent.ACTION_MOVE: - SDLJoystick joystick = getJoystick(event.getDeviceId()); - if ( joystick != null ) { - for (int i = 0; i < joystick.axes.size(); i++) { - InputDevice.MotionRange range = joystick.axes.get(i); - /* Normalize the value to -1...1 */ - float value = ( event.getAxisValue( range.getAxis(), actionPointerIndex) - range.getMin() ) / range.getRange() * 2.0f - 1.0f; - SDLActivity.onNativeJoy(joystick.device_id, i, value ); - } - for (int i = 0; i < joystick.hats.size(); i+=2) { - int hatX = Math.round(event.getAxisValue( joystick.hats.get(i).getAxis(), actionPointerIndex ) ); - int hatY = Math.round(event.getAxisValue( joystick.hats.get(i+1).getAxis(), actionPointerIndex ) ); - SDLActivity.onNativeHat(joystick.device_id, i/2, hatX, hatY ); - } - } - break; - default: - break; - } - } - return true; - } -} - -class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener { - // Generic Motion (mouse hover, joystick...) events go here - // We only have joysticks yet - @Override - public boolean onGenericMotion(View v, MotionEvent event) { - return SDLActivity.handleJoystickMotionEvent(event); - } -} diff --git a/src/framework/CMakeLists.txt b/src/framework/CMakeLists.txt index 4154bb32..6cec88f0 100644 --- a/src/framework/CMakeLists.txt +++ b/src/framework/CMakeLists.txt @@ -132,6 +132,10 @@ set(framework_SOURCES ${framework_SOURCES} ${CMAKE_CURRENT_LIST_DIR}/platform/unixplatform.cpp ${CMAKE_CURRENT_LIST_DIR}/platform/platform.cpp ${CMAKE_CURRENT_LIST_DIR}/platform/platform.h + + # mobile + ${CMAKE_CURRENT_LIST_DIR}/platform/mobilefacade.cpp + ${CMAKE_CURRENT_LIST_DIR}/platform/mobilefacade.h ) set_source_files_properties(${CMAKE_CURRENT_LIST_DIR}/luafunctions.cpp @@ -232,7 +236,6 @@ find_package(ZLIB REQUIRED) if(ANDROID) set(framework_LIBRARIES ${framework_LIBRARIES} android log) - find_package(SDL2 REQUIRED) endif() set(framework_LIBRARIES ${framework_LIBRARIES} @@ -252,8 +255,8 @@ set(framework_INCLUDE_DIRS ${framework_INCLUDE_DIRS} ) if(ANDROID) - set(framework_LIBRARIES ${framework_LIBRARIES} ${SDL2_LIBRARY}) - set(framework_INCLUDE_DIRS ${framework_INCLUDE_DIRS} ${SDL2_INCLUDE_DIR}) + set(framework_LIBRARIES ${framework_LIBRARIES}) + set(framework_INCLUDE_DIRS ${framework_INCLUDE_DIRS}) endif() if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") @@ -459,8 +462,8 @@ if(FRAMEWORK_GRAPHICS) ${CMAKE_CURRENT_LIST_DIR}/platform/win32window.h ${CMAKE_CURRENT_LIST_DIR}/platform/x11window.cpp ${CMAKE_CURRENT_LIST_DIR}/platform/x11window.h - ${CMAKE_CURRENT_LIST_DIR}/platform/sdlwindow.cpp - ${CMAKE_CURRENT_LIST_DIR}/platform/sdlwindow.h + ${CMAKE_CURRENT_LIST_DIR}/platform/androidwindow.cpp + ${CMAKE_CURRENT_LIST_DIR}/platform/androidwindow.h # window input ${CMAKE_CURRENT_LIST_DIR}/input/mouse.cpp diff --git a/src/framework/core/application.cpp b/src/framework/core/application.cpp index 6c5244ae..feb38972 100644 --- a/src/framework/core/application.cpp +++ b/src/framework/core/application.cpp @@ -177,6 +177,8 @@ std::string Application::getOs() return "mac"; #elif __linux return "linux"; +#elif ANDROID + return "android"; #else return "unknown"; #endif diff --git a/src/framework/global.h b/src/framework/global.h index acf585c4..87dc44f5 100644 --- a/src/framework/global.h +++ b/src/framework/global.h @@ -45,7 +45,7 @@ #include "core/logger.h" #ifdef ANDROID -#include +#include "platform/mobilefacade.h" #endif #endif diff --git a/src/framework/platform/androidwindow.cpp b/src/framework/platform/androidwindow.cpp new file mode 100644 index 00000000..8c8a8e0c --- /dev/null +++ b/src/framework/platform/androidwindow.cpp @@ -0,0 +1,506 @@ +/* + * Copyright (c) 2010-2014 OTClient + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef ANDROID + +#include "androidwindow.h" + +AndroidWindow::AndroidWindow() { + m_minimumSize = Size(600, 480); + m_size = Size(600, 480); + m_window = 0; + + m_keyMap[AndroidWindow::KEY_ENTER] = Fw::KeyEnter; + m_keyMap[AndroidWindow::KEY_BACKSPACE] = Fw::KeyBackspace; +} + +AndroidWindow::~AndroidWindow() { + JNIEnv* env = getJNIEnv(); + env->DeleteGlobalRef(m_nativeFacadeCalls); + + internalDestroyGLContext(); +} + +AndroidWindow::KeyCode AndroidWindow::NativeEvent::getKeyCodeFromInt(int keyCode) { + switch (keyCode) { + case 66: + return KEY_ENTER; + case 67: + return KEY_BACKSPACE; + default: + return KEY_UNDEFINED; + } +} + +AndroidWindow::EventType AndroidWindow::NativeEvent::getEventTypeFromInt(int actionType) { + switch (actionType) { + case 0: + return TOUCH_DOWN; + case 1: + return TOUCH_UP; + case 2: + return TOUCH_MOTION; + case 3: + return TOUCH_LONGPRESS; + default: + return EVENT_UNDEFINED; + } +} + +JNIEnv* AndroidWindow::getJNIEnv() { + JNIEnv *env; + + if (m_javaVM->AttachCurrentThread(&env, NULL) < 0) { + g_logger.fatal("failed to attach current thread"); + return 0; + } + + return env; +} + +std::string AndroidWindow::getStringFromJString( jstring text ) { + JNIEnv* env = getJNIEnv(); + + const char* newChar = env->GetStringUTFChars(text,NULL); + std::string newText = newChar; + env->ReleaseStringUTFChars(text, newChar); + + return newText; +} + +void AndroidWindow::getJavaSurfaceAndSet() { + jobject nativeWindowObject; + JNIEnv* env = getJNIEnv(); + + nativeWindowObject = env->CallStaticObjectMethod(m_nativeFacadeCalls, m_midGetNativeSurface); + m_window = ANativeWindow_fromSurface(env, nativeWindowObject); + env->DeleteLocalRef(nativeWindowObject); +} + +void AndroidWindow::internalInitGL() { + internalCheckGL(); + internalChooseGL(); + internalConnectGLContext(); + internalCreateGLContext(); +} + +void AndroidWindow::internalCheckGL() { + m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if(m_eglDisplay == EGL_NO_DISPLAY) + g_logger.fatal("EGL not supported"); + + if(!eglInitialize(m_eglDisplay, NULL, NULL)) + g_logger.fatal("Unable to initialize EGL"); +} + +void AndroidWindow::internalChooseGL() { + static int attrList[] = { +#if OPENGL_ES==2 + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, +#else + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT, +#endif + EGL_RED_SIZE, 4, + EGL_GREEN_SIZE, 4, + EGL_BLUE_SIZE, 4, + EGL_ALPHA_SIZE, 4, + EGL_NONE + }; + + EGLint numConfig; + + if(!eglChooseConfig(m_eglDisplay, attrList, &m_eglConfig, 1, &numConfig)) + g_logger.fatal("Failed to choose EGL config"); + + if(numConfig != 1) + g_logger.warning("Didn't got the exact EGL config"); + + EGLint vid; + if(!eglGetConfigAttrib(m_eglDisplay, m_eglConfig, EGL_NATIVE_VISUAL_ID, &vid)) + g_logger.fatal("Unable to get visual EGL visual id"); + + ANativeWindow_setBuffersGeometry(m_window, 0, 0, vid); +} + +void AndroidWindow::internalCreateGLContext() { + EGLint attrList[] = { +#if OPENGL_ES==2 + EGL_CONTEXT_CLIENT_VERSION, 2, +#else + EGL_CONTEXT_CLIENT_VERSION, 1, +#endif + EGL_NONE + }; + + m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, attrList); + if(m_eglContext == EGL_NO_CONTEXT ) + g_logger.fatal(stdext::format("Unable to create EGL context: %s", eglGetError())); + + if (!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext)) + g_logger.fatal("Unable to connect EGL context into Android native window"); +} + +void AndroidWindow::internalDestroyGLContext() { + if (m_window == NULL) + return; + + if(m_eglDisplay) { + eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + if (m_eglSurface) { + eglDestroySurface(m_eglDisplay, m_eglSurface); + m_eglSurface = EGL_NO_SURFACE; + } + + if(m_eglContext) { + eglDestroyContext(m_eglDisplay, m_eglContext); + m_eglContext = EGL_NO_CONTEXT; + } + + eglTerminate(m_eglDisplay); + m_eglDisplay = EGL_NO_DISPLAY; + } +} + +void AndroidWindow::internalConnectGLContext() { + m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, m_window, NULL); + if(m_eglSurface == EGL_NO_SURFACE) + g_logger.fatal(stdext::format("Unable to create EGL surface: %s", eglGetError())); +} + +void AndroidWindow::terminate() { + nativePause(); +} + +void AndroidWindow::poll() { + handleNativeEvents(); + + while( !m_events.empty() ) { + m_currentEvent = m_events.front(); + + processKeyDownOrKeyUp(); + + switch( m_currentEvent.type ) { + case TEXTINPUT: + processTextInput(); + break; + case TOUCH_DOWN: + case TOUCH_LONGPRESS: + case TOUCH_UP: + processFingerdownAndFingerup(); + break; + case TOUCH_MOTION: + processFingermotion(); + } + + m_events.pop(); + } + + fireKeysPress(); +} + +void AndroidWindow::processKeyDownOrKeyUp() { + if(m_currentEvent.type == KEY_DOWN || m_currentEvent.type == KEY_UP) { + Fw::Key keyCode = Fw::KeyUnknown; + KeyCode key = m_currentEvent.keyCode; + + if(m_keyMap.find(key) != m_keyMap.end()) + keyCode = m_keyMap[key]; + + if(m_currentEvent.type == KEY_DOWN) + processKeyDown(keyCode); + else if(m_currentEvent.type == KEY_UP) + processKeyUp(keyCode); + } +} + +void AndroidWindow::processTextInput() { + std::string text = m_currentEvent.text; + KeyCode keyCode = m_currentEvent.keyCode; + + if(text.length() == 0 || keyCode == KEY_ENTER || keyCode == KEY_BACKSPACE) + return; + + if(m_onInputEvent) { + m_inputEvent.reset(Fw::KeyTextInputEvent); + m_inputEvent.keyText = text; + m_onInputEvent(m_inputEvent); + } +} + +void AndroidWindow::processFingerdownAndFingerup() { + bool isTouchdown = m_currentEvent.type == TOUCH_DOWN; + Fw::MouseButton mouseButton = (m_currentEvent.type == TOUCH_LONGPRESS) ? + Fw::MouseRightButton : Fw::MouseLeftButton; + + m_inputEvent.reset(); + m_inputEvent.type = (isTouchdown) ? Fw::MousePressInputEvent : Fw::MouseReleaseInputEvent; + m_inputEvent.mouseButton = Fw::MouseLeftButton; + m_mouseButtonStates[mouseButton] = isTouchdown; + + Point newMousePos(m_currentEvent.x, m_currentEvent.y); + m_inputEvent.mouseMoved = newMousePos - m_inputEvent.mousePos; + m_inputEvent.mousePos = newMousePos; + + if(m_onInputEvent) + m_onInputEvent(m_inputEvent); +} + +void AndroidWindow::processFingermotion() { + m_inputEvent.reset(); + m_inputEvent.type = Fw::MouseMoveInputEvent; + + Point newMousePos(m_currentEvent.x, m_currentEvent.y); + m_inputEvent.mouseMoved = newMousePos - m_inputEvent.mousePos; + m_inputEvent.mousePos = newMousePos; + + if (m_onInputEvent) + m_onInputEvent(m_inputEvent); +} + +void AndroidWindow::swapBuffers() { + eglSwapBuffers(m_eglDisplay, m_eglSurface); +} + +void AndroidWindow::setVerticalSync(bool enable) { + eglSwapInterval(m_eglDisplay, enable ? 1 : 0); +} + +std::string AndroidWindow::getClipboardText() { + // TODO + return ""; +} + +void AndroidWindow::setClipboardText(const std::string& text) { + // TODO +} + +Size AndroidWindow::getDisplaySize() { + return m_size; +} + +std::string AndroidWindow::getPlatformType() { + return "ANDROID-EGL"; +} + +void AndroidWindow::init() { + internalInitGL(); + nativeResume(); +} + +void AndroidWindow::show() {} + +void AndroidWindow::hide() {} + +void AndroidWindow::maximize() {} + +void AndroidWindow::move(const Point& pos) {} + +void AndroidWindow::resize(const Size& size) {} + +void AndroidWindow::showMouse() {} + +void AndroidWindow::hideMouse() {} + +int AndroidWindow::internalLoadMouseCursor(const ImagePtr& image, const Point& hotSpot) { + return 0; +} + +void AndroidWindow::setMouseCursor(int cursorId) {} + +void AndroidWindow::restoreMouseCursor() {} + +void AndroidWindow::setTitle(const std::string& title) {} + +void AndroidWindow::setMinimumSize(const Size& minimumSize) {} + +void AndroidWindow::setFullscreen(bool fullscreen) {} + +void AndroidWindow::setIcon(const std::string& iconFile) {} + +void AndroidWindow::initializeJNI(JNIEnv* env) { + env->GetJavaVM(&m_javaVM); + env = getJNIEnv(); + + jclass temp = m_nativeFacadeCalls = env->FindClass( + "com/otclient/mobile/NativeFacadeCalls"); + m_nativeFacadeCalls = (jclass)env->NewGlobalRef(temp); + + m_midGetNativeSurface = env->GetStaticMethodID(m_nativeFacadeCalls, + "getNativeSurface", "()Landroid/view/Surface;"); + + m_midShowKeyboardSoft = env->GetStaticMethodID(m_nativeFacadeCalls, + "showKeyboardSoft", "()V"); +} + +void AndroidWindow::nativePause() { + m_visible = false; +} + +void AndroidWindow::nativeResume() { + m_visible = true; +} + +void AndroidWindow::nativeDestroy() { + m_messages.push(APP_TERMINATE); +} + +void AndroidWindow::onNativeResize(int width, int height) { + m_size = Size(width, height); +} + +void AndroidWindow::nativeCommitText(jstring jString) { + std::string text = getStringFromJString(jString); + + m_events.push(NativeEvent(TEXTINPUT, text)); +} + +void AndroidWindow::onNativeSurfaceChanged() { + if( m_eglSurface == EGL_NO_SURFACE ) { + if( m_window ) + ANativeWindow_release(m_window); + + getJavaSurfaceAndSet(); + + m_messages.push(RECREATE_CONTEXT); + + nativeResume(); + } +} + +void AndroidWindow::onNativeSurfaceDestroyed() { + internalDestroyGLContext(); +} + +void AndroidWindow::onNativeTouch(int actionType, + float x, float y) { + EventType type = NativeEvent::getEventTypeFromInt(actionType); + + m_events.push(NativeEvent(type, x, y)); +} + +void AndroidWindow::onNativeKeyDown( int keyCode ) { + KeyCode key = NativeEvent::getKeyCodeFromInt(keyCode); + + m_events.push(NativeEvent(KEY_DOWN, key)); +} + +void AndroidWindow::onNativeKeyUp( int keyCode ) { + KeyCode key = NativeEvent::getKeyCodeFromInt(keyCode); + + m_events.push(NativeEvent(KEY_UP, key)); +} + +// these events need to be processed on native thread. +void AndroidWindow::handleNativeEvents() { + NativeMessage nativeMessage; + + while (!m_messages.empty()) { + nativeMessage = m_messages.front(); + + switch (nativeMessage) { + case RECREATE_CONTEXT: + if(!m_eglContext) + internalInitGL(); + break; + case APP_TERMINATE: + raise(SIGTERM); + break; + } + + m_messages.pop(); + } +} + +void AndroidWindow::showKeyboardSoft() { + JNIEnv* env = getJNIEnv(); + env->CallStaticVoidMethod(m_nativeFacadeCalls, m_midShowKeyboardSoft); +} + +/* + * Java JNI functions +*/ + +void Java_com_otclient_mobile_MainActivity_nativeInit( + JNIEnv* env, jobject obj) { + ((AndroidWindow&)g_window).initializeJNI(env); +} + +void Java_com_otclient_mobile_MainActivity_nativePause( + JNIEnv* env, jobject obj) { + ((AndroidWindow&)g_window).nativePause(); +} + +void Java_com_otclient_mobile_MainActivity_nativeResume( + JNIEnv* env, jobject obj) { + ((AndroidWindow&)g_window).nativeResume(); +} + +void Java_com_otclient_mobile_MainActivity_nativeDestroy( + JNIEnv* env, jobject obj) { + ((AndroidWindow&)g_window).nativeDestroy(); +} + +void Java_com_otclient_mobile_NativeSurfaceView_onNativeResize( + JNIEnv*, jobject, jint width, jint height) { + ((AndroidWindow&)g_window).onNativeResize(width, height); +} + +void Java_com_otclient_mobile_NativeSurfaceView_onNativeSurfaceChanged( + JNIEnv* env, jobject obj) { + ((AndroidWindow&) g_window).onNativeSurfaceChanged(); +} + +void Java_com_otclient_mobile_NativeSurfaceView_onNativeSurfaceDestroyed( + JNIEnv* env, jobject obj) { + ((AndroidWindow&)g_window).onNativeSurfaceDestroyed(); +} + +void Java_com_otclient_mobile_NativeSurfaceView_onNativeTouch( + JNIEnv* env, jobject obj, jint actionType, jfloat x, jfloat y) { + ((AndroidWindow&) g_window).onNativeTouch(actionType, x, y); +} + +void Java_com_otclient_mobile_InputConnectionForNative_nativeCommitText( + JNIEnv* env, jobject obj, jstring text ) { + ((AndroidWindow&) g_window).nativeCommitText(text); +} + +void Java_com_otclient_mobile_FakeEditText_onNativeKeyDown( + JNIEnv* env, jobject obj, jint keyCode ) { + ((AndroidWindow&) g_window).onNativeKeyDown(keyCode); +} + +void Java_com_otclient_mobile_FakeEditText_onNativeKeyUp( + JNIEnv* env, jobject obj, jint keyCode ) { + ((AndroidWindow&) g_window).onNativeKeyUp(keyCode); +} + +void Java_com_otclient_mobile_NativeMainThread_nativeStartApp( + JNIEnv*, jobject ) { + char *argv[2]; + argv[0] = strdup("OTClient"); + argv[1] = NULL; + main(1, argv); +} + +#endif // ANDROID diff --git a/src/framework/platform/androidwindow.h b/src/framework/platform/androidwindow.h new file mode 100644 index 00000000..3bc003fa --- /dev/null +++ b/src/framework/platform/androidwindow.h @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2010-2014 OTClient + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef ANDROIDWINDOW_H +#define ANDROIDWINDOW_H + +#include "platformwindow.h" +#include +#include +#include +#include +#include + +class AndroidWindow : public PlatformWindow +{ + enum KeyCode { + KEY_UNDEFINED, + KEY_BACKSPACE = 66, + KEY_ENTER = 67 + }; + + enum EventType { + TOUCH_DOWN, + TOUCH_UP, + TOUCH_MOTION, + TOUCH_LONGPRESS, + KEY_DOWN, + KEY_UP, + TEXTINPUT, + EVENT_UNDEFINED + }; + + enum NativeMessage { + RECREATE_CONTEXT, + APP_TERMINATE + }; + + struct NativeEvent { + NativeEvent() {} + + NativeEvent(EventType type, float x, float y) : + type(type), text(""), keyCode(KEY_UNDEFINED), x(x), y(y) {} + + NativeEvent(EventType type, std::string text) : + type(type), text(text), keyCode(KEY_UNDEFINED), x(0), y(0) {} + + NativeEvent(EventType type, KeyCode keyCode) : + type(type), text(""), keyCode(keyCode), x(0), y(0) {} + + static KeyCode getKeyCodeFromInt(int); + static EventType getEventTypeFromInt(int); + + EventType type; + std::string text; + KeyCode keyCode; + float x; + float y; + }; + + void internalInitGL(); + void internalCheckGL(); + void internalChooseGL(); + void internalCreateGLContext(); + void internalDestroyGLContext(); + void internalConnectGLContext(); + + void processKeyDownOrKeyUp(); + void processTextInput(); + void processFingerdownAndFingerup(); + void processFingermotion(); + + void handleNativeEvents(); + + JNIEnv* getJNIEnv(); + std::string getStringFromJString( jstring ); + + // JNI Java calls + void getJavaSurfaceAndSet(); +public: + AndroidWindow(); + ~AndroidWindow(); + + void init(); + void terminate(); + + void move(const Point& pos); + void resize(const Size& size); + void show(); + void hide(); + void maximize(); + void poll(); + void swapBuffers(); + void showMouse(); + void hideMouse(); + + void setMouseCursor(int cursorId); + void restoreMouseCursor(); + + void setTitle(const std::string& title); + void setMinimumSize(const Size& minimumSize); + void setFullscreen(bool fullscreen); + void setVerticalSync(bool enable); + void setIcon(const std::string& iconFile); + void setClipboardText(const std::string& text); + + Size getDisplaySize(); + std::string getClipboardText(); + std::string getPlatformType(); + + void initializeJNI(JNIEnv*); + void nativePause(); + void nativeResume(); + void nativeDestroy(); + void onNativeResize(int, int); + void onNativeSurfaceChanged(); + void onNativeSurfaceDestroyed(); + void onNativeTouch(int actionType, float x, float y); + void nativeCommitText(jstring); + void onNativeKeyDown(int); + void onNativeKeyUp(int); + void showKeyboardSoft(); +protected: + int internalLoadMouseCursor(const ImagePtr& image, const Point& hotSpot); +private: + JavaVM* m_javaVM; + ANativeWindow* m_window; + jclass m_nativeFacadeCalls; + jmethodID m_midGetNativeSurface; + jmethodID m_midShowKeyboardSoft; + + EGLConfig m_eglConfig; + EGLContext m_eglContext; + EGLDisplay m_eglDisplay; + EGLSurface m_eglSurface; + + std::queue m_events; + std::queue m_messages; + NativeEvent m_currentEvent; +}; + +extern "C" { + +void Java_com_otclient_mobile_NativeMainThread_nativeStartApp( + JNIEnv*, jobject); + +void Java_com_otclient_mobile_MainActivity_nativeInit( + JNIEnv*, jobject); + +void Java_com_otclient_mobile_MainActivity_nativeResume( + JNIEnv*, jobject); + +void Java_com_otclient_mobile_MainActivity_nativePause( + JNIEnv*, jobject); + +void Java_com_otclient_mobile_MainActivity_nativeDestroy( + JNIEnv*, jobject); + +void Java_com_otclient_mobile_NativeSurfaceView_onNativeResize( + JNIEnv*, jobject, jint, jint); + +void Java_com_otclient_mobile_NativeSurfaceView_onNativeSurfaceChanged( + JNIEnv*, jobject); + +void Java_com_otclient_mobile_NativeSurfaceView_onNativeSurfaceDestroyed( + JNIEnv*, jobject); + +void Java_com_otclient_mobile_NativeSurfaceView_onNativeTouch( + JNIEnv* env, jobject obj, jint actionType, jfloat x, jfloat y); + +void Java_com_otclient_mobile_InputConnectionForNative_nativeCommitText( + JNIEnv*, jobject, jstring ); + +void Java_com_otclient_mobile_FakeEditText_onNativeKeyDown( + JNIEnv*, jobject, jint ); + +void Java_com_otclient_mobile_FakeEditText_onNativeKeyUp( + JNIEnv*, jobject, jint ); + +int main(int, char* argv[]); +} + +#endif diff --git a/src/framework/platform/mobilefacade.cpp b/src/framework/platform/mobilefacade.cpp new file mode 100644 index 00000000..108ecb26 --- /dev/null +++ b/src/framework/platform/mobilefacade.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2010-2014 OTClient + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "mobilefacade.h" +#include "androidwindow.h" + +MobileFacade g_mobileFacade; + +void MobileFacade::showKeyboardSoft() { + ((AndroidWindow&) g_window).showKeyboardSoft(); +} diff --git a/src/framework/platform/mobilefacade.h b/src/framework/platform/mobilefacade.h new file mode 100644 index 00000000..5dd48451 --- /dev/null +++ b/src/framework/platform/mobilefacade.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2010-2014 OTClient + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MOBILEFACADE_H +#define MOBILEFACADE_H + +class MobileFacade { +public: + void showKeyboardSoft(); +}; + +extern MobileFacade g_mobileFacade; + +#endif diff --git a/src/framework/platform/platformwindow.cpp b/src/framework/platform/platformwindow.cpp index 6284b558..614d4a9c 100644 --- a/src/framework/platform/platformwindow.cpp +++ b/src/framework/platform/platformwindow.cpp @@ -26,8 +26,8 @@ #include "win32window.h" WIN32Window window; #elif defined ANDROID -#include "sdlwindow.h" -SDLWindow window; +#include "androidwindow.h" +AndroidWindow window; #else #include "x11window.h" #include diff --git a/src/framework/platform/sdlwindow.cpp b/src/framework/platform/sdlwindow.cpp deleted file mode 100644 index 166fb8a3..00000000 --- a/src/framework/platform/sdlwindow.cpp +++ /dev/null @@ -1,383 +0,0 @@ -/* - * Copyright (c) 2010-2014 OTClient - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifdef ANDROID - -#include "sdlwindow.h" - -SDLWindow::SDLWindow() { - m_minimumSize = Size(600,480); - m_size = Size(600,480); - m_window = 0; - - //m_keyMap[SDLK_ESCAPE] = Fw::KeyEscape; - //m_keyMap[SDLK_TAB] = Fw::KeyTab; - m_keyMap[SDLK_RETURN] = Fw::KeyEnter; - m_keyMap[SDLK_BACKSPACE] = Fw::KeyBackspace; - m_keyMap[SDLK_SPACE] = Fw::KeySpace; - - /* - m_keyMap[SDLK_PAGEUP] = Fw::KeyPageUp; - m_keyMap[SDLK_PAGEDOWN] = Fw::KeyPageDown; - m_keyMap[SDLK_HOME] = Fw::KeyHome; - m_keyMap[SDLK_END] = Fw::KeyEnd; - m_keyMap[SDLK_INSERT] = Fw::KeyInsert; - m_keyMap[SDLK_DELETE] = Fw::KeyDelete; - - m_keyMap[SDLK_UP] = Fw::KeyUp; - m_keyMap[SDLK_DOWN] = Fw::KeyDown; - m_keyMap[SDLK_LEFT] = Fw::KeyLeft; - m_keyMap[SDLK_RIGHT] = Fw::KeyRight; - - m_keyMap[SDLK_NUMLOCKCLEAR] = Fw::KeyNumLock; - m_keyMap[SDLK_SCROLLLOCK] = Fw::KeyScrollLock; - m_keyMap[SDLK_CAPSLOCK] = Fw::KeyCapsLock; - m_keyMap[SDLK_PRINTSCREEN] = Fw::KeyPrintScreen; - m_keyMap[SDLK_PAUSE] = Fw::KeyPause; - - m_keyMap[SDLK_LCTRL] = Fw::KeyCtrl; - m_keyMap[SDLK_RCTRL] = Fw::KeyCtrl; - m_keyMap[SDLK_LSHIFT] = Fw::KeyShift; - m_keyMap[SDLK_RSHIFT] = Fw::KeyShift; - m_keyMap[SDLK_LALT] = Fw::KeyAlt; - m_keyMap[SDLK_RALT] = Fw::KeyAlt; - m_keyMap[SDLK_LGUI] = Fw::KeyMeta; - m_keyMap[SDLK_RGUI] = Fw::KeyMeta; - */ - - // ascii characters - m_keyMap[SDLK_EXCLAIM] = Fw::KeyExclamation; - m_keyMap[SDLK_QUOTEDBL] = Fw::KeyQuote; - m_keyMap[SDLK_HASH] = Fw::KeyNumberSign; - m_keyMap[SDLK_DOLLAR] = Fw::KeyDollar; - m_keyMap[SDLK_PERCENT] = Fw::KeyPercent; - m_keyMap[SDLK_AMPERSAND] = Fw::KeyAmpersand; - m_keyMap[SDLK_QUOTE] = Fw::KeyApostrophe; - m_keyMap[SDLK_LEFTPAREN] = Fw::KeyLeftParen; - m_keyMap[SDLK_RIGHTPAREN] = Fw::KeyRightParen; - m_keyMap[SDLK_ASTERISK] = Fw::KeyAsterisk; - m_keyMap[SDLK_PLUS] = Fw::KeyPlus; - m_keyMap[SDLK_COMMA] = Fw::KeyComma; - m_keyMap[SDLK_MINUS] = Fw::KeyMinus; - m_keyMap[SDLK_PERIOD] = Fw::KeyPeriod; - m_keyMap[SDLK_SLASH] = Fw::KeySlash; - - m_keyMap[SDLK_0] = Fw::Key0; - m_keyMap[SDLK_1] = Fw::Key1; - m_keyMap[SDLK_2] = Fw::Key2; - m_keyMap[SDLK_3] = Fw::Key3; - m_keyMap[SDLK_4] = Fw::Key4; - m_keyMap[SDLK_5] = Fw::Key5; - m_keyMap[SDLK_6] = Fw::Key6; - m_keyMap[SDLK_7] = Fw::Key7; - m_keyMap[SDLK_8] = Fw::Key8; - m_keyMap[SDLK_9] = Fw::Key9; - - m_keyMap[SDLK_COLON] = Fw::KeyColon; - m_keyMap[SDLK_SEMICOLON] = Fw::KeySemicolon; - m_keyMap[SDLK_LESS] = Fw::KeyLess; - m_keyMap[SDLK_EQUALS] = Fw::KeyEqual; - m_keyMap[SDLK_GREATER] = Fw::KeyGreater; - m_keyMap[SDLK_QUESTION] = Fw::KeyQuestion; - m_keyMap[SDLK_AT] = Fw::KeyAtSign; - - m_keyMap[SDLK_a] = Fw::KeyA; - m_keyMap[SDLK_b] = Fw::KeyB; - m_keyMap[SDLK_c] = Fw::KeyC; - m_keyMap[SDLK_d] = Fw::KeyD; - m_keyMap[SDLK_e] = Fw::KeyE; - m_keyMap[SDLK_f] = Fw::KeyF; - m_keyMap[SDLK_g] = Fw::KeyG; - m_keyMap[SDLK_h] = Fw::KeyH; - m_keyMap[SDLK_i] = Fw::KeyI; - m_keyMap[SDLK_j] = Fw::KeyJ; - m_keyMap[SDLK_k] = Fw::KeyK; - m_keyMap[SDLK_l] = Fw::KeyL; - m_keyMap[SDLK_m] = Fw::KeyM; - m_keyMap[SDLK_n] = Fw::KeyN; - m_keyMap[SDLK_o] = Fw::KeyO; - m_keyMap[SDLK_p] = Fw::KeyP; - m_keyMap[SDLK_q] = Fw::KeyQ; - m_keyMap[SDLK_r] = Fw::KeyR; - m_keyMap[SDLK_s] = Fw::KeyS; - m_keyMap[SDLK_t] = Fw::KeyT; - m_keyMap[SDLK_u] = Fw::KeyU; - m_keyMap[SDLK_v] = Fw::KeyV; - m_keyMap[SDLK_w] = Fw::KeyW; - m_keyMap[SDLK_x] = Fw::KeyX; - m_keyMap[SDLK_y] = Fw::KeyY; - m_keyMap[SDLK_z] = Fw::KeyZ; - - m_keyMap[SDLK_LGUI] = Fw::KeyLeftBracket; - m_keyMap[SDLK_BACKSLASH] = Fw::KeyBackslash; - m_keyMap[SDLK_RIGHTBRACKET] = Fw::KeyRightBracket; - m_keyMap[SDLK_CARET] = Fw::KeyCaret; - m_keyMap[SDLK_UNDERSCORE] = Fw::KeyUnderscore; - m_keyMap[SDLK_BACKQUOTE] = Fw::KeyGrave; - - // keypad - /* - m_keyMap[SDLK_KP_PLUS] = Fw::KeyPlus; - m_keyMap[SDLK_KP_MINUS] = Fw::KeyMinus; - m_keyMap[SDLK_KP_DECIMAL] = Fw::KeyPeriod; - m_keyMap[SDLK_KP_DIVIDE] = Fw::KeySlash; - m_keyMap[SDLK_KP_MULTIPLY] = Fw::KeyAsterisk; - - m_keyMap[VK_OEM_1] = Fw::KeySemicolon; - m_keyMap[VK_OEM_2] = Fw::KeySlash; - m_keyMap[VK_OEM_3] = Fw::KeyGrave; - m_keyMap[VK_OEM_4] = Fw::KeyLeftBracket; - m_keyMap[VK_OEM_5] = Fw::KeyBackslash; - m_keyMap[VK_OEM_6] = Fw::KeyRightBracket; - m_keyMap[VK_OEM_7] = Fw::KeyApostrophe; - m_keyMap[VK_OEM_MINUS] = Fw::KeyMinus; - m_keyMap[VK_OEM_PLUS] = Fw::KeyEqual; - m_keyMap[SDLK_KP_COMMA] = Fw::KeyComma; - m_keyMap[VK_OEM_PERIOD] = Fw::KeyPeriod; - - m_keyMap[SDLK_F1] = Fw::KeyF1; - m_keyMap[SDLK_F2] = Fw::KeyF2; - m_keyMap[SDLK_F3] = Fw::KeyF3; - m_keyMap[SDLK_F4] = Fw::KeyF4; - m_keyMap[SDLK_F5] = Fw::KeyF5; - m_keyMap[SDLK_F6] = Fw::KeyF6; - m_keyMap[SDLK_F7] = Fw::KeyF7; - m_keyMap[SDLK_F8] = Fw::KeyF8; - m_keyMap[SDLK_F9] = Fw::KeyF9; - m_keyMap[SDLK_F10] = Fw::KeyF10; - m_keyMap[SDLK_F11] = Fw::KeyF11; - m_keyMap[SDLK_F12] = Fw::KeyF12; - */ -} - -void SDLWindow::init() { - initializeSDL(); -} - -void SDLWindow::initializeSDL() { - if(SDL_Init(SDL_INIT_VIDEO) != 0 ) { - g_logger.fatal( stdext::format("Unable to initialize SDL: %s", SDL_GetError()) ); - terminate(); - } - - setSDLAttributes(); - - SDL_GetDisplayMode(0, 0, &m_mode); - int width = m_mode.w; - int height = m_mode.h; - - m_window = SDL_CreateWindow( NULL, 0, 0, width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_RESIZABLE ); - - m_size = Size( width, height ); - - if( m_window == 0 ) { - g_logger.fatal("Failed to create window"); - terminate(); - } - - m_visible = true; - - SDL_GL_CreateContext(m_window); - //SDL_StartTextInput(); -} - -void SDLWindow::setSDLAttributes() { - SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE); - - SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 4); - SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 4); - SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 4); - SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 4); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, OPENGL_ES); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0); -} - -void SDLWindow::terminate() { - SDL_Quit(); - m_visible = false; -} - -void SDLWindow::poll() { - while(SDL_PollEvent(&m_event)) { - processKeydownOrKeyrelease(); - - switch(m_event.type) { - case SDL_TEXTINPUT: - processTextInput(); - break; - case SDL_FINGERDOWN: - case SDL_FINGERUP: - processFingerdownAndFingerup(); - break; - case SDL_FINGERMOTION: - processFingermotion(); - break; - } - - //if(m_inputEvent.type != Fw::NoInputEvent && m_onInputEvent) - // m_onInputEvent(m_inputEvent); - } - - fireKeysPress(); -} - -void SDLWindow::processKeydownOrKeyrelease() { - if(m_event.key.state == SDL_PRESSED || m_event.key.state == SDL_RELEASED) { - Fw::Key keyCode = Fw::KeyUnknown; - SDL_Keycode keysym = m_event.key.keysym.sym; - - if(m_keyMap.find(keysym) != m_keyMap.end()) - keyCode = m_keyMap[keysym]; - - if(m_event.type == SDL_PRESSED) - processKeyDown(keyCode); - else if(m_event.type == SDL_RELEASED) - processKeyUp(keyCode); - } -} - -void SDLWindow::processTextInput() { - std::string text = m_event.text.text; - SDL_Keycode keysym = m_event.key.keysym.sym; - - if(text.length() == 0 || keysym == SDLK_BACKSPACE || keysym == SDLK_RETURN || keysym == SDLK_AC_BACK || keysym == SDLK_DELETE) - return; - - if(m_onInputEvent) { - m_inputEvent.reset(Fw::KeyTextInputEvent); - m_inputEvent.keyText = text; - m_onInputEvent(m_inputEvent); - } -} - -void SDLWindow::processFingerdownAndFingerup() { - bool isFinderdown = m_event.type == SDL_FINGERDOWN; - - m_inputEvent.reset(); - m_inputEvent.type = isFinderdown ? Fw::MousePressInputEvent : Fw::MouseReleaseInputEvent; - m_inputEvent.mouseButton = Fw::MouseLeftButton; - m_mouseButtonStates[Fw::MouseLeftButton] = isFinderdown; - - Point newMousePos(m_event.tfinger.x * m_mode.w, m_event.tfinger.y * m_mode.h); - m_inputEvent.mouseMoved = newMousePos - m_inputEvent.mousePos; - m_inputEvent.mousePos = newMousePos; - - if(m_onInputEvent) - m_onInputEvent(m_inputEvent); -} - -void SDLWindow::processFingermotion() { - m_inputEvent.reset(); - m_inputEvent.type = Fw::MouseMoveInputEvent; - Point newMousePos(m_event.tfinger.x * m_mode.w, m_event.tfinger.y * m_mode.h); - m_inputEvent.mouseMoved = newMousePos - m_inputEvent.mousePos; - m_inputEvent.mousePos = newMousePos; - if (m_onInputEvent) - m_onInputEvent(m_inputEvent); -} - -void SDLWindow::swapBuffers() { - SDL_GL_SwapWindow(m_window); -} - -void SDLWindow::setVerticalSync(bool enable) { - SDL_GL_SetSwapInterval(enable); -} - -std::string SDLWindow::getClipboardText() { - return SDL_GetClipboardText(); -} - -void SDLWindow::setClipboardText(const std::string& text) { - SDL_SetClipboardText(text.c_str()); -} - -Size SDLWindow::getDisplaySize() { - Size size(m_mode.w, m_mode.h); - return size; -} - -std::string SDLWindow::getPlatformType() { - return "MOBILE_SDL2"; -} - -void SDLWindow::show() { - // mobile devices doesn't need to show activity, it's open automatically -} - -void SDLWindow::hide() { - // SDL make this for us -} - -void SDLWindow::maximize() { - // mobile devices doesn't has window -} - -void SDLWindow::move(const Point& pos) { - // mobile devices doesn't has window -} - -void SDLWindow::resize(const Size& size) { - // android doesn't resize window -} - -void SDLWindow::showMouse() { - // mobile devices doesn't has mouse -} - -void SDLWindow::hideMouse() { - // mobile devices doesn't has mouse -} - -int SDLWindow::internalLoadMouseCursor(const ImagePtr& image, const Point& hotSpot) { - // mobile devices doesn't has mouse - return 0; -} - -void SDLWindow::setMouseCursor(int cursorId) { - // mobile devices doesn't has mouse -} - -void SDLWindow::restoreMouseCursor() { - // mobile devices doesn't has mouse -} - -void SDLWindow::setTitle(const std::string& title) { - // mobile devices doesn't need to set title, the app make it -} - -void SDLWindow::setMinimumSize(const Size& minimumSize) { - // mobile devices doesn't has window -} - -void SDLWindow::setFullscreen(bool fullscreen) { - // mobile devices doesn't has window -} - -void SDLWindow::setIcon(const std::string& iconFile) { - // mobile devices doesn't has icon -} - -#endif // ANDROID diff --git a/src/framework/platform/sdlwindow.h b/src/framework/platform/sdlwindow.h deleted file mode 100644 index 78535977..00000000 --- a/src/framework/platform/sdlwindow.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2010-2014 OTClient - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef SDL_PLATFORM_H -#define SDL_PLATFORM_H - -#include "platformwindow.h" -#include -#include - -class SDLWindow : public PlatformWindow -{ -public: - SDLWindow(); - - void init(); - void terminate(); - - void move(const Point& pos); - void resize(const Size& size); - void show(); - void hide(); - void maximize(); - void poll(); - void swapBuffers(); - void showMouse(); - void hideMouse(); - - void setMouseCursor(int cursorId); - void restoreMouseCursor(); - - void setTitle(const std::string& title); - void setMinimumSize(const Size& minimumSize); - void setFullscreen(bool fullscreen); - void setVerticalSync(bool enable); - void setIcon(const std::string& iconFile); - void setClipboardText(const std::string& text); - - Size getDisplaySize(); - std::string getClipboardText(); - std::string getPlatformType(); - - void initializeSDL(); - void setSDLAttributes(); - void processKeydownOrKeyrelease(); - void processTextInput(); - void processFingerdownAndFingerup(); - void processFingermotion(); - -protected: - int internalLoadMouseCursor(const ImagePtr& image, const Point& hotSpot); -private: - SDL_Window* m_window; - SDL_DisplayMode m_mode; - SDL_Event m_event; -}; - -#endif diff --git a/src/framework/platform/unixplatform.cpp b/src/framework/platform/unixplatform.cpp index 650a54eb..b154961e 100644 --- a/src/framework/platform/unixplatform.cpp +++ b/src/framework/platform/unixplatform.cpp @@ -206,7 +206,12 @@ std::string Platform::traceback(const std::string& where, int level, int maxDept return ss.str(); } #else -std::string Platform::traceback(const std::string& where, int level, int maxDepth){ return " ANDROID TRACEBACK NEED TO DO"; } +std::string Platform::traceback(const std::string& where, int level, int maxDepth){ + std::stringstream ss; + ss << "\nat:"; + ss << "\n\t[C++]: " << where; + return ss.str(); +} #endif #endif diff --git a/src/framework/ui/uitextedit.cpp b/src/framework/ui/uitextedit.cpp index c484ac11..ab48f827 100644 --- a/src/framework/ui/uitextedit.cpp +++ b/src/framework/ui/uitextedit.cpp @@ -659,9 +659,8 @@ void UITextEdit::onFocusChange(bool focused, Fw::FocusReason reason) else blinkCursor(); update(true); - #ifdef ANDROID - SDL_StartTextInput(); + g_mobileFacade.showKeyboardSoft(); #endif } else if(m_selectable) clearSelection(); diff --git a/vc12/otclient.vcxproj b/vc12/otclient.vcxproj index 7012335d..1b0be0b6 100644 --- a/vc12/otclient.vcxproj +++ b/vc12/otclient.vcxproj @@ -285,9 +285,9 @@ + - @@ -444,10 +444,10 @@ + - diff --git a/vc12/otclient.vcxproj.filters b/vc12/otclient.vcxproj.filters index b4019aab..32f18428 100644 --- a/vc12/otclient.vcxproj.filters +++ b/vc12/otclient.vcxproj.filters @@ -19,9 +19,6 @@ {5c843160-5a3a-4841-b64f-a1be3d5c2df6} - - {9c343067-7e4c-497d-9423-f580732fd03c} - {be8876e0-a2cf-45f5-ac85-f822d0c85be4} @@ -103,6 +100,9 @@ {8b96ee09-99f1-4c70-90c1-d99505de6264} + + {9c343067-7e4c-497d-9423-f580732fd03c} + @@ -531,7 +531,7 @@ Source Files\client - + Source Files\framework\platform @@ -1058,7 +1058,7 @@ Header Files\client - + Header Files\framework\platform