@Override protected void onDestroy() { Log.v("SDL", "onDestroy()"); if (SDLActivity.mBrokenLibraries) { super.onDestroy(); // Reset everything in case the user re opens the app SDLActivity.initialize(); return; } // 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(); }
// 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() & InputDevice.SOURCE_GAMEPAD) != 0 || (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) { final int touchDevId = event.getDeviceId(); final int pointerCount = event.getPointerCount(); // touchId, pointerId, action, x, y, pressure int actionPointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT; /* API 8: event.getActionIndex(); */ int pointerFingerId = event.getPointerId(actionPointerIndex); int action = (event.getAction() & MotionEvent.ACTION_MASK); /* API 8: event.getActionMasked(); */ float x = event.getX(actionPointerIndex) / mWidth; float y = event.getY(actionPointerIndex) / mHeight; float p = event.getPressure(actionPointerIndex); if (action == MotionEvent.ACTION_MOVE && pointerCount > 1) { // TODO send motion to every pointer if its position has // changed since prev event. for (int 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); } } else { SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p); } return true; }
// 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(); }
@Override public void run() { // Runs SDL_main() SDLActivity.nativeInit(((Activity) SDLActivity.getContext()).getFilesDir().getAbsolutePath()); // Log.v("SDL", "SDL thread terminated"); // Urho3D: finish activity when SDL_main returns SDLActivity.finishActivity(); }
// 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); SDLActivity.startApp(); }
@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<InputDevice.MotionRange>(); for (InputDevice.MotionRange range : joystickDevice.getMotionRanges()) { if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { joystick.axes.add(range); } } mJoysticks.add(joystick); SDLActivity.nativeAddJoystick( joystick.device_id, joystick.name, 0, -1, joystick.axes.size(), 0, 0); } } } /* Check removed devices */ ArrayList<Integer> removedDevices = new ArrayList<Integer>(); 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; } } } }
// Key events public boolean onKey(View v, int keyCode, KeyEvent event) { 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; }
@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); } } break; default: break; } } return true; }
@Override public void run() { // Runs SDL_main() SDLActivity.nativeInit(SDLActivity.mSingleton.getArguments()); // Log.v("SDL", "SDL thread terminated"); }
/** * 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); } }
@Override public void run() { // Runs SDL_main() SDLActivity.nativeInit(); // Log.v("SDL", "SDL thread terminated"); }
/** * 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.handleResume(); } }
@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); } }
// EGL functions public static boolean initEGL(int majorVersion, int minorVersion, int[] attribs) { try { if (SDLActivity.mEGLDisplay == null) { Log.v("SDL", "Starting up OpenGL ES " + majorVersion + "." + minorVersion); EGL10 egl = (EGL10) EGLContext.getEGL(); EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); int[] version = new int[2]; egl.eglInitialize(dpy, version); EGLConfig[] configs = new EGLConfig[1]; int[] num_config = new int[1]; if (!egl.eglChooseConfig(dpy, attribs, configs, 1, num_config) || num_config[0] == 0) { Log.e("SDL", "No EGL config available"); return false; } EGLConfig config = configs[0]; SDLActivity.mEGLDisplay = dpy; SDLActivity.mEGLConfig = config; SDLActivity.mGLMajor = majorVersion; SDLActivity.mGLMinor = minorVersion; } return SDLActivity.createEGLSurface(); } catch (Exception e) { Log.v("SDL", e + ""); for (StackTraceElement s : e.getStackTrace()) { Log.v("SDL", s.toString()); } 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; }
@Override public void onSensorChanged(SensorEvent event) { if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { SDLActivity.onNativeAccel( event.values[0] / SensorManager.GRAVITY_EARTH, event.values[1] / SensorManager.GRAVITY_EARTH, event.values[2] / SensorManager.GRAVITY_EARTH); } }
// Called when we lose the surface @Override public void surfaceDestroyed(SurfaceHolder holder) { Log.v("SDL", "surfaceDestroyed()"); if (!SDLActivity.mIsPaused) { SDLActivity.mIsPaused = true; SDLActivity.nativePause(); } enableSensor(Sensor.TYPE_ACCELEROMETER, false); }
@Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); Log.v("SDL", "onWindowFocusChanged(): " + hasFocus); SDLActivity.mHasFocus = hasFocus; if (hasFocus) { SDLActivity.handleResume(); } }
// Called when the surface is resized public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // Log.v("SDL", "surfaceChanged()"); int sdlFormat = 0x85151002; // 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 = 0x85421002; // SDL_PIXELFORMAT_RGBA4444 break; case PixelFormat.RGBA_5551: Log.v("SDL", "pixel format RGBA_5551"); sdlFormat = 0x85441002; // SDL_PIXELFORMAT_RGBA5551 break; case PixelFormat.RGBA_8888: Log.v("SDL", "pixel format RGBA_8888"); sdlFormat = 0x86462004; // SDL_PIXELFORMAT_RGBA8888 break; case PixelFormat.RGBX_8888: Log.v("SDL", "pixel format RGBX_8888"); sdlFormat = 0x86262004; // SDL_PIXELFORMAT_RGBX8888 break; case PixelFormat.RGB_332: Log.v("SDL", "pixel format RGB_332"); sdlFormat = 0x84110801; // SDL_PIXELFORMAT_RGB332 break; case PixelFormat.RGB_565: Log.v("SDL", "pixel format RGB_565"); sdlFormat = 0x85151002; // 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 = 0x86161804; // SDL_PIXELFORMAT_RGB888 break; default: Log.v("SDL", "pixel format unknown " + format); break; } SDLActivity.onNativeResize(width, height, sdlFormat); // Now start up the C app thread if (mSDLThread == null) { mSDLThread = new Thread(new SDLMain(), "SDLThread"); mSDLThread.start(); } }
@Override protected void onResume() { Log.v("SDL", "onResume()"); super.onResume(); if (SDLActivity.mBrokenLibraries) { return; } SDLActivity.handleResume(); }
// Key events @Override public boolean onKey(View v, int keyCode, KeyEvent event) { // Urho3D: let the home & volume keys be handled by the system if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_HOME) return false; 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 public boolean onTouch(View v, MotionEvent event) { int action = event.getAction(); float x = event.getX(); float y = event.getY(); float p = event.getPressure(); // TODO: Anything else we need to pass? SDLActivity.onNativeTouch(action, x, y, p); return true; }
@Override public void onLowMemory() { Log.v("SDL", "onLowMemory()"); super.onLowMemory(); if (SDLActivity.mBrokenLibraries) { return; } SDLActivity.nativeLowMemory(); }
@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 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; }
// Generic Motion (mouse hover, joystick...) events go here @Override public boolean onGenericMotion(View v, MotionEvent event) { float x, y; int mouseButton; int action; switch (event.getSource()) { case InputDevice.SOURCE_JOYSTICK: case InputDevice.SOURCE_GAMEPAD: case InputDevice.SOURCE_DPAD: SDLActivity.handleJoystickMotionEvent(event); return true; case InputDevice.SOURCE_MOUSE: action = event.getActionMasked(); switch (action) { case MotionEvent.ACTION_SCROLL: x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0); y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0); SDLActivity.onNativeMouse(0, action, x, y); return true; case MotionEvent.ACTION_HOVER_MOVE: x = event.getX(0); y = event.getY(0); SDLActivity.onNativeMouse(0, action, x, y); return true; default: break; } default: break; } // Event was not managed return false; }
@Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); Log.v("SDL", "onWindowFocusChanged(): " + hasFocus); if (SDLActivity.mBrokenLibraries) { return; } SDLActivity.mHasFocus = hasFocus; if (hasFocus) { SDLActivity.handleResume(); } }
public static void startApp() { // Start up the C app thread if (mSDLThread == null) { mSDLThread = new Thread(new SDLMain(), "SDLThread"); mSDLThread.start(); } else { /* * Some Android variants may send multiple surfaceChanged events, so we don't need to resume every time * every time we get one of those events, only if it comes after surfaceDestroyed */ if (mIsPaused) { SDLActivity.nativeResume(); SDLActivity.mIsPaused = false; } } }