diff options
author | 2016-03-27 14:06:51 +0200 | |
---|---|---|
committer | 2016-03-27 17:35:23 +0200 | |
commit | 158c90cf1196f219e6a7c9c3e23ca47129429a7b (patch) | |
tree | 4630c4834544108f1ec74b5b48fe4d437d3efbeb | |
parent | 5640305d0ecfdbf6a1a456688f48f6f0fe22adb3 (diff) |
Initial work to make MAME work on Android [Miodrag Milanovic]
37 files changed, 2197 insertions, 58 deletions
diff --git a/.gitignore b/.gitignore index 083e0369858..aec8b94c19b 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ /* /*/ !/3rdparty/ +!/android-project/ !/benchmarks/ !/artwork/ !/bgfx/ diff --git a/android-project/.gitignore b/android-project/.gitignore new file mode 100644 index 00000000000..731d58fc5e5 --- /dev/null +++ b/android-project/.gitignore @@ -0,0 +1,7 @@ +.idea +.gradle +local.properties +*.iml +*.so +*.apk +build diff --git a/android-project/app/build.gradle b/android-project/app/build.gradle new file mode 100644 index 00000000000..1e913c88438 --- /dev/null +++ b/android-project/app/build.gradle @@ -0,0 +1,29 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 18 + buildToolsVersion "22.0.1" + + defaultConfig { + applicationId "org.mamedev.mame" + minSdkVersion 18 + targetSdkVersion 18 + + ndk { + moduleName 'main' + } + + } + + sourceSets.main { + jni.srcDirs = [] + jniLibs.srcDir 'src/main/libs' + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' + } + } +} diff --git a/android-project/app/src/main/AndroidManifest.xml b/android-project/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..abe6f84cb04 --- /dev/null +++ b/android-project/app/src/main/AndroidManifest.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Replace org.libsdl.app with the identifier of your game below, e.g. + com.gamemaker.game +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="org.mamedev.mame" + android:versionCode="1" + android:versionName="0.137" + android:installLocation="auto"> + + <!-- Android 4.0 --> + <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="14" /> + + <!-- OpenGL ES 2.0 --> + <uses-feature android:glEsVersion="0x00020000" /> + + <!-- Allow writing to external storage --> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + + <!-- Create a Java class extending SDLActivity and place it in a + directory under src matching the package, e.g. + src/com/gamemaker/game/MyGame.java + + then replace "SDLActivity" with the name of your class (e.g. "MyGame") + in the XML below. + + An example Java class can be found in README-android.txt + --> + <application android:label="@string/app_name" + android:icon="@drawable/ic_launcher" + android:allowBackup="true" + android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + android:hardwareAccelerated="true" > + <activity android:name="MAME" + android:label="@string/app_name" + android:configChanges="keyboardHidden|orientation" + > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + <!-- Drop file event --> + <!-- + <intent-filter> + <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:mimeType="*/*" /> + </intent-filter> + --> + </activity> + </application> + +</manifest> diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java new file mode 100644 index 00000000000..0a2e5d87e31 --- /dev/null +++ b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java @@ -0,0 +1,1661 @@ +package org.libsdl.app; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.lang.reflect.Method; + +import android.app.*; +import android.content.*; +import android.text.InputType; +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.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.os.*; +import android.util.Log; +import android.util.SparseArray; +import android.graphics.*; +import android.graphics.drawable.Drawable; +import android.media.*; +import android.hardware.*; +import android.content.pm.ActivityInfo; + +/** + 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; + + /** If shared libraries (e.g. SDL or the native application) could not be loaded. */ + public static boolean mBrokenLibraries; + + // If we want to separate mouse and touch events. + // This is only toggled in native code when a hint is set! + public static boolean mSeparateMouseAndTouch; + + // 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; + + /** + * This method is called by SDL before loading the native shared libraries. + * It can be overridden to provide names of shared libraries to be loaded. + * The default implementation returns the defaults. It never returns null. + * An array returned by a new implementation must at least contain "SDL2". + * Also keep in mind that the order the libraries are loaded may matter. + * @return names of shared libraries to be loaded (e.g. "SDL2", "main"). + */ + protected String[] getLibraries() { + return new String[] { + "SDL2", + // "SDL2_image", + // "SDL2_mixer", + // "SDL2_net", + // "SDL2_ttf", + "main" + }; + } + + // Load the .so + public void loadLibraries() { + for (String lib : getLibraries()) { + System.loadLibrary(lib); + } + } + + /** + * This method is called by SDL before starting the native application thread. + * It can be overridden to provide the arguments after the application name. + * The default implementation returns an empty array. It never returns null. + * @return arguments for the native application. + */ + protected String[] getArguments() { + return new String[0]; + } + + 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; + mBrokenLibraries = false; + mIsPaused = false; + mIsSurfaceReady = false; + mHasFocus = true; + } + + // Setup + @Override + protected void onCreate(Bundle savedInstanceState) { + Log.v(TAG, "Device: " + android.os.Build.DEVICE); + Log.v(TAG, "Model: " + android.os.Build.MODEL); + Log.v(TAG, "onCreate(): " + mSingleton); + super.onCreate(savedInstanceState); + + SDLActivity.initialize(); + // So we can call stuff from static callbacks + mSingleton = this; + + // Load shared libraries + String errorMsgBrokenLib = ""; + try { + loadLibraries(); + } catch(UnsatisfiedLinkError e) { + System.err.println(e.getMessage()); + mBrokenLibraries = true; + errorMsgBrokenLib = e.getMessage(); + } catch(Exception e) { + System.err.println(e.getMessage()); + mBrokenLibraries = true; + errorMsgBrokenLib = e.getMessage(); + } + + if (mBrokenLibraries) + { + AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this); + dlgAlert.setMessage("An error occurred while trying to start the application. Please try again and/or reinstall." + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + "Error: " + errorMsgBrokenLib); + dlgAlert.setTitle("SDL Error"); + dlgAlert.setPositiveButton("Exit", + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog,int id) { + // if this button is clicked, close current activity + SDLActivity.mSingleton.finish(); + } + }); + dlgAlert.setCancelable(false); + dlgAlert.create().show(); + + return; + } + + // 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); + + // Get filename from "Open with" of another application + Intent intent = getIntent(); + + if (intent != null && intent.getData() != null) { + String filename = intent.getData().getPath(); + if (filename != null) { + Log.v(TAG, "Got filename: " + filename); + SDLActivity.onNativeDropFile(filename); + } + } + } + + // Events + @Override + protected void onPause() { + Log.v(TAG, "onPause()"); + super.onPause(); + + if (SDLActivity.mBrokenLibraries) { + return; + } + + SDLActivity.handlePause(); + } + + @Override + protected void onResume() { + Log.v(TAG, "onResume()"); + super.onResume(); + + if (SDLActivity.mBrokenLibraries) { + return; + } + + SDLActivity.handleResume(); + } + + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + Log.v(TAG, "onWindowFocusChanged(): " + hasFocus); + + if (SDLActivity.mBrokenLibraries) { + return; + } + + SDLActivity.mHasFocus = hasFocus; + if (hasFocus) { + SDLActivity.handleResume(); + } + } + + @Override + public void onLowMemory() { + Log.v(TAG, "onLowMemory()"); + super.onLowMemory(); + + if (SDLActivity.mBrokenLibraries) { + return; + } + + SDLActivity.nativeLowMemory(); + } + + @Override + protected void onDestroy() { + Log.v(TAG, "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(TAG, "Problem stopping thread: " + e); + } + SDLActivity.mSDLThread = null; + + //Log.v(TAG, "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) { + + if (SDLActivity.mBrokenLibraries) { + return false; + } + + 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.handlePause(); + } + } + + /** 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(); + } + } + + /* 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; + static final int COMMAND_SET_KEEP_SCREEN_ON = 5; + + 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; + case COMMAND_SET_KEEP_SCREEN_ON: + { + Window window = ((Activity) context).getWindow(); + if (window != null) { + if ((msg.obj instanceof Integer) && (((Integer) msg.obj).intValue() != 0)) { + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } else { + window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + } + 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 int nativeInit(Object arguments); + public static native void nativeLowMemory(); + public static native void nativeQuit(); + public static native void nativePause(); + public static native void nativeResume(); + public static native void onNativeDropFile(String filename); + public static native void onNativeResize(int x, int y, int format, float rate); + 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 onNativeMouse(int button, int action, float x, float y); + 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 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 native String nativeGetHint(String name); + + /** + * This method is called by SDL using JNI. + */ + public static boolean setActivityTitle(String title) { + // Called from SDLMain() thread and can't directly affect the view + return mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title); + } + + /** + * This method is called by SDL using JNI. + */ + public static boolean sendMessage(int command, int param) { + return mSingleton.sendCommand(command, Integer.valueOf(param)); + } + + /** + * This method is called by SDL using JNI. + */ + public static Context getContext() { + return mSingleton; + } + + /** + * This method is called by SDL using JNI. + * @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); + } + } + + /** + * This method is called by SDL using JNI. + */ + 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)); + } + + /** + * This method is called by SDL using JNI. + */ + public static Surface getNativeSurface() { + return SDLActivity.mSurface.getNativeSurface(); + } + + // Audio + + /** + * This method is called by SDL using JNI. + */ + 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(TAG, "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(TAG, "Failed during initialization of Audio Track"); + mAudioTrack = null; + return -1; + } + + mAudioTrack.play(); + } + + Log.v(TAG, "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; + } + + /** + * This method is called by SDL using JNI. + */ + 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(TAG, "SDL audio: error return from write(short)"); + return; + } + } + } + + /** + * This method is called by SDL using JNI. + */ + 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(TAG, "SDL audio: error return from write(byte)"); + return; + } + } + } + + /** + * This method is called by SDL using JNI. + */ + public static void audioQuit() { + if (mAudioTrack != null) { + mAudioTrack.stop(); + mAudioTrack = null; + } + } + + // Input + + /** + * This method is called by SDL using JNI. + * @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); + } + + /** + * This method is called by SDL using JNI. + */ + public static void pollInputDevices() { + if (SDLActivity.mSDLThread != null) { + mJoystickHandler.pollInputDevices(); + } + } + + // APK expansion files support + + /** com.android.vending.expansion.zipfile.ZipResourceFile object or null. */ + private Object expansionFile; + + /** com.android.vending.expansion.zipfile.ZipResourceFile's getInputStream() or null. */ + private Method expansionFileMethod; + + /** + * This method was called by SDL using JNI. + * @deprecated because of an incorrect name + */ + @Deprecated + public InputStream openAPKExtensionInputStream(String fileName) throws IOException { + return openAPKExpansionInputStream(fileName); + } + + /** + * This method is called by SDL using JNI. + * @return an InputStream on success or null if no expansion file was used. + * @throws IOException on errors. Message is set for the SDL error message. + */ + public InputStream openAPKExpansionInputStream(String fileName) throws IOException { + // Get a ZipResourceFile representing a merger of both the main and patch files + if (expansionFile == null) { + String mainHint = nativeGetHint("SDL_ANDROID_APK_EXPANSION_MAIN_FILE_VERSION"); + if (mainHint == null) { + return null; // no expansion use if no main version was set + } + String patchHint = nativeGetHint("SDL_ANDROID_APK_EXPANSION_PATCH_FILE_VERSION"); + if (patchHint == null) { + return null; // no expansion use if no patch version was set + } + + Integer mainVersion; + Integer patchVersion; + try { + mainVersion = Integer.valueOf(mainHint); + patchVersion = Integer.valueOf(patchHint); + } catch (NumberFormatException ex) { + ex.printStackTrace(); + throw new IOException("No valid file versions set for APK expansion files", ex); + } + + try { + // To avoid direct dependency on Google APK expansion library that is + // not a part of Android SDK we access it using reflection + expansionFile = Class.forName("com.android.vending.expansion.zipfile.APKExpansionSupport") + .getMethod("getAPKExpansionZipFile", Context.class, int.class, int.class) + .invoke(null, this, mainVersion, patchVersion); + + expansionFileMethod = expansionFile.getClass() + .getMethod("getInputStream", String.class); + } catch (Exception ex) { + ex.printStackTrace(); + expansionFile = null; + expansionFileMethod = null; + throw new IOException("Could not access APK expansion support library", ex); + } + } + + // Get an input stream for a known file inside the expansion file ZIPs + InputStream fileStream; + try { + fileStream = (InputStream)expansionFileMethod.invoke(expansionFile, fileName); + } catch (Exception ex) { + // calling "getInputStream" failed + ex.printStackTrace(); + throw new IOException("Could not open stream from APK expansion file", ex); + } + + if (fileStream == null) { + // calling "getInputStream" was successful but null was returned + throw new IOException("Could not find path in APK expansion file"); + } + + return fileStream; + } + + // Messagebox + + /** Result of current messagebox. Also used for blocking the calling thread. */ + protected final int[] messageboxSelection = new int[1]; + + /** Id of current dialog. */ + protected int dialogs = 0; + + /** + * This method is called by SDL using JNI. + * Shows the messagebox from UI thread and block calling thread. + * buttonFlags, buttonIds and buttonTexts must have same length. + * @param buttonFlags array containing flags for every button. + * @param buttonIds array containing id for every button. + * @param buttonTexts array containing text for every button. + * @param colors null for default or array of length 5 containing colors. + * @return button id or -1. + */ + public int messageboxShowMessageBox( + final int flags, + final String title, + final String message, + final int[] buttonFlags, + final int[] buttonIds, + final String[] buttonTexts, + final int[] colors) { + + messageboxSelection[0] = -1; + + // sanity checks + + if ((buttonFlags.length != buttonIds.length) && (buttonIds.length != buttonTexts.length)) { + return -1; // implementation broken + } + + // collect arguments for Dialog + + final Bundle args = new Bundle(); + args.putInt("flags", flags); + args.putString("title", title); + args.putString("message", message); + args.putIntArray("buttonFlags", buttonFlags); + args.putIntArray("buttonIds", buttonIds); + args.putStringArray("buttonTexts", buttonTexts); + args.putIntArray("colors", colors); + + // trigger Dialog creation on UI thread + + runOnUiThread(new Runnable() { + @Override + public void run() { + showDialog(dialogs++, args); + } + }); + + // block the calling thread + + synchronized (messageboxSelection) { + try { + messageboxSelection.wait(); + } catch (InterruptedException ex) { + ex.printStackTrace(); + return -1; + } + } + + // return selected value + + return messageboxSelection[0]; + } + + @Override + protected Dialog onCreateDialog(int ignore, Bundle args) { + + // TODO set values from "flags" to messagebox dialog + + // get colors + + int[] colors = args.getIntArray("colors"); + int backgroundColor; + int textColor; + int buttonBorderColor; + int buttonBackgroundColor; + int buttonSelectedColor; + if (colors != null) { + int i = -1; + backgroundColor = colors[++i]; + textColor = colors[++i]; + buttonBorderColor = colors[++i]; + buttonBackgroundColor = colors[++i]; + buttonSelectedColor = colors[++i]; + } else { + backgroundColor = Color.TRANSPARENT; + textColor = Color.TRANSPARENT; + buttonBorderColor = Color.TRANSPARENT; + buttonBackgroundColor = Color.TRANSPARENT; + buttonSelectedColor = Color.TRANSPARENT; + } + + // create dialog with title and a listener to wake up calling thread + + final Dialog dialog = new Dialog(this); + dialog.setTitle(args.getString("title")); + dialog.setCancelable(false); + dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface unused) { + synchronized (messageboxSelection) { + messageboxSelection.notify(); + } + } + }); + + // create text + + TextView message = new TextView(this); + message.setGravity(Gravity.CENTER); + message.setText(args.getString("message")); + if (textColor != Color.TRANSPARENT) { + message.setTextColor(textColor); + } + + // create buttons + + int[] buttonFlags = args.getIntArray("buttonFlags"); + int[] buttonIds = args.getIntArray("buttonIds"); + String[] buttonTexts = args.getStringArray("buttonTexts"); + + final SparseArray<Button> mapping = new SparseArray<Button>(); + + LinearLayout buttons = new LinearLayout(this); + buttons.setOrientation(LinearLayout.HORIZONTAL); + buttons.setGravity(Gravity.CENTER); + for (int i = 0; i < buttonTexts.length; ++i) { + Button button = new Button(this); + final int id = buttonIds[i]; + button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + messageboxSelection[0] = id; + dialog.dismiss(); + } + }); + if (buttonFlags[i] != 0) { + // see SDL_messagebox.h + if ((buttonFlags[i] & 0x00000001) != 0) { + mapping.put(KeyEvent.KEYCODE_ENTER, button); + } + if ((buttonFlags[i] & 0x00000002) != 0) { + mapping.put(111, button); /* API 11: KeyEvent.KEYCODE_ESCAPE */ + } + } + button.setText(buttonTexts[i]); + if (textColor != Color.TRANSPARENT) { + button.setTextColor(textColor); + } + if (buttonBorderColor != Color.TRANSPARENT) { + // TODO set color for border of messagebox button + } + if (buttonBackgroundColor != Color.TRANSPARENT) { + Drawable drawable = button.getBackground(); + if (drawable == null) { + // setting the color this way removes the style + button.setBackgroundColor(buttonBackgroundColor); + } else { + // setting the color this way keeps the style (gradient, padding, etc.) + drawable.setColorFilter(buttonBackgroundColor, PorterDuff.Mode.MULTIPLY); + } + } + if (buttonSelectedColor != Color.TRANSPARENT) { + // TODO set color for selected messagebox button + } + buttons.addView(button); + } + + // create content + + LinearLayout content = new LinearLayout(this); + content.setOrientation(LinearLayout.VERTICAL); + content.addView(message); + content.addView(buttons); + if (backgroundColor != Color.TRANSPARENT) { + content.setBackgroundColor(backgroundColor); + } + + // add content to dialog and return + + dialog.setContentView(content); + dialog.setOnKeyListener(new Dialog.OnKeyListener() { + @Override + public boolean onKey(DialogInterface d, int keyCode, KeyEvent event) { + Button button = mapping.get(keyCode); + if (button != null) { + if (event.getAction() == KeyEvent.ACTION_UP) { + button.performClick(); + } + return true; // also for ignored actions + } + return false; + } + }); + + return dialog; + } +} + +/** + Simple nativeInit() runnable +*/ +class SDLMain implements Runnable { + @Override + public void run() { + // Runs SDL_main() + SDLActivity.nativeInit(SDLActivity.mSingleton.getArguments()); + + //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 void handlePause() { + enableSensor(Sensor.TYPE_ACCELEROMETER, false); + } + + public void handleResume() { + setFocusable(true); + setFocusableInTouchMode(true); + requestFocus(); + setOnKeyListener(this); + setOnTouchListener(this); + enableSensor(Sensor.TYPE_ACCELEROMETER, true); + } + + 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, mDisplay.getRefreshRate()); + Log.v("SDL", "Window size: " + width + "x" + height); + + + boolean skip = false; + int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation(); + + if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) + { + // Accept any + } + else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) + { + if (mWidth > mHeight) { + skip = true; + } + } else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) { + if (mWidth < mHeight) { + skip = true; + } + } + + // Special Patch for Square Resolution: Black Berry Passport + if (skip) { + double min = Math.min(mWidth, mHeight); + double max = Math.max(mWidth, mHeight); + + if (max / min < 1.20) { + Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution."); + skip = false; + } + } + + if (skip) { + Log.v("SDL", "Skip .. Surface is not ready."); + return; + } + + + // 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 + + final Thread sdlThread = new Thread(new SDLMain(), "SDLThread"); + enableSensor(Sensor.TYPE_ACCELEROMETER, true); + sdlThread.start(); + + // Set up a listener thread to catch when the native thread ends + SDLActivity.mSDLThread = new Thread(new Runnable(){ + @Override + public void run(){ + try { + sdlThread.join(); + } + catch(Exception e){} + finally{ + // Native thread has finished + if (! SDLActivity.mExitCalledFromJava) { + SDLActivity.handleNativeExit(); + } + } + } + }, "SDLThreadListener"); + SDLActivity.mSDLThread.start(); + } + + if (SDLActivity.mHasFocus) { + SDLActivity.handleResume(); + } + } + + // 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) { + /* 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 mouseButton; + int i = -1; + float x,y,p; + + // !!! FIXME: dump this SDK check after 2.0.4 ships and require API14. + if (event.getSource() == InputDevice.SOURCE_MOUSE && SDLActivity.mSeparateMouseAndTouch) { + if (Build.VERSION.SDK_INT < 14) { + mouseButton = 1; // For Android==12 all mouse buttons are the left button + } else { + try { + mouseButton = (Integer) event.getClass().getMethod("getButtonState").invoke(event); + } catch(Exception e) { + mouseButton = 1; // oh well. + } + } + SDLActivity.onNativeMouse(mouseButton, action, event.getX(0), event.getY(0)); + } else { + 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); + if (p > 1.0f) { + // may be larger than 1.0f on some devices + // see the documentation of getPressure(i) + p = 1.0f; + } + 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); + if (p > 1.0f) { + // may be larger than 1.0f on some devices + // see the documentation of getPressure(i) + p = 1.0f; + } + SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p); + break; + + case MotionEvent.ACTION_CANCEL: + for (i = 0; i < pointerCount; i++) { + pointerFingerId = event.getPointerId(i); + x = event.getX(i) / mWidth; + y = event.getY(i) / mHeight; + p = event.getPressure(i); + if (p > 1.0f) { + // may be larger than 1.0f on some devices + // see the documentation of getPressure(i) + p = 1.0f; + } + SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, 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.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD; + 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 { + + /** + * Handles given MotionEvent. + * @param event the event to be handled. + * @return if given event was processed. + */ + public boolean handleMotionEvent(MotionEvent event) { + return false; + } + + /** + * Handles adding and removing of input devices. + */ + public void pollInputDevices() { + } +} + +/* Actual joystick functionality available for API >= 12 devices */ +class SDLJoystickHandler_API12 extends SDLJoystickHandler { + + static class SDLJoystick { + public int device_id; + public String name; + public ArrayList<InputDevice.MotionRange> axes; + public ArrayList<InputDevice.MotionRange> hats; + } + static class RangeComparator implements Comparator<InputDevice.MotionRange> { + @Override + public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) { + return arg0.getAxis() - arg1.getAxis(); + } + } + + private ArrayList<SDLJoystick> mJoysticks; + + public SDLJoystickHandler_API12() { + + mJoysticks = new ArrayList<SDLJoystick>(); + } + + @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 + || + (joystickDevice.getSources() & InputDevice.SOURCE_CLASS_BUTTON) != 0 + ) + { + joystick.device_id = deviceIds[i]; + joystick.name = joystickDevice.getName(); + joystick.axes = new ArrayList<InputDevice.MotionRange>(); + joystick.hats = new ArrayList<InputDevice.MotionRange>(); + + List<InputDevice.MotionRange> 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<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(Integer.valueOf(device_id)); + } + } + + for(int i=0; i < removedDevices.size(); i++) { + int device_id = removedDevices.get(i).intValue(); + 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 + @Override + public boolean onGenericMotion(View v, MotionEvent event) { + float x, y; + 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; + } + break; + + default: + break; + } + + // Event was not managed + return false; + } +} diff --git a/android-project/app/src/main/java/org/mamedev/mame/MAME.java b/android-project/app/src/main/java/org/mamedev/mame/MAME.java new file mode 100644 index 00000000000..d243b59159f --- /dev/null +++ b/android-project/app/src/main/java/org/mamedev/mame/MAME.java @@ -0,0 +1,9 @@ +package org.mamedev.mame; + +import org.libsdl.app.SDLActivity; +/** + SDL Activity +*/ +public class MAME extends SDLActivity { + +} diff --git a/android-project/app/src/main/libs/arm64-v8a/.gitignore b/android-project/app/src/main/libs/arm64-v8a/.gitignore new file mode 100644 index 00000000000..140f8cf80f2 --- /dev/null +++ b/android-project/app/src/main/libs/arm64-v8a/.gitignore @@ -0,0 +1 @@ +*.so diff --git a/android-project/app/src/main/libs/armeabi-v7a/.gitignore b/android-project/app/src/main/libs/armeabi-v7a/.gitignore new file mode 100644 index 00000000000..140f8cf80f2 --- /dev/null +++ b/android-project/app/src/main/libs/armeabi-v7a/.gitignore @@ -0,0 +1 @@ +*.so diff --git a/android-project/app/src/main/libs/mips/.gitignore b/android-project/app/src/main/libs/mips/.gitignore new file mode 100644 index 00000000000..140f8cf80f2 --- /dev/null +++ b/android-project/app/src/main/libs/mips/.gitignore @@ -0,0 +1 @@ +*.so diff --git a/android-project/app/src/main/libs/mips64/.gitignore b/android-project/app/src/main/libs/mips64/.gitignore new file mode 100644 index 00000000000..140f8cf80f2 --- /dev/null +++ b/android-project/app/src/main/libs/mips64/.gitignore @@ -0,0 +1 @@ +*.so diff --git a/android-project/app/src/main/libs/x86/.gitignore b/android-project/app/src/main/libs/x86/.gitignore new file mode 100644 index 00000000000..140f8cf80f2 --- /dev/null +++ b/android-project/app/src/main/libs/x86/.gitignore @@ -0,0 +1 @@ +*.so diff --git a/android-project/app/src/main/libs/x86_64/.gitignore b/android-project/app/src/main/libs/x86_64/.gitignore new file mode 100644 index 00000000000..140f8cf80f2 --- /dev/null +++ b/android-project/app/src/main/libs/x86_64/.gitignore @@ -0,0 +1 @@ +*.so diff --git a/android-project/app/src/main/res/drawable-hdpi/ic_launcher.png b/android-project/app/src/main/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000000..d50bdaae06e --- /dev/null +++ b/android-project/app/src/main/res/drawable-hdpi/ic_launcher.png diff --git a/android-project/app/src/main/res/drawable-mdpi/ic_launcher.png b/android-project/app/src/main/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000000..0a299eb3cc0 --- /dev/null +++ b/android-project/app/src/main/res/drawable-mdpi/ic_launcher.png diff --git a/android-project/app/src/main/res/drawable-xhdpi/ic_launcher.png b/android-project/app/src/main/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000000..a336ad5c2ba --- /dev/null +++ b/android-project/app/src/main/res/drawable-xhdpi/ic_launcher.png diff --git a/android-project/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/android-project/app/src/main/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000000..d423dac2624 --- /dev/null +++ b/android-project/app/src/main/res/drawable-xxhdpi/ic_launcher.png diff --git a/android-project/app/src/main/res/layout/main.xml b/android-project/app/src/main/res/layout/main.xml new file mode 100644 index 00000000000..123c4b6eac5 --- /dev/null +++ b/android-project/app/src/main/res/layout/main.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + > +<TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="Hello World, SDLActivity" + /> +</LinearLayout> + diff --git a/android-project/app/src/main/res/values/strings.xml b/android-project/app/src/main/res/values/strings.xml new file mode 100644 index 00000000000..28dd1ee79a1 --- /dev/null +++ b/android-project/app/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">MAME</string> +</resources> diff --git a/android-project/build.gradle b/android-project/build.gradle new file mode 100644 index 00000000000..88d246d44a6 --- /dev/null +++ b/android-project/build.gradle @@ -0,0 +1,15 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:1.2.3' + } +} + +allprojects { + repositories { + jcenter() + } +} diff --git a/android-project/gradle/wrapper/gradle-wrapper.jar b/android-project/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 00000000000..8c0fb64a869 --- /dev/null +++ b/android-project/gradle/wrapper/gradle-wrapper.jar diff --git a/android-project/gradle/wrapper/gradle-wrapper.properties b/android-project/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000000..0c71e760dc9 --- /dev/null +++ b/android-project/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Apr 10 15:27:10 PDT 2013 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip diff --git a/android-project/gradlew b/android-project/gradlew new file mode 100644 index 00000000000..91a7e269e19 --- /dev/null +++ b/android-project/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/android-project/gradlew.bat b/android-project/gradlew.bat new file mode 100644 index 00000000000..8a0b282aa68 --- /dev/null +++ b/android-project/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/android-project/settings.gradle b/android-project/settings.gradle new file mode 100644 index 00000000000..e7b4def49cb --- /dev/null +++ b/android-project/settings.gradle @@ -0,0 +1 @@ +include ':app' @@ -978,145 +978,145 @@ endif # android-arm #------------------------------------------------- -$(PROJECTDIR_MINI)/gmake-android-arm/Makefile: makefile $(SCRIPTS) $(GENIE) +$(PROJECTDIR_SDL)/gmake-android-arm/Makefile: makefile $(SCRIPTS) $(GENIE) ifndef ANDROID_NDK_ARM $(error ANDROID_NDK_ARM is not set) endif ifndef ANDROID_NDK_ROOT $(error ANDROID_NDK_ROOT is not set) endif - $(SILENT) $(GENIE) $(PARAMS) --gcc=android-arm --gcc_version=3.8.0 --osd=osdmini --targetos=android-arm --targetos=android --PLATFORM=arm --NOASM=1 gmake + $(SILENT) $(GENIE) $(PARAMS) --gcc=android-arm --gcc_version=3.8.0 --osd=sdl --targetos=android --PLATFORM=arm --NO_USE_MIDI=1 --NO_OPENGL=1 --USE_QTDEBUG=0 --NO_X11=1 --DONT_USE_NETWORK=1 --NOASM=1 gmake .PHONY: android-arm -android-arm: generate $(PROJECTDIR_MINI)/gmake-android-arm/Makefile +android-arm: generate $(PROJECTDIR_SDL)/gmake-android-arm/Makefile ifndef ANDROID_NDK_ARM $(error ANDROID_NDK_ARM is not set) endif ifndef ANDROID_NDK_ROOT $(error ANDROID_NDK_ROOT is not set) endif - $(SILENT) $(MAKE) $(MAKEPARAMS) -C $(PROJECTDIR_MINI)/gmake-android-arm config=$(CONFIG) precompile - $(SILENT) $(MAKE) $(MAKEPARAMS) -C $(PROJECTDIR_MINI)/gmake-android-arm config=$(CONFIG) + $(SILENT) $(MAKE) $(MAKEPARAMS) -C $(PROJECTDIR_SDL)/gmake-android-arm config=$(CONFIG) precompile + $(SILENT) $(MAKE) $(MAKEPARAMS) -C $(PROJECTDIR_SDL)/gmake-android-arm config=$(CONFIG) #------------------------------------------------- # android-arm64 #------------------------------------------------- -$(PROJECTDIR_MINI)/gmake-android-arm64/Makefile: makefile $(SCRIPTS) $(GENIE) +$(PROJECTDIR_SDL)/gmake-android-arm64/Makefile: makefile $(SCRIPTS) $(GENIE) ifndef ANDROID_NDK_ARM64 $(error ANDROID_NDK_ARM64 is not set) endif ifndef ANDROID_NDK_ROOT $(error ANDROID_NDK_ROOT is not set) endif - $(SILENT) $(GENIE) $(PARAMS) --gcc=android-arm64 --gcc_version=3.8.0 --osd=osdmini --targetos=android-arm64 --targetos=android --PLATFORM=arm64 --NOASM=1 gmake + $(SILENT) $(GENIE) $(PARAMS) --gcc=android-arm64 --gcc_version=3.8.0 --osd=sdl --targetos=android --PLATFORM=arm64 --NO_USE_MIDI=1 --NO_OPENGL=1 --USE_QTDEBUG=0 --NO_X11=1 --DONT_USE_NETWORK=1 --NOASM=1 gmake .PHONY: android-arm64 -android-arm64: generate $(PROJECTDIR_MINI)/gmake-android-arm64/Makefile +android-arm64: generate $(PROJECTDIR_SDL)/gmake-android-arm64/Makefile ifndef ANDROID_NDK_ARM64 $(error ANDROID_NDK_ARM64 is not set) endif ifndef ANDROID_NDK_ROOT $(error ANDROID_NDK_ROOT is not set) endif - $(SILENT) $(MAKE) $(MAKEPARAMS) -C $(PROJECTDIR_MINI)/gmake-android-arm64 config=$(CONFIG) precompile - $(SILENT) $(MAKE) $(MAKEPARAMS) -C $(PROJECTDIR_MINI)/gmake-android-arm64 config=$(CONFIG) + $(SILENT) $(MAKE) $(MAKEPARAMS) -C $(PROJECTDIR_SDL)/gmake-android-arm64 config=$(CONFIG) precompile + $(SILENT) $(MAKE) $(MAKEPARAMS) -C $(PROJECTDIR_SDL)/gmake-android-arm64 config=$(CONFIG) #------------------------------------------------- # android-mips #------------------------------------------------- -$(PROJECTDIR_MINI)/gmake-android-mips/Makefile: makefile $(SCRIPTS) $(GENIE) +$(PROJECTDIR_SDL)/gmake-android-mips/Makefile: makefile $(SCRIPTS) $(GENIE) ifndef ANDROID_NDK_MIPS $(error ANDROID_NDK_MIPS is not set) endif ifndef ANDROID_NDK_ROOT $(error ANDROID_NDK_ROOT is not set) endif - $(SILENT) $(GENIE) $(PARAMS) --gcc=android-mips --gcc_version=3.8.0 --osd=osdmini --targetos=android-mips --targetos=android --PLATFORM=mips --NOASM=1 gmake + $(SILENT) $(GENIE) $(PARAMS) --gcc=android-mips --gcc_version=3.8.0 --osd=sdl --targetos=android --PLATFORM=mips --NO_USE_MIDI=1 --NO_OPENGL=1 --USE_QTDEBUG=0 --NO_X11=1 --DONT_USE_NETWORK=1 --NOASM=1 gmake .PHONY: android-mips -android-mips: generate $(PROJECTDIR_MINI)/gmake-android-mips/Makefile +android-mips: generate $(PROJECTDIR_SDL)/gmake-android-mips/Makefile ifndef ANDROID_NDK_MIPS $(error ANDROID_NDK_MIPS is not set) endif ifndef ANDROID_NDK_ROOT $(error ANDROID_NDK_ROOT is not set) endif - $(SILENT) $(MAKE) $(MAKEPARAMS) -C $(PROJECTDIR_MINI)/gmake-android-mips config=$(CONFIG) precompile - $(SILENT) $(MAKE) $(MAKEPARAMS) -C $(PROJECTDIR_MINI)/gmake-android-mips config=$(CONFIG) + $(SILENT) $(MAKE) $(MAKEPARAMS) -C $(PROJECTDIR_SDL)/gmake-android-mips config=$(CONFIG) precompile + $(SILENT) $(MAKE) $(MAKEPARAMS) -C $(PROJECTDIR_SDL)/gmake-android-mips config=$(CONFIG) #------------------------------------------------- # android-mips64 #------------------------------------------------- -$(PROJECTDIR_MINI)/gmake-android-mips64/Makefile: makefile $(SCRIPTS) $(GENIE) +$(PROJECTDIR_SDL)/gmake-android-mips64/Makefile: makefile $(SCRIPTS) $(GENIE) ifndef ANDROID_NDK_MIPS64 $(error ANDROID_NDK_MIPS64 is not set) endif ifndef ANDROID_NDK_ROOT $(error ANDROID_NDK_ROOT is not set) endif - $(SILENT) $(GENIE) $(PARAMS) --gcc=android-mips64 --gcc_version=3.8.0 --osd=osdmini --targetos=android-mips64 --targetos=android --PLATFORM=mips64 --NOASM=1 gmake + $(SILENT) $(GENIE) $(PARAMS) --gcc=android-mips64 --gcc_version=3.8.0 --osd=sdl --targetos=android --PLATFORM=mips64 --NO_USE_MIDI=1 --NO_OPENGL=1 --USE_QTDEBUG=0 --NO_X11=1 --DONT_USE_NETWORK=1 --NOASM=1 gmake .PHONY: android-mips64 -android-mips64: generate $(PROJECTDIR_MINI)/gmake-android-mips64/Makefile +android-mips64: generate $(PROJECTDIR_SDL)/gmake-android-mips64/Makefile ifndef ANDROID_NDK_MIPS64 $(error ANDROID_NDK_MIPS64 is not set) endif ifndef ANDROID_NDK_ROOT $(error ANDROID_NDK_ROOT is not set) endif - $(SILENT) $(MAKE) $(MAKEPARAMS) -C $(PROJECTDIR_MINI)/gmake-android-mips64 config=$(CONFIG) precompile - $(SILENT) $(MAKE) $(MAKEPARAMS) -C $(PROJECTDIR_MINI)/gmake-android-mips64 config=$(CONFIG) + $(SILENT) $(MAKE) $(MAKEPARAMS) -C $(PROJECTDIR_SDL)/gmake-android-mips64 config=$(CONFIG) precompile + $(SILENT) $(MAKE) $(MAKEPARAMS) -C $(PROJECTDIR_SDL)/gmake-android-mips64 config=$(CONFIG) #------------------------------------------------- # android-x86 #------------------------------------------------- -$(PROJECTDIR_MINI)/gmake-android-x86/Makefile: makefile $(SCRIPTS) $(GENIE) +$(PROJECTDIR_SDL)/gmake-android-x86/Makefile: makefile $(SCRIPTS) $(GENIE) ifndef ANDROID_NDK_X86 $(error ANDROID_NDK_X86 is not set) endif ifndef ANDROID_NDK_ROOT $(error ANDROID_NDK_ROOT is not set) endif - $(SILENT) $(GENIE) $(PARAMS) --gcc=android-x86 --gcc_version=3.8.0 --osd=osdmini --targetos=android-x86 --targetos=android --PLATFORM=x86 gmake + $(SILENT) $(GENIE) $(PARAMS) --gcc=android-x86 --gcc_version=3.8.0 --osd=sdl --targetos=android --PLATFORM=x86 --NO_USE_MIDI=1 --NO_OPENGL=1 --USE_QTDEBUG=0 --NO_X11=1 --DONT_USE_NETWORK=1 gmake .PHONY: android-x86 -android-x86: generate $(PROJECTDIR_MINI)/gmake-android-x86/Makefile +android-x86: generate $(PROJECTDIR_SDL)/gmake-android-x86/Makefile ifndef ANDROID_NDK_X86 $(error ANDROID_NDK_X86 is not set) endif ifndef ANDROID_NDK_ROOT $(error ANDROID_NDK_ROOT is not set) endif - $(SILENT) $(MAKE) $(MAKEPARAMS) -C $(PROJECTDIR_MINI)/gmake-android-x86 config=$(CONFIG) precompile - $(SILENT) $(MAKE) $(MAKEPARAMS) -C $(PROJECTDIR_MINI)/gmake-android-x86 config=$(CONFIG) + $(SILENT) $(MAKE) $(MAKEPARAMS) -C $(PROJECTDIR_SDL)/gmake-android-x86 config=$(CONFIG) precompile + $(SILENT) $(MAKE) $(MAKEPARAMS) -C $(PROJECTDIR_SDL)/gmake-android-x86 config=$(CONFIG) #------------------------------------------------- # android-x64 #------------------------------------------------- -$(PROJECTDIR_MINI)/gmake-android-x64/Makefile: makefile $(SCRIPTS) $(GENIE) +$(PROJECTDIR_SDL)/gmake-android-x64/Makefile: makefile $(SCRIPTS) $(GENIE) ifndef ANDROID_NDK_X64 $(error ANDROID_NDK_X64 is not set) endif ifndef ANDROID_NDK_ROOT $(error ANDROID_NDK_ROOT is not set) endif - $(SILENT) $(GENIE) $(PARAMS) --gcc=android-x64 --gcc_version=3.8.0 --osd=osdmini --targetos=android-x64 --targetos=android --PLATFORM=x64 gmake + $(SILENT) $(GENIE) $(PARAMS) --gcc=android-x64 --gcc_version=3.8.0 --osd=sdl --targetos=android --PLATFORM=x64 --NO_USE_MIDI=1 --NO_OPENGL=1 --USE_QTDEBUG=0 --NO_X11=1 --DONT_USE_NETWORK=1 gmake .PHONY: android-x64 -android-x64: generate $(PROJECTDIR_MINI)/gmake-android-x64/Makefile +android-x64: generate $(PROJECTDIR_SDL)/gmake-android-x64/Makefile ifndef ANDROID_NDK_X64 $(error ANDROID_NDK_X64 is not set) endif ifndef ANDROID_NDK_ROOT $(error ANDROID_NDK_ROOT is not set) endif - $(SILENT) $(MAKE) $(MAKEPARAMS) -C $(PROJECTDIR_MINI)/gmake-android-x64 config=$(CONFIG) precompile - $(SILENT) $(MAKE) $(MAKEPARAMS) -C $(PROJECTDIR_MINI)/gmake-android-x64 config=$(CONFIG) + $(SILENT) $(MAKE) $(MAKEPARAMS) -C $(PROJECTDIR_SDL)/gmake-android-x64 config=$(CONFIG) precompile + $(SILENT) $(MAKE) $(MAKEPARAMS) -C $(PROJECTDIR_SDL)/gmake-android-x64 config=$(CONFIG) #------------------------------------------------- # asmjs / Emscripten diff --git a/scripts/src/3rdparty.lua b/scripts/src/3rdparty.lua index ecd83a5ae99..08d536a57e6 100644 --- a/scripts/src/3rdparty.lua +++ b/scripts/src/3rdparty.lua @@ -1182,9 +1182,38 @@ end if _OPTIONS["with-bundled-sdl2"] then project "SDL2" uuid "caab3327-574f-4abf-b25b-74d5238ae59b" +if _OPTIONS["targetos"]=="android" then + kind "SharedLib" + targetextension ".so" + targetprefix "lib" + links { + "GLESv1_CM", + "GLESv2", + "log", + } + if _OPTIONS["SEPARATE_BIN"]~="1" then + if _OPTIONS["PLATFORM"]=="arm" then + targetdir(MAME_DIR .. "android-project/app/src/main/libs/armeabi-v7a") + end + if _OPTIONS["PLATFORM"]=="arm64" then + targetdir(MAME_DIR .. "android-project/app/src/main/libs/arm64-v8a") + end + if _OPTIONS["PLATFORM"]=="mips" then + targetdir(MAME_DIR .. "android-project/app/src/main/libs/mips") + end + if _OPTIONS["PLATFORM"]=="mips64" then + targetdir(MAME_DIR .. "android-project/app/src/main/libs/mips64") + end + if _OPTIONS["PLATFORM"]=="x86" then + targetdir(MAME_DIR .. "android-project/app/src/main/libs/x86") + end + if _OPTIONS["PLATFORM"]=="x64" then + targetdir(MAME_DIR .. "android-project/app/src/main/libs/x86_64") + end + end +else kind "StaticLib" - - configuration { } +end files { MAME_DIR .. "3rdparty/SDL2/include/begin_code.h", @@ -1356,7 +1385,6 @@ project "SDL2" MAME_DIR .. "3rdparty/SDL2/src/stdlib/SDL_qsort.c", MAME_DIR .. "3rdparty/SDL2/src/stdlib/SDL_stdlib.c", MAME_DIR .. "3rdparty/SDL2/src/stdlib/SDL_string.c", - MAME_DIR .. "3rdparty/SDL2/src/thread/generic/SDL_syscond.c", MAME_DIR .. "3rdparty/SDL2/src/thread/SDL_systhread.h", MAME_DIR .. "3rdparty/SDL2/src/thread/SDL_thread.c", MAME_DIR .. "3rdparty/SDL2/src/thread/SDL_thread_c.h", @@ -1520,6 +1548,7 @@ project "SDL2" if _OPTIONS["targetos"]=="windows" then files { + MAME_DIR .. "3rdparty/SDL2/src/thread/generic/SDL_syscond.c", MAME_DIR .. "3rdparty/SDL2/src/audio/directsound/SDL_directsound.c", MAME_DIR .. "3rdparty/SDL2/src/audio/directsound/SDL_directsound.h", MAME_DIR .. "3rdparty/SDL2/src/audio/winmm/SDL_winmm.c", diff --git a/scripts/src/main.lua b/scripts/src/main.lua index 8f03031d8aa..89b77213ce8 100644 --- a/scripts/src/main.lua +++ b/scripts/src/main.lua @@ -27,13 +27,17 @@ end kind "ConsoleApp" configuration { "android*" } + targetprefix "lib" + targetname "main" targetextension ".so" linkoptions { "-shared", } links { "EGL", + "GLESv1_CM", "GLESv2", + "SDL2", } configuration { "pnacl" } kind "ConsoleApp" @@ -132,8 +136,38 @@ end configuration { } - if _OPTIONS["SEPARATE_BIN"]~="1" then - targetdir(MAME_DIR) + if _OPTIONS["targetos"]=="android" then + includedirs { + MAME_DIR .. "3rdparty/SDL2/include", + } + + files { + MAME_DIR .. "3rdparty/SDL2/src/main/android/SDL_android_main.c", + } + if _OPTIONS["SEPARATE_BIN"]~="1" then + if _OPTIONS["PLATFORM"]=="arm" then + targetdir(MAME_DIR .. "android-project/app/src/main/libs/armeabi-v7a") + end + if _OPTIONS["PLATFORM"]=="arm64" then + targetdir(MAME_DIR .. "android-project/app/src/main/libs/arm64-v8a") + end + if _OPTIONS["PLATFORM"]=="mips" then + targetdir(MAME_DIR .. "android-project/app/src/main/libs/mips") + end + if _OPTIONS["PLATFORM"]=="mips64" then + targetdir(MAME_DIR .. "android-project/app/src/main/libs/mips64") + end + if _OPTIONS["PLATFORM"]=="x86" then + targetdir(MAME_DIR .. "android-project/app/src/main/libs/x86") + end + if _OPTIONS["PLATFORM"]=="x64" then + targetdir(MAME_DIR .. "android-project/app/src/main/libs/x86_64") + end + end + else + if _OPTIONS["SEPARATE_BIN"]~="1" then + targetdir(MAME_DIR) + end end findfunction("linkProjects_" .. _OPTIONS["target"] .. "_" .. _OPTIONS["subtarget"])(_OPTIONS["target"], _OPTIONS["subtarget"]) diff --git a/scripts/src/osd/modules.lua b/scripts/src/osd/modules.lua index dcce1a8e07b..5ea9bec2f1c 100644 --- a/scripts/src/osd/modules.lua +++ b/scripts/src/osd/modules.lua @@ -122,6 +122,12 @@ function osdmodulesbuild() end end + defines { + "__STDC_LIMIT_MACROS", + "__STDC_FORMAT_MACROS", + "__STDC_CONSTANT_MACROS", + } + files { MAME_DIR .. "src/osd/modules/render/drawbgfx.cpp", MAME_DIR .. "src/osd/modules/render/binpacker.cpp", diff --git a/scripts/src/osd/sdl.lua b/scripts/src/osd/sdl.lua index 47a8b0ff3b6..03e82ffcb2f 100644 --- a/scripts/src/osd/sdl.lua +++ b/scripts/src/osd/sdl.lua @@ -38,7 +38,7 @@ function maintargetosdoptions(_target,_subtarget) } end - if BASE_TARGETOS=="unix" and _OPTIONS["targetos"]~="macosx" then + if BASE_TARGETOS=="unix" and _OPTIONS["targetos"]~="macosx" and _OPTIONS["targetos"]~="android" then links { "SDL2_ttf", } @@ -239,7 +239,7 @@ elseif _OPTIONS["targetos"]=="macosx" then SDL_NETWORK = "pcap" end -if _OPTIONS["with-bundled-sdl2"]~=nil then +if _OPTIONS["with-bundled-sdl2"]~=nil or _OPTIONS["targetos"]=="android" then includedirs { GEN_DIR .. "includes", } @@ -295,7 +295,7 @@ if BASE_TARGETOS=="unix" then "/usr/openwin/lib", } end - if _OPTIONS["with-bundled-sdl2"]~=nil then + if _OPTIONS["with-bundled-sdl2"]~=nil and _OPTIONS["targetos"]~="android" then links { "SDL2", } @@ -305,7 +305,7 @@ if BASE_TARGETOS=="unix" then addoptionsfromstring(str) end - if _OPTIONS["targetos"]~="haiku" then + if _OPTIONS["targetos"]~="haiku" and _OPTIONS["targetos"]~="android" then links { "m", "pthread", diff --git a/src/osd/modules/file/posixfile.cpp b/src/osd/modules/file/posixfile.cpp index a5e3fe6a54e..513a3cbfd70 100644 --- a/src/osd/modules/file/posixfile.cpp +++ b/src/osd/modules/file/posixfile.cpp @@ -88,7 +88,7 @@ public: { ssize_t result; -#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) || defined(EMSCRIPTEN) +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) || defined(EMSCRIPTEN) || defined(__ANDROID__) result = ::pread(m_fd, buffer, size_t(count), off_t(std::make_unsigned_t<off_t>(offset))); #elif defined(WIN32) || defined(SDLMAME_NO64BITIO) if (lseek(m_fd, off_t(std::make_unsigned_t<off_t>(offset)), SEEK_SET) < 0) @@ -109,7 +109,7 @@ public: { ssize_t result; -#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) || defined(EMSCRIPTEN) +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) || defined(EMSCRIPTEN) || defined(__ANDROID__) result = ::pwrite(m_fd, buffer, size_t(count), off_t(std::make_unsigned_t<off_t>(offset))); #elif defined(WIN32) || defined(SDLMAME_NO64BITIO) if (lseek(m_fd, off_t(std::make_unsigned_t<off_t>(offset)), SEEK_SET) < 0) @@ -130,7 +130,7 @@ public: { int result; -#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) || defined(EMSCRIPTEN) || defined(WIN32) || defined(SDLMAME_NO64BITIO) +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) || defined(EMSCRIPTEN) || defined(WIN32) || defined(SDLMAME_NO64BITIO) || defined(__ANDROID__) result = ::ftruncate(m_fd, off_t(std::make_unsigned_t<off_t>(offset))); #else result = ::ftruncate64(m_fd, off64_t(offset)); @@ -236,7 +236,7 @@ osd_file::error osd_file::open(std::string const &path, std::uint32_t openflags, // attempt to open the file int fd = -1; -#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) || defined(__HAIKU__) || defined(WIN32) || defined(SDLMAME_NO64BITIO) +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) || defined(__HAIKU__) || defined(WIN32) || defined(SDLMAME_NO64BITIO) || defined(__ANDROID__) fd = ::open(dst.c_str(), access, 0666); #else fd = ::open64(dst.c_str(), access, 0666); @@ -256,7 +256,7 @@ osd_file::error osd_file::open(std::string const &path, std::uint32_t openflags, // attempt to reopen the file if (error == osd_file::error::NONE) { -#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) || defined(__HAIKU__) || defined(WIN32) || defined(SDLMAME_NO64BITIO) +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) || defined(__HAIKU__) || defined(WIN32) || defined(SDLMAME_NO64BITIO) || defined(__ANDROID__) fd = ::open(dst.c_str(), access, 0666); #else fd = ::open64(dst.c_str(), access, 0666); @@ -273,7 +273,7 @@ osd_file::error osd_file::open(std::string const &path, std::uint32_t openflags, } // get the file size -#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) || defined(__HAIKU__) || defined(WIN32) || defined(SDLMAME_NO64BITIO) +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) || defined(__HAIKU__) || defined(WIN32) || defined(SDLMAME_NO64BITIO) || defined(__ANDROID__) struct stat st; if (::fstat(fd, &st) < 0) #else @@ -340,7 +340,7 @@ int osd_get_physical_drive_geometry(const char *filename, UINT32 *cylinders, UIN osd_directory_entry *osd_stat(const std::string &path) { -#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) || defined(__HAIKU__) || defined(WIN32) || defined(SDLMAME_NO64BITIO) +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) || defined(__HAIKU__) || defined(WIN32) || defined(SDLMAME_NO64BITIO) || defined(__ANDROID__) struct stat st; int const err = ::stat(path.c_str(), &st); #else diff --git a/src/osd/modules/file/posixptty.cpp b/src/osd/modules/file/posixptty.cpp index b84838c0578..f7842574312 100644 --- a/src/osd/modules/file/posixptty.cpp +++ b/src/osd/modules/file/posixptty.cpp @@ -22,7 +22,7 @@ #if defined(__FreeBSD__) || defined(__DragonFly__) #include <termios.h> #include <libutil.h> -#elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) +#elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__ANDROID__) #include <termios.h> #include <util.h> #elif defined(__linux__) || defined(EMSCRIPTEN) @@ -141,7 +141,10 @@ osd_file::error posix_open_ptty(std::uint32_t openflags, osd_file::ptr &file, st ::close(masterfd); return errno_to_file_error(err); } -#else +#elif defined(__ANDROID__) + int masterfd = -1, slavefd = -1; + char slavepath[PATH_MAX]; +#else struct termios tios; std::memset(&tios, 0, sizeof(tios)); ::cfmakeraw(&tios); diff --git a/src/osd/modules/font/font_sdl.cpp b/src/osd/modules/font/font_sdl.cpp index c6f45635623..c5a73b0a541 100644 --- a/src/osd/modules/font/font_sdl.cpp +++ b/src/osd/modules/font/font_sdl.cpp @@ -8,7 +8,7 @@ #include "font_module.h" #include "modules/osdmodule.h" -#if defined(SDLMAME_UNIX) && !defined(SDLMAME_MACOSX) && !defined(SDLMAME_SOLARIS) && !defined(SDLMAME_HAIKU) +#if defined(SDLMAME_UNIX) && !defined(SDLMAME_MACOSX) && !defined(SDLMAME_SOLARIS) && !defined(SDLMAME_HAIKU) && !defined(SDLMAME_ANDROID) #include "corestr.h" #include "corealloc.h" diff --git a/src/osd/modules/render/drawbgfx.cpp b/src/osd/modules/render/drawbgfx.cpp index 3ef04cbf5cf..ab1985a6898 100644 --- a/src/osd/modules/render/drawbgfx.cpp +++ b/src/osd/modules/render/drawbgfx.cpp @@ -5,10 +5,6 @@ // drawbgfx.cpp - BGFX renderer // //============================================================ -#define __STDC_LIMIT_MACROS -#define __STDC_FORMAT_MACROS -#define __STDC_CONSTANT_MACROS - #if defined(SDLMAME_WIN32) || defined(OSD_WINDOWS) // standard windows headers #define WIN32_LEAN_AND_MEAN @@ -95,7 +91,7 @@ static void* sdlNativeWindowHandle(SDL_Window* _window) return wmi.info.win.window; # elif BX_PLATFORM_STEAMLINK return wmi.info.vivante.window; -# elif BX_PLATFORM_EMSCRIPTEN +# elif BX_PLATFORM_EMSCRIPTEN || BX_PLATFORM_ANDROID return nullptr; # endif // BX_PLATFORM_ } diff --git a/src/osd/sdl/sdldir.cpp b/src/osd/sdl/sdldir.cpp index 439eb0a9792..abf1f126b94 100644 --- a/src/osd/sdl/sdldir.cpp +++ b/src/osd/sdl/sdldir.cpp @@ -50,7 +50,7 @@ #define INVPATHSEPCH '\\' #endif -#if defined(SDLMAME_DARWIN) || defined(SDLMAME_WIN32) || defined(SDLMAME_NO64BITIO) || defined(SDLMAME_BSD) || defined(SDLMAME_HAIKU) || defined(SDLMAME_EMSCRIPTEN) +#if defined(SDLMAME_DARWIN) || defined(SDLMAME_WIN32) || defined(SDLMAME_NO64BITIO) || defined(SDLMAME_BSD) || defined(SDLMAME_HAIKU) || defined(SDLMAME_EMSCRIPTEN) || defined(SDLMAME_ANDROID) typedef struct dirent sdl_dirent; typedef struct stat sdl_stat; #define sdl_readdir readdir diff --git a/src/osd/sdl/sdlmain.cpp b/src/osd/sdl/sdlmain.cpp index ad71a48aa08..ef4c6b7c927 100644 --- a/src/osd/sdl/sdlmain.cpp +++ b/src/osd/sdl/sdlmain.cpp @@ -10,7 +10,7 @@ #ifdef SDLMAME_UNIX -#if (!defined(SDLMAME_MACOSX)) && (!defined(SDLMAME_EMSCRIPTEN)) +#if (!defined(SDLMAME_MACOSX)) && (!defined(SDLMAME_EMSCRIPTEN)) && (!defined(SDLMAME_ANDROID)) #ifndef SDLMAME_HAIKU #include <fontconfig/fontconfig.h> #endif @@ -202,7 +202,7 @@ int main(int argc, char *argv[]) #ifdef SDLMAME_UNIX sdl_entered_debugger = 0; -#if (!defined(SDLMAME_MACOSX)) && (!defined(SDLMAME_HAIKU)) && (!defined(SDLMAME_EMSCRIPTEN)) +#if (!defined(SDLMAME_MACOSX)) && (!defined(SDLMAME_HAIKU)) && (!defined(SDLMAME_EMSCRIPTEN)) && (!defined(SDLMAME_ANDROID)) FcInit(); #endif #endif @@ -216,7 +216,7 @@ int main(int argc, char *argv[]) } #ifdef SDLMAME_UNIX -#if (!defined(SDLMAME_MACOSX)) && (!defined(SDLMAME_HAIKU)) && (!defined(SDLMAME_EMSCRIPTEN)) +#if (!defined(SDLMAME_MACOSX)) && (!defined(SDLMAME_HAIKU)) && (!defined(SDLMAME_EMSCRIPTEN)) && (!defined(SDLMAME_ANDROID)) if (!sdl_entered_debugger) { FcFini(); diff --git a/src/osd/sdl/sdlos_unix.cpp b/src/osd/sdl/sdlos_unix.cpp index 23080512e91..82e11ebc07c 100644 --- a/src/osd/sdl/sdlos_unix.cpp +++ b/src/osd/sdl/sdlos_unix.cpp @@ -23,6 +23,12 @@ // MAME headers #include "osdcore.h" +#ifdef SDLMAME_ANDROID +char *osd_get_clipboard_text(void) +{ + return nullptr; +} +#else //============================================================ // osd_get_clipboard_text //============================================================ @@ -40,3 +46,5 @@ char *osd_get_clipboard_text(void) } return result; } + +#endif
\ No newline at end of file diff --git a/src/osd/sdl/sdlprefix.h b/src/osd/sdl/sdlprefix.h index 2acc6a9d98e..9ba0001ccc5 100644 --- a/src/osd/sdl/sdlprefix.h +++ b/src/osd/sdl/sdlprefix.h @@ -70,6 +70,10 @@ struct _IO_FILE {}; //_IO_FILE is an opaque type in the emscripten libc which makes clang cranky #endif +#if defined(__ANDROID__) +#define SDLMAME_ANDROID 1 +#endif + // fix for Ubuntu 8.10 #ifdef _FORTIFY_SOURCE #undef _FORTIFY_SOURCE |