From 1e6a3958beeda3584058114ee72ce9c31b60656b Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Mon, 25 Feb 2019 17:19:44 +0100 Subject: [PATCH 01/78] [ion/sdl] First import --- ion/src/sdl/Makefile | 33 +++++ .../app/src/main/AndroidManifest.xml | 75 +++++++++++ .../numworks/calculator/EpsilonActivity.java | 34 +++++ ion/src/sdl/android/images.cpp | 53 ++++++++ ion/src/sdl/display.cpp | 59 +++++++++ ion/src/sdl/display.h | 20 +++ ion/src/sdl/events.cpp | 43 +++++++ ion/src/sdl/images.h | 5 + ion/src/sdl/keyboard.cpp | 63 +++++++++ ion/src/sdl/keyboard.h | 19 +++ ion/src/sdl/layout.cpp | 121 ++++++++++++++++++ ion/src/sdl/layout.h | 23 ++++ ion/src/sdl/main.cpp | 78 +++++++++++ ion/src/sdl/main.h | 17 +++ 14 files changed, 643 insertions(+) create mode 100644 ion/src/sdl/Makefile create mode 100644 ion/src/sdl/android/com.numworks.calculator/app/src/main/AndroidManifest.xml create mode 100644 ion/src/sdl/android/com.numworks.calculator/app/src/main/java/com/numworks/calculator/EpsilonActivity.java create mode 100644 ion/src/sdl/android/images.cpp create mode 100644 ion/src/sdl/display.cpp create mode 100644 ion/src/sdl/display.h create mode 100644 ion/src/sdl/events.cpp create mode 100644 ion/src/sdl/images.h create mode 100644 ion/src/sdl/keyboard.cpp create mode 100644 ion/src/sdl/keyboard.h create mode 100644 ion/src/sdl/layout.cpp create mode 100644 ion/src/sdl/layout.h create mode 100644 ion/src/sdl/main.cpp create mode 100644 ion/src/sdl/main.h diff --git a/ion/src/sdl/Makefile b/ion/src/sdl/Makefile new file mode 100644 index 000000000..07368a953 --- /dev/null +++ b/ion/src/sdl/Makefile @@ -0,0 +1,33 @@ +# TODO +objs += $(addprefix ion/src/shared/, \ + crc32.o \ + crc32_padded.o \ + events.o \ + events_modifier.o \ + power.o \ + random.o \ + timing.o \ + dummy/backlight.o \ + dummy/battery.o \ + dummy/fcc_id.o \ + dummy/led.o \ + dummy/serial_number.o \ + dummy/stack.o \ + dummy/usb.o \ +) + +objs += $(addprefix ion/src/sdl/, \ + display.o \ + events.o \ + main.o \ + keyboard.o \ + layout.o \ +) + +objs += $(addprefix ion/src/sdl/android/, \ + images.o \ +) + +SFLAGS += -I/Users/romain/Sources/SDL/include + +include ion/src/sdl/external/Makefile diff --git a/ion/src/sdl/android/com.numworks.calculator/app/src/main/AndroidManifest.xml b/ion/src/sdl/android/com.numworks.calculator/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..afabdf7b4 --- /dev/null +++ b/ion/src/sdl/android/com.numworks.calculator/app/src/main/AndroidManifest.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ion/src/sdl/android/com.numworks.calculator/app/src/main/java/com/numworks/calculator/EpsilonActivity.java b/ion/src/sdl/android/com.numworks.calculator/app/src/main/java/com/numworks/calculator/EpsilonActivity.java new file mode 100644 index 000000000..b07febd5b --- /dev/null +++ b/ion/src/sdl/android/com.numworks.calculator/app/src/main/java/com/numworks/calculator/EpsilonActivity.java @@ -0,0 +1,34 @@ +package com.numworks.calculator; + +import android.graphics.Bitmap; +//import android.graphics.Bitmap.CompressFormat; +import android.graphics.BitmapFactory; +import android.app.Activity; +import android.util.Log; + +import org.libsdl.app.SDLActivity; + +public class EpsilonActivity extends SDLActivity { + protected String[] getLibraries() { + return new String[] { + "SDL2", + "epsilon" + }; + } + + public Bitmap retrieveBitmapAsset(String identifier) { + Log.w("LoadTexture", "Retrieving bitmap asset"); + Log.w("LoadTexture", "Asset is " + identifier); + Bitmap bitmap = null; + try { + Log.w("LoadTexture", "Rez is " + this.getResources().getAssets().open(identifier)); + bitmap = BitmapFactory.decodeStream( + this.getResources().getAssets().open(identifier) + ); + } catch (Exception e) { + Log.w("LoadTexture", "Coundn't load a file:" + identifier); + } + + return bitmap; + } +} diff --git a/ion/src/sdl/android/images.cpp b/ion/src/sdl/android/images.cpp new file mode 100644 index 000000000..15a3905c6 --- /dev/null +++ b/ion/src/sdl/android/images.cpp @@ -0,0 +1,53 @@ +// This shall be implemented per-platform +// +#include +#include +#include + +SDL_Surface * loadImage(const char * identifier) { + JNIEnv * env = static_cast(SDL_AndroidGetJNIEnv()); + jobject activity = static_cast(SDL_AndroidGetActivity()); + + jstring j_identifier = env->NewStringUTF(identifier); + + jclass j_class = env->FindClass("com/numworks/calculator/EpsilonActivity"); + jmethodID j_methodId = env->GetMethodID( + j_class, + "retrieveBitmapAsset", + "(Ljava/lang/String;)Landroid/graphics/Bitmap;" + ); + + jobject j_bitmap = env->CallObjectMethod(activity, j_methodId, j_identifier); + + AndroidBitmapInfo bitmapInfo; + AndroidBitmap_getInfo(env, j_bitmap, &bitmapInfo); + + void * bitmapPixels = nullptr; + AndroidBitmap_lockPixels(env, j_bitmap, &bitmapPixels); + // TODO: Handle the case where lockPixels fails + + SDL_Surface * inputSurface = SDL_CreateRGBSurfaceWithFormatFrom( + bitmapPixels, + bitmapInfo.width, + bitmapInfo.height, + 32, // BPP. TODO: Infer from pixel format + 4 * bitmapInfo.width, // Pitch. TODO: Infer from pixel format + SDL_PIXELFORMAT_ABGR8888 + ); + + SDL_Surface * outputSurface = SDL_CreateRGBSurface( + 0, // Flags. Unused. + bitmapInfo.width, // Width + bitmapInfo.height, // Height + 32, // Bits per pixel + 0, 0, 0, 0 // Default masks for the given depth + ); + + SDL_BlitSurface(inputSurface, NULL, outputSurface, NULL); + + SDL_FreeSurface(inputSurface); + + AndroidBitmap_unlockPixels(env, j_bitmap); + + return outputSurface; +} diff --git a/ion/src/sdl/display.cpp b/ion/src/sdl/display.cpp new file mode 100644 index 000000000..6c2c5822a --- /dev/null +++ b/ion/src/sdl/display.cpp @@ -0,0 +1,59 @@ +#include "display.h" +#include "keyboard.h" +#include +#include + +static KDColor sPixels[Ion::Display::Width * Ion::Display::Height]; + +namespace Ion { +namespace Display { + +static KDFrameBuffer sFrameBuffer = KDFrameBuffer(sPixels, KDSize(Ion::Display::Width, Ion::Display::Height)); + +void pushRect(KDRect r, const KDColor * pixels) { + sFrameBuffer.pushRect(r, pixels); +} + +void pushRectUniform(KDRect r, KDColor c) { + sFrameBuffer.pushRectUniform(r, c); +} + +void pullRect(KDRect r, KDColor * pixels) { + sFrameBuffer.pullRect(r, pixels); +} + +void waitForVBlank() { +} + +} +} + +namespace Ion { +namespace SDL { +namespace Display { + +static SDL_Surface * sFramebufferSurface = nullptr; + +void init() { + sFramebufferSurface = SDL_CreateRGBSurfaceWithFormatFrom( + sPixels, + Ion::Display::Width, + Ion::Display::Height, + 16, + Ion::Display::Width * 2, + SDL_PIXELFORMAT_RGB565 + ); +} + +void quit() { + SDL_FreeSurface(sFramebufferSurface); + sFramebufferSurface = nullptr; +} + +void blit(SDL_Surface * dst, SDL_Rect * rect) { + SDL_BlitScaled(sFramebufferSurface, NULL, dst, rect); +} + +} +} +} diff --git a/ion/src/sdl/display.h b/ion/src/sdl/display.h new file mode 100644 index 000000000..7d3582304 --- /dev/null +++ b/ion/src/sdl/display.h @@ -0,0 +1,20 @@ +#ifndef ION_SDL_DISPLAY_H +#define ION_SDL_DISPLAY_H + +#include +#include + +namespace Ion { +namespace SDL { +namespace Display { + +void init(); +void quit(); + +void blit(SDL_Surface * dst, SDL_Rect * rect); + +} +} +} + +#endif diff --git a/ion/src/sdl/events.cpp b/ion/src/sdl/events.cpp new file mode 100644 index 000000000..2d6324cd3 --- /dev/null +++ b/ion/src/sdl/events.cpp @@ -0,0 +1,43 @@ +#include "main.h" +#include "layout.h" + +#include + +#include + +namespace Ion { +namespace Events { + +static bool needsRefresh = true; + +Event getEvent(int * timeout) { + + if (needsRefresh) { + Ion::SDL::Main::refresh(); + needsRefresh = false; + } + + SDL_Event event; + + if (!SDL_WaitEventTimeout(&event, *timeout)) { + return None; + } + + if (event.type == SDL_FINGERDOWN) { + SDL_FPoint fp; + fp.x = event.tfinger.x; + fp.y = event.tfinger.y; + Keyboard::Key key = SDL::Layout::keyAtF(&fp); + if (key == Keyboard::Key::None) { + return None; + } + needsRefresh = true; + Event event = Event(key, isShiftActive(), isAlphaActive()); + return event; + } + + return None; +} + +} +} diff --git a/ion/src/sdl/images.h b/ion/src/sdl/images.h new file mode 100644 index 000000000..189e42333 --- /dev/null +++ b/ion/src/sdl/images.h @@ -0,0 +1,5 @@ +// This shall be implemented per-platform +// +#include + +SDL_Surface * loadImage(const char * identifier); diff --git a/ion/src/sdl/keyboard.cpp b/ion/src/sdl/keyboard.cpp new file mode 100644 index 000000000..7ebf1b8a6 --- /dev/null +++ b/ion/src/sdl/keyboard.cpp @@ -0,0 +1,63 @@ +#include "keyboard.h" +#include + +#include + +#include "images.h" + +#define APPNAME "MyApp" + +namespace Ion { +namespace Keyboard { + +State scan() { + return State(0); +} + +} +} + +namespace Ion { +namespace SDL { +namespace Keyboard { + +using namespace Ion::Keyboard; + +static SDL_Rect s_rect; + +void init(SDL_Rect rect) { + s_rect = rect; +} + +static SDL_Rect rectForKey(Key k) { + int i = static_cast(k); + return {i%10, i/10, 10, 10}; +} + +void drawOnSurface(SDL_Surface * surface) { + for (int i=0; iformat, 0xFF, 0x00, 0xFF)); + } +} + +Key keyAt(SDL_Point p) { + __android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "Looking up %dx%d", p.x, p.y); + for (int i=0; i +#include +#include + +namespace Ion { +namespace SDL { +namespace Keyboard { + +void drawOnSurface(SDL_Surface * surface); +Ion::Keyboard::Key keyAt(SDL_Point p); + +} +} +} + +#endif diff --git a/ion/src/sdl/layout.cpp b/ion/src/sdl/layout.cpp new file mode 100644 index 000000000..df06e18c0 --- /dev/null +++ b/ion/src/sdl/layout.cpp @@ -0,0 +1,121 @@ +#include "layout.h" + +namespace Ion { +namespace SDL { +namespace Layout { + +#define X(x) ((x)/(1250.0f)) +#define Y(y) ((y)/(2100.0f)) + +static int sWidth = 0; +static int sHeight = 0; + +static void makeAbsolute(SDL_FRect * f, SDL_Rect * r) { + r->x = f->x * sWidth; + r->y = f->y * sHeight; + r->w = f->w * sWidth; + r->h = f->h * sHeight; +} + +static void makeAbsolute(SDL_FPoint * f, SDL_Point * p) { + p->x = f->x * sWidth; + p->y = f->y * sHeight; +} + +void setSize(int width, int height) { + sWidth = width; + sHeight = height; +} + +void getScreenRect(SDL_Rect * rect) { + SDL_FRect fRect; + fRect.x = X(310); + fRect.y = Y(215); + fRect.w = X(640); + fRect.h = Y(485); + makeAbsolute(&fRect, rect); +} + +Keyboard::Key keyAtF(SDL_FPoint * f) { + SDL_Point p; + makeAbsolute(f, &p); + return keyAt(&p); +} + +Keyboard::Key keyAt(SDL_Point * p) { + for (int i=0; i= 0); + assert(validKeyIndex < Keyboard::NumberOfValidKeys); + SDL_FRect rectForKey[Keyboard::NumberOfValidKeys] = { + {X(250), Y(880), X(85), Y(75) }, // A1, Left + {X(330), Y(795), X(75), Y(85) }, // A2, Up + {X(330), Y(950), X(75), Y(85) }, // A3, Down + {X(400), Y(880), X(85), Y(75) }, // A4, Right + {X(765), Y(855), X(110), Y(110)}, // A5, OK + {X(900), Y(855), X(110), Y(110)}, // A6, Back + + {X(565), Y(815), X(130), Y(85)}, // B1, Home + {X(565), Y(920), X(130), Y(85)}, // B2, Power + + {X(255), Y(1066), X(110), Y(75)}, // C1, Shift + {X(385), Y(1066), X(110), Y(75)}, // C2, Alpha + {X(512), Y(1066), X(110), Y(75)}, // C3, xnt + {X(638), Y(1066), X(110), Y(75)}, // C4, var + {X(768), Y(1066), X(110), Y(75)}, // C5, toolbox + {X(895), Y(1066), X(110), Y(75)}, // C6, Delete + + {X(255), Y(1170), X(110), Y(75)}, // D1, exp + {X(385), Y(1170), X(110), Y(75)}, // D2, ln + {X(512), Y(1170), X(110), Y(75)}, // D3, log + {X(638), Y(1170), X(110), Y(75)}, // D4, i + {X(768), Y(1170), X(110), Y(75)}, // D5, comma + {X(895), Y(1170), X(110), Y(75)}, // D6, power + + {X(255), Y(1272), X(110), Y(75)}, // E1, sin + {X(385), Y(1272), X(110), Y(75)}, // E2, cos + {X(512), Y(1272), X(110), Y(75)}, // E3, tan + {X(638), Y(1272), X(110), Y(75)}, // E4, pi + {X(768), Y(1272), X(110), Y(75)}, // E5, sqrt + {X(895), Y(1272), X(110), Y(75)}, // E6, square + + {X(255), Y(1376), X(130), X(85)}, // F1, 7 + {X(408), Y(1376), X(130), X(85)}, // F2, 8 + {X(564), Y(1376), X(130), X(85)}, // F3, 9 + {X(718), Y(1376), X(130), X(85)}, // F4, ( + {X(872), Y(1376), X(130), X(85)}, // F5, ) + + {X(255), Y(1490), X(130), X(85)}, // G1, 4 + {X(408), Y(1490), X(130), X(85)}, // G2, 5 + {X(564), Y(1490), X(130), X(85)}, // G3, 6 + {X(718), Y(1490), X(130), X(85)}, // G4, * + {X(872), Y(1490), X(130), X(85)}, // G5, / + + {X(255), Y(1605), X(130), X(85)}, // H1, 1 + {X(408), Y(1605), X(130), X(85)}, // H2, 2 + {X(564), Y(1605), X(130), X(85)}, // H3, 3 + {X(718), Y(1605), X(130), X(85)}, // H4, + + {X(872), Y(1605), X(130), X(85)}, // H5, - + + {X(255), Y(1718), X(130), X(85)}, // I1, 0 + {X(408), Y(1718), X(130), X(85)}, // I2, . + {X(564), Y(1718), X(130), X(85)}, // I3, x10 + {X(718), Y(1718), X(130), X(85)}, // I4, Ans + {X(872), Y(1718), X(130), X(85)}, // I5, EXE + }; + + makeAbsolute(&rectForKey[validKeyIndex], rect); +} + +} +} +} diff --git a/ion/src/sdl/layout.h b/ion/src/sdl/layout.h new file mode 100644 index 000000000..a1afca516 --- /dev/null +++ b/ion/src/sdl/layout.h @@ -0,0 +1,23 @@ +#ifndef ION_SDL_LAYOUT_H +#define ION_SDL_LAYOUT_H + +#include +#include + +namespace Ion { +namespace SDL { +namespace Layout { + +void setSize(int w, int h); + +void getScreenRect(SDL_Rect * rect); +Ion::Keyboard::Key keyAt(SDL_Point * p); +Ion::Keyboard::Key keyAtF(SDL_FPoint * p); + +void getKeyRect(int validKeyIndex, SDL_Rect * rect); + +} +} +} + +#endif diff --git a/ion/src/sdl/main.cpp b/ion/src/sdl/main.cpp new file mode 100644 index 000000000..49059cfe1 --- /dev/null +++ b/ion/src/sdl/main.cpp @@ -0,0 +1,78 @@ +#include "main.h" +#include "display.h" +#include +#include "images.h" +#include "layout.h" + +#include +#include + +#include + +void Ion::Timing::msleep(unsigned int) { + // Do nothing! +} + +int main(int argc, char * argv[]) { + Ion::SDL::Main::init(); + ion_main(argc, argv); + //Ion::SDL::Main::quit(); + return 0; +} + +namespace Ion { +namespace SDL { +namespace Main { + +static SDL_Window * sWindow = nullptr; +static SDL_Surface * sSurface = nullptr; +static SDL_Surface * sBackgroundSurface = nullptr; + +void init() { + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + // Error... + return; + } + + sWindow = SDL_CreateWindow( + "Epsilon", + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + 0, 0, + SDL_WINDOW_RESIZABLE + ); + + SDL_SetWindowFullscreen(sWindow, 0); + + sSurface = SDL_GetWindowSurface(sWindow); + + Display::init(); + + sBackgroundSurface = loadImage("background.jpg"); +} + +void relayout() { + int windowWidth = 0; + int windowHeight = 0; + SDL_GetWindowSize(sWindow, &windowWidth, &windowHeight); + Layout::setSize(windowWidth, windowHeight); +} + +static void blitBackground(SDL_Rect * rect) { + SDL_BlitScaled(sBackgroundSurface, NULL, sSurface, rect); +} + +void refresh() { + relayout(); + + SDL_Rect screenRect; + Layout::getScreenRect(&screenRect); + + blitBackground(nullptr); + Display::blit(sSurface, &screenRect); + SDL_UpdateWindowSurface(sWindow); +} + +} +} +} diff --git a/ion/src/sdl/main.h b/ion/src/sdl/main.h new file mode 100644 index 000000000..5362dea02 --- /dev/null +++ b/ion/src/sdl/main.h @@ -0,0 +1,17 @@ +#ifndef ION_SDL_MAIN_H +#define ION_SDL_MAIN_H + +namespace Ion { +namespace SDL { +namespace Main { + +void init(); +void quit(); + +void refresh(); + +} +} +} + +#endif From cedc68314d4b40291d2816642786e02c116f96ea Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Mon, 25 Feb 2019 18:22:23 +0100 Subject: [PATCH 02/78] [ion/sdl] Use an areaOfInterest for the layout --- ion/src/sdl/events.cpp | 10 +++++----- ion/src/sdl/layout.cpp | 33 +++++++++++++++------------------ ion/src/sdl/layout.h | 5 +++-- ion/src/sdl/main.cpp | 27 +++++++++++++++++++++++++-- 4 files changed, 48 insertions(+), 27 deletions(-) diff --git a/ion/src/sdl/events.cpp b/ion/src/sdl/events.cpp index 2d6324cd3..d985c5925 100644 --- a/ion/src/sdl/events.cpp +++ b/ion/src/sdl/events.cpp @@ -23,11 +23,11 @@ Event getEvent(int * timeout) { return None; } - if (event.type == SDL_FINGERDOWN) { - SDL_FPoint fp; - fp.x = event.tfinger.x; - fp.y = event.tfinger.y; - Keyboard::Key key = SDL::Layout::keyAtF(&fp); + if (event.type == SDL_MOUSEBUTTONDOWN) { + SDL_Point p; + p.x = event.button.x; + p.y = event.button.y; + Keyboard::Key key = SDL::Layout::keyAt(&p); if (key == Keyboard::Key::None) { return None; } diff --git a/ion/src/sdl/layout.cpp b/ion/src/sdl/layout.cpp index df06e18c0..e6a3cbcee 100644 --- a/ion/src/sdl/layout.cpp +++ b/ion/src/sdl/layout.cpp @@ -7,24 +7,27 @@ namespace Layout { #define X(x) ((x)/(1250.0f)) #define Y(y) ((y)/(2100.0f)) -static int sWidth = 0; -static int sHeight = 0; +static SDL_Rect sFrame; static void makeAbsolute(SDL_FRect * f, SDL_Rect * r) { - r->x = f->x * sWidth; - r->y = f->y * sHeight; - r->w = f->w * sWidth; - r->h = f->h * sHeight; + r->x = f->x * sFrame.w + sFrame.x; + r->y = f->y * sFrame.h + sFrame.y; + r->w = f->w * sFrame.w; + r->h = f->h * sFrame.h; } -static void makeAbsolute(SDL_FPoint * f, SDL_Point * p) { - p->x = f->x * sWidth; - p->y = f->y * sHeight; +void setFrame(SDL_Rect * rect) { + sFrame.x = rect->x; + sFrame.y = rect->y; + sFrame.w = rect->w; + sFrame.h = rect->h; } -void setSize(int width, int height) { - sWidth = width; - sHeight = height; +void getAreaOfInterest(SDL_FRect * fRect) { + fRect->x = X(237); + fRect->y = Y(83); + fRect->w = X(776); + fRect->h = Y(1733); } void getScreenRect(SDL_Rect * rect) { @@ -36,12 +39,6 @@ void getScreenRect(SDL_Rect * rect) { makeAbsolute(&fRect, rect); } -Keyboard::Key keyAtF(SDL_FPoint * f) { - SDL_Point p; - makeAbsolute(f, &p); - return keyAt(&p); -} - Keyboard::Key keyAt(SDL_Point * p) { for (int i=0; i(windowWidth)/static_cast(windowHeight); + + if (aoiRatio < windowRatio) { + // Area of interest is wider than the window (aoe is 16:9, window is 4:3) + // There will be "black bars" above and below + sLayoutRect.w = static_cast(windowWidth) / areaOfInterest.w; + sLayoutRect.x = - areaOfInterest.x * sLayoutRect.w; // Compensate the + sLayoutRect.h = sLayoutRect.w / windowRatio; + sLayoutRect.y = - areaOfInterest.y * sLayoutRect.h / 2; // Center vertically + } else { + sLayoutRect.h = static_cast(windowHeight) / areaOfInterest.h; + sLayoutRect.y = - areaOfInterest.y * sLayoutRect.h; // Compensate, align left + sLayoutRect.w = sLayoutRect.h * windowRatio; + sLayoutRect.x = - areaOfInterest.x * sLayoutRect.w / 2; // Center horizontally + // Area of interest is taller than the window + } + + Layout::setFrame(&sLayoutRect); } static void blitBackground(SDL_Rect * rect) { @@ -68,7 +91,7 @@ void refresh() { SDL_Rect screenRect; Layout::getScreenRect(&screenRect); - blitBackground(nullptr); + blitBackground(&sLayoutRect); Display::blit(sSurface, &screenRect); SDL_UpdateWindowSurface(sWindow); } From 22a8683b8196933c7745242e69424d8e20ce19ab Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Tue, 26 Feb 2019 12:06:54 +0100 Subject: [PATCH 03/78] [ion/sdl] Use the Texture API --- ion/src/sdl/android/images.cpp | 30 +++++++++++---------------- ion/src/sdl/display.cpp | 33 ++++++++++++++++++++---------- ion/src/sdl/display.h | 4 ++-- ion/src/sdl/images.h | 2 +- ion/src/sdl/main.cpp | 37 ++++++++++++++++++++-------------- ion/src/sdl/main.h | 1 + 6 files changed, 60 insertions(+), 47 deletions(-) diff --git a/ion/src/sdl/android/images.cpp b/ion/src/sdl/android/images.cpp index 15a3905c6..49d5b87d3 100644 --- a/ion/src/sdl/android/images.cpp +++ b/ion/src/sdl/android/images.cpp @@ -4,7 +4,7 @@ #include #include -SDL_Surface * loadImage(const char * identifier) { +SDL_Texture * loadImage(SDL_Renderer * renderer, const char * identifier) { JNIEnv * env = static_cast(SDL_AndroidGetJNIEnv()); jobject activity = static_cast(SDL_AndroidGetActivity()); @@ -26,28 +26,22 @@ SDL_Surface * loadImage(const char * identifier) { AndroidBitmap_lockPixels(env, j_bitmap, &bitmapPixels); // TODO: Handle the case where lockPixels fails - SDL_Surface * inputSurface = SDL_CreateRGBSurfaceWithFormatFrom( - bitmapPixels, + SDL_Texture * texture = SDL_CreateTexture( + renderer, + SDL_PIXELFORMAT_ABGR8888, + SDL_TEXTUREACCESS_STATIC, bitmapInfo.width, - bitmapInfo.height, - 32, // BPP. TODO: Infer from pixel format - 4 * bitmapInfo.width, // Pitch. TODO: Infer from pixel format - SDL_PIXELFORMAT_ABGR8888 + bitmapInfo.height ); - SDL_Surface * outputSurface = SDL_CreateRGBSurface( - 0, // Flags. Unused. - bitmapInfo.width, // Width - bitmapInfo.height, // Height - 32, // Bits per pixel - 0, 0, 0, 0 // Default masks for the given depth + SDL_UpdateTexture( + texture, + nullptr, + bitmapPixels, + 4 * bitmapInfo.width ); - SDL_BlitSurface(inputSurface, NULL, outputSurface, NULL); - - SDL_FreeSurface(inputSurface); - AndroidBitmap_unlockPixels(env, j_bitmap); - return outputSurface; + return texture; } diff --git a/ion/src/sdl/display.cpp b/ion/src/sdl/display.cpp index 6c2c5822a..947ada2a2 100644 --- a/ion/src/sdl/display.cpp +++ b/ion/src/sdl/display.cpp @@ -33,25 +33,36 @@ namespace SDL { namespace Display { static SDL_Surface * sFramebufferSurface = nullptr; +static SDL_Texture * sFramebufferTexture = nullptr; -void init() { - sFramebufferSurface = SDL_CreateRGBSurfaceWithFormatFrom( - sPixels, +void init(SDL_Renderer * renderer) { + sFramebufferTexture = SDL_CreateTexture( + renderer, + SDL_PIXELFORMAT_RGB565, + SDL_TEXTUREACCESS_STREAMING, Ion::Display::Width, - Ion::Display::Height, - 16, - Ion::Display::Width * 2, - SDL_PIXELFORMAT_RGB565 + Ion::Display::Height ); } void quit() { - SDL_FreeSurface(sFramebufferSurface); - sFramebufferSurface = nullptr; + SDL_DestroyTexture(sFramebufferTexture); + sFramebufferTexture = nullptr; } -void blit(SDL_Surface * dst, SDL_Rect * rect) { - SDL_BlitScaled(sFramebufferSurface, NULL, dst, rect); +void draw(SDL_Renderer * renderer, SDL_Rect * rect) { +#if 1 + int pitch = 0; + void * pixels = nullptr; + SDL_LockTexture(sFramebufferTexture, nullptr, &pixels, &pitch); + assert(pitch == 2*Ion::Display::Width); + memcpy(pixels, sPixels, sizeof(sPixels)); + SDL_UnlockTexture(sFramebufferTexture); + + SDL_RenderCopy(renderer, sFramebufferTexture, nullptr, rect); +#else + SDL_UpdateTexture(sFramebufferTexture, nullptr, sPixels, 2*Ion::Display::Width); +#endif } } diff --git a/ion/src/sdl/display.h b/ion/src/sdl/display.h index 7d3582304..0734977d1 100644 --- a/ion/src/sdl/display.h +++ b/ion/src/sdl/display.h @@ -8,10 +8,10 @@ namespace Ion { namespace SDL { namespace Display { -void init(); +void init(SDL_Renderer * renderer); void quit(); -void blit(SDL_Surface * dst, SDL_Rect * rect); +void draw(SDL_Renderer * renderer, SDL_Rect * rect); } } diff --git a/ion/src/sdl/images.h b/ion/src/sdl/images.h index 189e42333..ef3e1cc55 100644 --- a/ion/src/sdl/images.h +++ b/ion/src/sdl/images.h @@ -2,4 +2,4 @@ // #include -SDL_Surface * loadImage(const char * identifier); +SDL_Texture * loadImage(SDL_Renderer * renderer, const char * identifier); diff --git a/ion/src/sdl/main.cpp b/ion/src/sdl/main.cpp index 960195784..f080b29f1 100644 --- a/ion/src/sdl/main.cpp +++ b/ion/src/sdl/main.cpp @@ -9,8 +9,8 @@ #include -void Ion::Timing::msleep(unsigned int) { - // Do nothing! +void Ion::Timing::msleep(uint32_t ms) { +//TODO SDL_Delay(ms); } int main(int argc, char * argv[]) { @@ -25,8 +25,8 @@ namespace SDL { namespace Main { static SDL_Window * sWindow = nullptr; -static SDL_Surface * sSurface = nullptr; -static SDL_Surface * sBackgroundSurface = nullptr; +static SDL_Renderer * sRenderer = nullptr; +static SDL_Texture * sBackgroundTexture = nullptr; static SDL_Rect sLayoutRect; void init() { @@ -45,11 +45,15 @@ void init() { SDL_SetWindowFullscreen(sWindow, 0); - sSurface = SDL_GetWindowSurface(sWindow); + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); - Display::init(); + sRenderer = SDL_CreateRenderer(sWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); - sBackgroundSurface = loadImage("background.jpg"); + Display::init(sRenderer); + + sBackgroundTexture = loadImage(sRenderer, "background.jpg"); + + relayout(); } void relayout() { @@ -79,21 +83,24 @@ void relayout() { } Layout::setFrame(&sLayoutRect); -} -static void blitBackground(SDL_Rect * rect) { - SDL_BlitScaled(sBackgroundSurface, NULL, sSurface, rect); + SDL_RenderCopy(sRenderer, sBackgroundTexture, nullptr, &sLayoutRect); + SDL_RenderPresent(sRenderer); + + refresh(); + + //SDL_UpdateWindowSurface(sWindow); } void refresh() { - relayout(); - SDL_Rect screenRect; Layout::getScreenRect(&screenRect); - blitBackground(&sLayoutRect); - Display::blit(sSurface, &screenRect); - SDL_UpdateWindowSurface(sWindow); + SDL_RenderCopy(sRenderer, sBackgroundTexture, nullptr, &sLayoutRect); + + Display::draw(sRenderer, &screenRect); + SDL_RenderPresent(sRenderer); + //SDL_UpdateWindowSurface(sWindow); } } diff --git a/ion/src/sdl/main.h b/ion/src/sdl/main.h index 5362dea02..c6950da6a 100644 --- a/ion/src/sdl/main.h +++ b/ion/src/sdl/main.h @@ -9,6 +9,7 @@ void init(); void quit(); void refresh(); +void relayout(); } } From 2a62f9b8ec132d49e16dd0067b87e09a329a2a09 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 27 Feb 2019 13:43:28 +0100 Subject: [PATCH 04/78] [ion/sdl] Use an areaOfInterest --- ion/src/sdl/layout.cpp | 63 ++++++++++++++++++++++++++++++++++-------- ion/src/sdl/layout.h | 9 +++--- ion/src/sdl/main.cpp | 35 +++++------------------ 3 files changed, 63 insertions(+), 44 deletions(-) diff --git a/ion/src/sdl/layout.cpp b/ion/src/sdl/layout.cpp index e6a3cbcee..e86adcbc4 100644 --- a/ion/src/sdl/layout.cpp +++ b/ion/src/sdl/layout.cpp @@ -7,6 +7,8 @@ namespace Layout { #define X(x) ((x)/(1250.0f)) #define Y(y) ((y)/(2100.0f)) +constexpr SDL_FRect areaOfInterest = {X(237), Y(83), X(776), Y(1733)}; + static SDL_Rect sFrame; static void makeAbsolute(SDL_FRect * f, SDL_Rect * r) { @@ -16,18 +18,50 @@ static void makeAbsolute(SDL_FRect * f, SDL_Rect * r) { r->h = f->h * sFrame.h; } -void setFrame(SDL_Rect * rect) { - sFrame.x = rect->x; - sFrame.y = rect->y; - sFrame.w = rect->w; - sFrame.h = rect->h; -} +void recompute(int width, int height) { + float windowWidth = static_cast(width); + float windowHeight = static_cast(height); -void getAreaOfInterest(SDL_FRect * fRect) { - fRect->x = X(237); - fRect->y = Y(83); - fRect->w = X(776); - fRect->h = Y(1733); + float aoiRatio = (areaOfInterest.w / areaOfInterest.h) * (1250.0f/2100.0f); + float windowRatio = windowWidth/windowHeight; + + if (aoiRatio > windowRatio) { + // Area of interest is wider than the window (e.g. aoe 16:9, window 4:3) + // There will be "black bars" above and below + // We want the areaOfInterest's rect in pixels to be + // aoiInPixels.w = windowWidth + // aoiInPixels.x = 0 + // aoiInPixels.h = windowWidth / (aoiRatio*deviceRatio) + // aoiInPixels.y = (windowHeight - aoiInPixels.h)/2; + // But we also know that + // aoiInPixels.x = sFrame.x + areaOfInterest.x*sFrame.w + // aoiInPixels.y = sFrame.y + areaOfInterest.y*sFrame.h + // aoiInPixels.w = sFrame.w * areaOfInterest.w + // aoiInPixels.h = sFrame.h * areaOfInterest*h + + sFrame.w = windowWidth / areaOfInterest.w; + sFrame.h = (windowWidth / aoiRatio) / areaOfInterest.h; + sFrame.x = - areaOfInterest.x * sFrame.w; + sFrame.y = (windowHeight - windowWidth/aoiRatio)/2 - areaOfInterest.y * sFrame.h; + } else { + // Area of interest is taller than the window (e.g. window 16:9, aoe 4:3) + // There will be "black bars" on the sides + // We want the areaOfInterest's rect in pixels to be + // aoiInPixels.h = windowHeight + // aoiInPixels.y = 0 + // aoiInPixels.x = windowHeight * aoiRatio + // aoiInPixels.w = (windowWidth - aoiInPixels.w)/2; + // But we also know that + // aoiInPixels.x = sFrame.x + areaOfInterest.x*sFrame.w + // aoiInPixels.y = sFrame.y + areaOfInterest.y*sFrame.h + // aoiInPixels.w = sFrame.w * areaOfInterest.w + // aoiInPixels.h = sFrame.h * areaOfInterest*h + + sFrame.h = windowHeight / areaOfInterest.h; + sFrame.w = (windowHeight * aoiRatio) / areaOfInterest.w; + sFrame.y = - areaOfInterest.y * sFrame.h; + sFrame.x = (windowWidth - windowHeight*aoiRatio)/2 - areaOfInterest.x * sFrame.w; + } } void getScreenRect(SDL_Rect * rect) { @@ -39,6 +73,13 @@ void getScreenRect(SDL_Rect * rect) { makeAbsolute(&fRect, rect); } +void getBackgroundRect(SDL_Rect * rect) { + rect->x = sFrame.x; + rect->y = sFrame.y; + rect->w = sFrame.w; + rect->h = sFrame.h; +} + Keyboard::Key keyAt(SDL_Point * p) { for (int i=0; i(windowWidth)/static_cast(windowHeight); - - if (aoiRatio < windowRatio) { - // Area of interest is wider than the window (aoe is 16:9, window is 4:3) - // There will be "black bars" above and below - sLayoutRect.w = static_cast(windowWidth) / areaOfInterest.w; - sLayoutRect.x = - areaOfInterest.x * sLayoutRect.w; // Compensate the - sLayoutRect.h = sLayoutRect.w / windowRatio; - sLayoutRect.y = - areaOfInterest.y * sLayoutRect.h / 2; // Center vertically - } else { - sLayoutRect.h = static_cast(windowHeight) / areaOfInterest.h; - sLayoutRect.y = - areaOfInterest.y * sLayoutRect.h; // Compensate, align left - sLayoutRect.w = sLayoutRect.h * windowRatio; - sLayoutRect.x = - areaOfInterest.x * sLayoutRect.w / 2; // Center horizontally - // Area of interest is taller than the window - } - - Layout::setFrame(&sLayoutRect); - - SDL_RenderCopy(sRenderer, sBackgroundTexture, nullptr, &sLayoutRect); + SDL_RenderCopy(sRenderer, sBackgroundTexture, nullptr, &backgroundRect); SDL_RenderPresent(sRenderer); refresh(); @@ -95,12 +74,12 @@ void relayout() { void refresh() { SDL_Rect screenRect; Layout::getScreenRect(&screenRect); + SDL_Rect backgroundRect; + Layout::getBackgroundRect(&backgroundRect); - SDL_RenderCopy(sRenderer, sBackgroundTexture, nullptr, &sLayoutRect); - + SDL_RenderCopy(sRenderer, sBackgroundTexture, nullptr, &backgroundRect); Display::draw(sRenderer, &screenRect); SDL_RenderPresent(sRenderer); - //SDL_UpdateWindowSurface(sWindow); } } From 72a8ab7461adc201f022ff35578b56f9737bbcca Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 27 Feb 2019 13:44:00 +0100 Subject: [PATCH 05/78] [ion/sdl] Implement msleep --- ion/src/sdl/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ion/src/sdl/main.cpp b/ion/src/sdl/main.cpp index 54b44061c..31abfe63f 100644 --- a/ion/src/sdl/main.cpp +++ b/ion/src/sdl/main.cpp @@ -10,7 +10,7 @@ #include void Ion::Timing::msleep(uint32_t ms) { -//TODO SDL_Delay(ms); + SDL_Delay(ms); } int main(int argc, char * argv[]) { From abc204f06aacae58836ee1dbad45782bf30132a5 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 27 Feb 2019 14:43:20 +0100 Subject: [PATCH 06/78] [ion/sdl] Use key centers to compute keypresses --- ion/src/sdl/layout.cpp | 170 ++++++++++++++++++++++------------------- ion/src/sdl/layout.h | 1 - 2 files changed, 90 insertions(+), 81 deletions(-) diff --git a/ion/src/sdl/layout.cpp b/ion/src/sdl/layout.cpp index e86adcbc4..9268e195f 100644 --- a/ion/src/sdl/layout.cpp +++ b/ion/src/sdl/layout.cpp @@ -4,25 +4,34 @@ namespace Ion { namespace SDL { namespace Layout { -#define X(x) ((x)/(1250.0f)) -#define Y(y) ((y)/(2100.0f)) +static constexpr int backgroundWidth = 1160; +static constexpr int backgroundHeight = 2220; -constexpr SDL_FRect areaOfInterest = {X(237), Y(83), X(776), Y(1733)}; +static constexpr float X(int x) { return static_cast(x)/static_cast(backgroundWidth); } +static constexpr float Y(int y) { return static_cast(y)/static_cast(backgroundHeight); } + +static constexpr SDL_FRect areaOfInterest = {X(110), Y(30), X(940), Y(2150)}; +static constexpr SDL_FRect screenRect = {X(191), Y(189), X(779), Y(582)}; static SDL_Rect sFrame; -static void makeAbsolute(SDL_FRect * f, SDL_Rect * r) { +static void makeAbsolute(const SDL_FRect * f, SDL_Rect * r) { r->x = f->x * sFrame.w + sFrame.x; r->y = f->y * sFrame.h + sFrame.y; r->w = f->w * sFrame.w; r->h = f->h * sFrame.h; } +static void makeAbsolute(const SDL_FPoint * f, SDL_Point * p) { + p->x = f->x * sFrame.w + sFrame.x; + p->y = f->y * sFrame.h + sFrame.y; +} + void recompute(int width, int height) { float windowWidth = static_cast(width); float windowHeight = static_cast(height); - float aoiRatio = (areaOfInterest.w / areaOfInterest.h) * (1250.0f/2100.0f); + float aoiRatio = (areaOfInterest.w / areaOfInterest.h) * (static_cast(backgroundWidth)/static_cast(backgroundHeight)); float windowRatio = windowWidth/windowHeight; if (aoiRatio > windowRatio) { @@ -60,17 +69,12 @@ void recompute(int width, int height) { sFrame.h = windowHeight / areaOfInterest.h; sFrame.w = (windowHeight * aoiRatio) / areaOfInterest.w; sFrame.y = - areaOfInterest.y * sFrame.h; - sFrame.x = (windowWidth - windowHeight*aoiRatio)/2 - areaOfInterest.x * sFrame.w; + sFrame.x = (windowWidth - windowHeight*aoiRatio)/2.0f - areaOfInterest.x * sFrame.w; } } void getScreenRect(SDL_Rect * rect) { - SDL_FRect fRect; - fRect.x = X(310); - fRect.y = Y(215); - fRect.w = X(640); - fRect.h = Y(485); - makeAbsolute(&fRect, rect); + makeAbsolute(&screenRect, rect); } void getBackgroundRect(SDL_Rect * rect) { @@ -80,78 +84,84 @@ void getBackgroundRect(SDL_Rect * rect) { rect->h = sFrame.h; } -Keyboard::Key keyAt(SDL_Point * p) { - for (int i=0; i= 0); assert(validKeyIndex < Keyboard::NumberOfValidKeys); - SDL_FRect rectForKey[Keyboard::NumberOfValidKeys] = { - {X(250), Y(880), X(85), Y(75) }, // A1, Left - {X(330), Y(795), X(75), Y(85) }, // A2, Up - {X(330), Y(950), X(75), Y(85) }, // A3, Down - {X(400), Y(880), X(85), Y(75) }, // A4, Right - {X(765), Y(855), X(110), Y(110)}, // A5, OK - {X(900), Y(855), X(110), Y(110)}, // A6, Back + makeAbsolute(&sKeyCenters[validKeyIndex], point); +} - {X(565), Y(815), X(130), Y(85)}, // B1, Home - {X(565), Y(920), X(130), Y(85)}, // B2, Power - - {X(255), Y(1066), X(110), Y(75)}, // C1, Shift - {X(385), Y(1066), X(110), Y(75)}, // C2, Alpha - {X(512), Y(1066), X(110), Y(75)}, // C3, xnt - {X(638), Y(1066), X(110), Y(75)}, // C4, var - {X(768), Y(1066), X(110), Y(75)}, // C5, toolbox - {X(895), Y(1066), X(110), Y(75)}, // C6, Delete - - {X(255), Y(1170), X(110), Y(75)}, // D1, exp - {X(385), Y(1170), X(110), Y(75)}, // D2, ln - {X(512), Y(1170), X(110), Y(75)}, // D3, log - {X(638), Y(1170), X(110), Y(75)}, // D4, i - {X(768), Y(1170), X(110), Y(75)}, // D5, comma - {X(895), Y(1170), X(110), Y(75)}, // D6, power - - {X(255), Y(1272), X(110), Y(75)}, // E1, sin - {X(385), Y(1272), X(110), Y(75)}, // E2, cos - {X(512), Y(1272), X(110), Y(75)}, // E3, tan - {X(638), Y(1272), X(110), Y(75)}, // E4, pi - {X(768), Y(1272), X(110), Y(75)}, // E5, sqrt - {X(895), Y(1272), X(110), Y(75)}, // E6, square - - {X(255), Y(1376), X(130), X(85)}, // F1, 7 - {X(408), Y(1376), X(130), X(85)}, // F2, 8 - {X(564), Y(1376), X(130), X(85)}, // F3, 9 - {X(718), Y(1376), X(130), X(85)}, // F4, ( - {X(872), Y(1376), X(130), X(85)}, // F5, ) - - {X(255), Y(1490), X(130), X(85)}, // G1, 4 - {X(408), Y(1490), X(130), X(85)}, // G2, 5 - {X(564), Y(1490), X(130), X(85)}, // G3, 6 - {X(718), Y(1490), X(130), X(85)}, // G4, * - {X(872), Y(1490), X(130), X(85)}, // G5, / - - {X(255), Y(1605), X(130), X(85)}, // H1, 1 - {X(408), Y(1605), X(130), X(85)}, // H2, 2 - {X(564), Y(1605), X(130), X(85)}, // H3, 3 - {X(718), Y(1605), X(130), X(85)}, // H4, + - {X(872), Y(1605), X(130), X(85)}, // H5, - - - {X(255), Y(1718), X(130), X(85)}, // I1, 0 - {X(408), Y(1718), X(130), X(85)}, // I2, . - {X(564), Y(1718), X(130), X(85)}, // I3, x10 - {X(718), Y(1718), X(130), X(85)}, // I4, Ans - {X(872), Y(1718), X(130), X(85)}, // I5, EXE - }; - - makeAbsolute(&rectForKey[validKeyIndex], rect); +Keyboard::Key keyAt(SDL_Point * p) { + int minSquaredDistance = -1; + Keyboard::Key nearestKey = Keyboard::Key::None; + for (int i=0; ix; + int dy = keyCenter.y - p->y; + int squaredDistance = dx*dx + dy*dy; + if (squaredDistance < minSquaredDistance || minSquaredDistance < 0) { + minSquaredDistance = squaredDistance; + nearestKey = Keyboard::ValidKeys[i]; + } + } + return nearestKey; } } diff --git a/ion/src/sdl/layout.h b/ion/src/sdl/layout.h index c8a356d2b..765a79648 100644 --- a/ion/src/sdl/layout.h +++ b/ion/src/sdl/layout.h @@ -12,7 +12,6 @@ void recompute(int width, int height); void getScreenRect(SDL_Rect * rect); void getBackgroundRect(SDL_Rect * rect); -void getKeyRect(int validKeyIndex, SDL_Rect * rect); Ion::Keyboard::Key keyAt(SDL_Point * p); From b9bb730a9d61867db54d6b4fdd1168f08b9e6766 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 27 Feb 2019 17:44:58 +0100 Subject: [PATCH 07/78] [ion/sdl] Sort the build system --- build/platform.sdl.android.mak | 5 + build/platform.sdl.mak | 16 +++ build/targets.sdl.mak | 33 ++++++ build/toolchain.android.mak | 41 +++++++ ion/src/sdl/Makefile | 14 ++- .../app/src/main/AndroidManifest.xml | 104 +++++++----------- ion/src/sdl/{ => shared}/display.cpp | 0 ion/src/sdl/{ => shared}/display.h | 0 ion/src/sdl/{ => shared}/events.cpp | 0 ion/src/sdl/{ => shared}/images.h | 0 ion/src/sdl/{ => shared}/keyboard.cpp | 0 ion/src/sdl/{ => shared}/keyboard.h | 0 ion/src/sdl/{ => shared}/layout.cpp | 0 ion/src/sdl/{ => shared}/layout.h | 0 ion/src/sdl/{ => shared}/main.cpp | 0 ion/src/sdl/{ => shared}/main.h | 0 16 files changed, 142 insertions(+), 71 deletions(-) create mode 100644 build/platform.sdl.android.mak create mode 100644 build/platform.sdl.mak create mode 100644 build/targets.sdl.mak create mode 100644 build/toolchain.android.mak rename ion/src/sdl/{ => shared}/display.cpp (100%) rename ion/src/sdl/{ => shared}/display.h (100%) rename ion/src/sdl/{ => shared}/events.cpp (100%) rename ion/src/sdl/{ => shared}/images.h (100%) rename ion/src/sdl/{ => shared}/keyboard.cpp (100%) rename ion/src/sdl/{ => shared}/keyboard.h (100%) rename ion/src/sdl/{ => shared}/layout.cpp (100%) rename ion/src/sdl/{ => shared}/layout.h (100%) rename ion/src/sdl/{ => shared}/main.cpp (100%) rename ion/src/sdl/{ => shared}/main.h (100%) diff --git a/build/platform.sdl.android.mak b/build/platform.sdl.android.mak new file mode 100644 index 000000000..334ea68b6 --- /dev/null +++ b/build/platform.sdl.android.mak @@ -0,0 +1,5 @@ +TOOLCHAIN ?= android +EXE = so + +LDFLAGS += -L$(SDL_PATH)/build/android/lib/$(NDK_ABI) +LDFLAGS += -lSDL2 -ljnigraphics -llog diff --git a/build/platform.sdl.mak b/build/platform.sdl.mak new file mode 100644 index 000000000..ab070854e --- /dev/null +++ b/build/platform.sdl.mak @@ -0,0 +1,16 @@ +USE_LIBA = 0 +EPSILON_ONBOARDING_APP = 0 +SFLAGS += -fPIE +EXE = elf + +ifndef SDL_PATH + $(error SDL_PATH should point to the SDL2 sources. A recent snapshot is required.) +endif + +SFLAGS += -I$(SDL_PATH)/include + +ifndef MODEL + $(error MODEL should be defined) +endif + +include build/platform.sdl.$(MODEL).mak diff --git a/build/targets.sdl.mak b/build/targets.sdl.mak new file mode 100644 index 000000000..2c81deee4 --- /dev/null +++ b/build/targets.sdl.mak @@ -0,0 +1,33 @@ +# CAUTION: All the following builds have to happen in a sequential order so we +# cannot use standard Make dependencies +.PHONY: android-apk-dependencies +android-apk-dependencies: + rm -rf ion/src/sdl/android/com.numworks.calculator/app/libs + # First, build libsdl + cd $(SDL_PATH) && build-scripts/androidbuildlibs.sh + cp -r $(SDL_PATH)/build/android/lib ion/src/sdl/android/com.numworks.calculator/app/libs + # Then build epsilon for armv7 + rm -f ion/src/sdl/android/com.numworks.calculator/app/libs/armeabi-v7a/libepsilon.so + $(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/armeabi-v7a/libepsilon.so + # Then for arm64 + rm -f ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so + $(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so + # Then for x86 + rm -f ion/src/sdl/android/com.numworks.calculator/app/libs/x86/libepsilon.so + $(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/x86/libepsilon.so + # And eventually for x86_64 + rm -f ion/src/sdl/android/com.numworks.calculator/app/libs/x86_64/libepsilon.so + $(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/x86_64/libepsilon.so + # Last but not least, copy the sources from SDL + cp -r $(SDL_PATH)/android-project/app/src/main/java/org ion/src/sdl/android/com.numworks.calculator/app/src/main/java + +epsilon.apk: + #rm ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so + #$(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so + cd ion/src/sdl/android/com.numworks.calculator ; ANDROID_HOME=/usr/local/android ./gradlew assembleRelease + cp ion/src/sdl/android/com.numworks.calculator/app/build/outputs/apk/release/app-release-unsigned.apk epsilon.apk + +epsilon_run: + #rm ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so + #$(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so + cd ion/src/sdl/android/com.numworks.calculator ; ANDROID_HOME=/usr/local/android ./gradlew installDebug diff --git a/build/toolchain.android.mak b/build/toolchain.android.mak new file mode 100644 index 000000000..98aa981ee --- /dev/null +++ b/build/toolchain.android.mak @@ -0,0 +1,41 @@ +NDK_PATH ?= /usr/local/android/ndk-bundle +NDK_HOST_TAG ?= darwin-x86_64 + +NDK_TOOLCHAIN_PATH = $(NDK_PATH)/toolchains/llvm/prebuilt/$(NDK_HOST_TAG)/bin + +NDK_ABI ?= arm64-v8a + +ifeq ($(NDK_ABI),armeabi-v7a) + NDK_TARGET = armv7a-linux-androideabi +else ifeq ($(NDK_ABI),arm64-v8a) + NDK_TARGET = aarch64-linux-android +else ifeq ($(NDK_ABI),x86) + NDK_TARGET = i686-linux-android +else ifeq ($(NDK_ABI),x86_64) + NDK_TARGET = x86_64-linux-android +else + $(error Unknown NDK_ABI) +endif + +NDK_VERSION ?= 21 +CC = $(NDK_TOOLCHAIN_PATH)/$(NDK_TARGET)$(NDK_VERSION)-clang +CXX = $(NDK_TOOLCHAIN_PATH)/$(NDK_TARGET)$(NDK_VERSION)-clang++ +LD = $(NDK_TOOLCHAIN_PATH)/$(NDK_TARGET)$(NDK_VERSION)-clang++ + +SFLAGS += -fPIC + +#GDB = arm-none-eabi-gdb +#OBJCOPY = arm-none-eabi-objcopy +#SIZE = arm-none-eabi-size + + +#SFLAGS += -fdata-sections -ffunction-sections +#LDFLAGS += -Wl,--gc-sections +#SFLAGS += -fvisibility=hidden +#LDFLAGS += -Wl,-export-symbols,ion/src/sdl/export_list.txt + +LDFLAGS += -shared +LDFLAGS += -static-libstdc++ +#LDFLAGS += -Wl,-z,defs # Warn on missing symbols when linking the dynamic library + +#LDFLAGS += -lsdl diff --git a/ion/src/sdl/Makefile b/ion/src/sdl/Makefile index 07368a953..8917a7692 100644 --- a/ion/src/sdl/Makefile +++ b/ion/src/sdl/Makefile @@ -16,7 +16,7 @@ objs += $(addprefix ion/src/shared/, \ dummy/usb.o \ ) -objs += $(addprefix ion/src/sdl/, \ +objs += $(addprefix ion/src/sdl/shared/, \ display.o \ events.o \ main.o \ @@ -24,10 +24,12 @@ objs += $(addprefix ion/src/sdl/, \ layout.o \ ) -objs += $(addprefix ion/src/sdl/android/, \ - images.o \ -) +include ion/src/sdl/$(MODEL)/Makefile -SFLAGS += -I/Users/romain/Sources/SDL/include +#objs += $(addprefix ion/src/sdl/android/, \ +# images.o \ +#) -include ion/src/sdl/external/Makefile +#SFLAGS += -I/Users/romain/Sources/SDL/include + +#include ion/src/sdl/external/Makefile diff --git a/ion/src/sdl/android/com.numworks.calculator/app/src/main/AndroidManifest.xml b/ion/src/sdl/android/com.numworks.calculator/app/src/main/AndroidManifest.xml index afabdf7b4..d5f616716 100644 --- a/ion/src/sdl/android/com.numworks.calculator/app/src/main/AndroidManifest.xml +++ b/ion/src/sdl/android/com.numworks.calculator/app/src/main/AndroidManifest.xml @@ -1,75 +1,49 @@ - + package="com.numworks.calculator" + android:versionCode="1" + android:versionName="1.0" + android:installLocation="auto"> - - + + - - + + - - + + - - + + - - - - + + + + - - - - - - - - - - - - - - - - - + + + + + + + + diff --git a/ion/src/sdl/display.cpp b/ion/src/sdl/shared/display.cpp similarity index 100% rename from ion/src/sdl/display.cpp rename to ion/src/sdl/shared/display.cpp diff --git a/ion/src/sdl/display.h b/ion/src/sdl/shared/display.h similarity index 100% rename from ion/src/sdl/display.h rename to ion/src/sdl/shared/display.h diff --git a/ion/src/sdl/events.cpp b/ion/src/sdl/shared/events.cpp similarity index 100% rename from ion/src/sdl/events.cpp rename to ion/src/sdl/shared/events.cpp diff --git a/ion/src/sdl/images.h b/ion/src/sdl/shared/images.h similarity index 100% rename from ion/src/sdl/images.h rename to ion/src/sdl/shared/images.h diff --git a/ion/src/sdl/keyboard.cpp b/ion/src/sdl/shared/keyboard.cpp similarity index 100% rename from ion/src/sdl/keyboard.cpp rename to ion/src/sdl/shared/keyboard.cpp diff --git a/ion/src/sdl/keyboard.h b/ion/src/sdl/shared/keyboard.h similarity index 100% rename from ion/src/sdl/keyboard.h rename to ion/src/sdl/shared/keyboard.h diff --git a/ion/src/sdl/layout.cpp b/ion/src/sdl/shared/layout.cpp similarity index 100% rename from ion/src/sdl/layout.cpp rename to ion/src/sdl/shared/layout.cpp diff --git a/ion/src/sdl/layout.h b/ion/src/sdl/shared/layout.h similarity index 100% rename from ion/src/sdl/layout.h rename to ion/src/sdl/shared/layout.h diff --git a/ion/src/sdl/main.cpp b/ion/src/sdl/shared/main.cpp similarity index 100% rename from ion/src/sdl/main.cpp rename to ion/src/sdl/shared/main.cpp diff --git a/ion/src/sdl/main.h b/ion/src/sdl/shared/main.h similarity index 100% rename from ion/src/sdl/main.h rename to ion/src/sdl/shared/main.h From bfda5808823b1b0624ce214cca3bef36d3ff7433 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 27 Feb 2019 17:48:15 +0100 Subject: [PATCH 08/78] [ion/sdl] Add a build/targets file --- build/targets.sdl.android.mak | 33 +++++++++++++++++++++++++++++++++ build/targets.sdl.mak | 34 +--------------------------------- ion/src/sdl/android/Makefile | 11 +++++++++++ 3 files changed, 45 insertions(+), 33 deletions(-) create mode 100644 build/targets.sdl.android.mak create mode 100644 ion/src/sdl/android/Makefile diff --git a/build/targets.sdl.android.mak b/build/targets.sdl.android.mak new file mode 100644 index 000000000..a702ef525 --- /dev/null +++ b/build/targets.sdl.android.mak @@ -0,0 +1,33 @@ +# CAUTION: All the following builds have to happen in a sequential order so we +# cannot use standard Make dependencies +.PHONY: dependencies +dependencies: + rm -rf ion/src/sdl/android/com.numworks.calculator/app/libs + # First, build libsdl + cd $(SDL_PATH) && build-scripts/androidbuildlibs.sh + cp -r $(SDL_PATH)/build/android/lib ion/src/sdl/android/com.numworks.calculator/app/libs + # Then build epsilon for armv7 + rm -f ion/src/sdl/android/com.numworks.calculator/app/libs/armeabi-v7a/libepsilon.so + $(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/armeabi-v7a/libepsilon.so + # Then for arm64 + rm -f ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so + $(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so + # Then for x86 + rm -f ion/src/sdl/android/com.numworks.calculator/app/libs/x86/libepsilon.so + $(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/x86/libepsilon.so + # And eventually for x86_64 + rm -f ion/src/sdl/android/com.numworks.calculator/app/libs/x86_64/libepsilon.so + $(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/x86_64/libepsilon.so + # Last but not least, copy the sources from SDL + cp -r $(SDL_PATH)/android-project/app/src/main/java/org ion/src/sdl/android/com.numworks.calculator/app/src/main/java + +epsilon.apk: + #rm ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so + #$(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so + cd ion/src/sdl/android/com.numworks.calculator ; ANDROID_HOME=/usr/local/android ./gradlew assembleRelease + cp ion/src/sdl/android/com.numworks.calculator/app/build/outputs/apk/release/app-release-unsigned.apk epsilon.apk + +epsilon_run: + #rm ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so + #$(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so + cd ion/src/sdl/android/com.numworks.calculator ; ANDROID_HOME=/usr/local/android ./gradlew installDebug diff --git a/build/targets.sdl.mak b/build/targets.sdl.mak index 2c81deee4..54afd6954 100644 --- a/build/targets.sdl.mak +++ b/build/targets.sdl.mak @@ -1,33 +1 @@ -# CAUTION: All the following builds have to happen in a sequential order so we -# cannot use standard Make dependencies -.PHONY: android-apk-dependencies -android-apk-dependencies: - rm -rf ion/src/sdl/android/com.numworks.calculator/app/libs - # First, build libsdl - cd $(SDL_PATH) && build-scripts/androidbuildlibs.sh - cp -r $(SDL_PATH)/build/android/lib ion/src/sdl/android/com.numworks.calculator/app/libs - # Then build epsilon for armv7 - rm -f ion/src/sdl/android/com.numworks.calculator/app/libs/armeabi-v7a/libepsilon.so - $(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/armeabi-v7a/libepsilon.so - # Then for arm64 - rm -f ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so - $(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so - # Then for x86 - rm -f ion/src/sdl/android/com.numworks.calculator/app/libs/x86/libepsilon.so - $(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/x86/libepsilon.so - # And eventually for x86_64 - rm -f ion/src/sdl/android/com.numworks.calculator/app/libs/x86_64/libepsilon.so - $(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/x86_64/libepsilon.so - # Last but not least, copy the sources from SDL - cp -r $(SDL_PATH)/android-project/app/src/main/java/org ion/src/sdl/android/com.numworks.calculator/app/src/main/java - -epsilon.apk: - #rm ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so - #$(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so - cd ion/src/sdl/android/com.numworks.calculator ; ANDROID_HOME=/usr/local/android ./gradlew assembleRelease - cp ion/src/sdl/android/com.numworks.calculator/app/build/outputs/apk/release/app-release-unsigned.apk epsilon.apk - -epsilon_run: - #rm ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so - #$(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so - cd ion/src/sdl/android/com.numworks.calculator ; ANDROID_HOME=/usr/local/android ./gradlew installDebug +include build/targets.sdl.$(MODEL).mak diff --git a/ion/src/sdl/android/Makefile b/ion/src/sdl/android/Makefile new file mode 100644 index 000000000..8c140d7eb --- /dev/null +++ b/ion/src/sdl/android/Makefile @@ -0,0 +1,11 @@ +objs += $(addprefix ion/src/sdl/android/, \ + images.o \ +) + +ANDROID_LIB_PATH = ion/src/sdl/android/com.numworks.calculator/app/libs + +$(ANDROID_LIB_PATH)/%/libepsilon.so: +ion/src/sdl/android/com.numworks.calculator/app/libs/%/libepsilon.so: + $(Q) $(MAKE) MODEL=android NDK_ABI=$* clean + $(Q) $(MAKE) MODEL=android NDK_ABI=$* epsilon.so + $(Q) mv epsilon.so $@ From 8b27785a077c77624a0ace5a739f10b777e4639f Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 27 Feb 2019 18:00:19 +0100 Subject: [PATCH 09/78] [ion/sdl] Clean the Android app structure --- ion/src/sdl/android/build.gradle | 55 +++++++++++++++++++ ion/src/sdl/android/proguard-rules.pro | 17 ++++++ .../app/src/main => src}/AndroidManifest.xml | 0 ion/src/sdl/android/{ => src/cpp}/images.cpp | 0 .../numworks/calculator/EpsilonActivity.java | 0 5 files changed, 72 insertions(+) create mode 100644 ion/src/sdl/android/build.gradle create mode 100644 ion/src/sdl/android/proguard-rules.pro rename ion/src/sdl/android/{com.numworks.calculator/app/src/main => src}/AndroidManifest.xml (100%) rename ion/src/sdl/android/{ => src/cpp}/images.cpp (100%) rename ion/src/sdl/android/{com.numworks.calculator/app/src/main => src}/java/com/numworks/calculator/EpsilonActivity.java (100%) diff --git a/ion/src/sdl/android/build.gradle b/ion/src/sdl/android/build.gradle new file mode 100644 index 000000000..6b99d09cf --- /dev/null +++ b/ion/src/sdl/android/build.gradle @@ -0,0 +1,55 @@ +buildscript { + repositories { + jcenter() + google() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.2.0' + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + jcenter() + google() + } +} + +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + defaultConfig { + applicationId "com.numworks.calculator" + minSdkVersion 16 + targetSdkVersion 26 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + sourceSets{ + main { + manifest.srcFile 'src/AndroidManifest.xml' + res.srcDir 'src/res' + java.srcDir 'src' + jniLibs.srcDir 'libs' + assets.srcDir '../../../assets' + } + } + + lintOptions { + abortOnError false + } +} + +dependencies { + implementation fileTree(include: ['*.jar'], dir: 'libs') + implementation "androidx.appcompat:appcompat:1.0.0" +} diff --git a/ion/src/sdl/android/proguard-rules.pro b/ion/src/sdl/android/proguard-rules.pro new file mode 100644 index 000000000..eaf0e916c --- /dev/null +++ b/ion/src/sdl/android/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in [sdk]/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/ion/src/sdl/android/com.numworks.calculator/app/src/main/AndroidManifest.xml b/ion/src/sdl/android/src/AndroidManifest.xml similarity index 100% rename from ion/src/sdl/android/com.numworks.calculator/app/src/main/AndroidManifest.xml rename to ion/src/sdl/android/src/AndroidManifest.xml diff --git a/ion/src/sdl/android/images.cpp b/ion/src/sdl/android/src/cpp/images.cpp similarity index 100% rename from ion/src/sdl/android/images.cpp rename to ion/src/sdl/android/src/cpp/images.cpp diff --git a/ion/src/sdl/android/com.numworks.calculator/app/src/main/java/com/numworks/calculator/EpsilonActivity.java b/ion/src/sdl/android/src/java/com/numworks/calculator/EpsilonActivity.java similarity index 100% rename from ion/src/sdl/android/com.numworks.calculator/app/src/main/java/com/numworks/calculator/EpsilonActivity.java rename to ion/src/sdl/android/src/java/com/numworks/calculator/EpsilonActivity.java From 9fc81d9a3f4615c9d04a0561e86b140635f982dd Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 27 Feb 2019 22:18:24 +0100 Subject: [PATCH 10/78] [ion/sdl] Build from the shortened Android directory --- .gitignore | 5 ++++- build/targets.sdl.android.mak | 28 ++++++++++++++-------------- ion/src/sdl/android/Makefile | 6 +++--- ion/src/sdl/android/build.gradle | 2 +- 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index a49e8c8c0..0d3f04cd3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ # Quiz output quiz/src/symbols.c -build +# Ignore SDL files +ion/src/sdl/android/build +ion/src/sdl/android/libs +ion/src/sdl/android/src/java/org diff --git a/build/targets.sdl.android.mak b/build/targets.sdl.android.mak index a702ef525..8c865bb27 100644 --- a/build/targets.sdl.android.mak +++ b/build/targets.sdl.android.mak @@ -2,32 +2,32 @@ # cannot use standard Make dependencies .PHONY: dependencies dependencies: - rm -rf ion/src/sdl/android/com.numworks.calculator/app/libs + rm -rf ion/src/sdl/android/libs # First, build libsdl cd $(SDL_PATH) && build-scripts/androidbuildlibs.sh - cp -r $(SDL_PATH)/build/android/lib ion/src/sdl/android/com.numworks.calculator/app/libs + cp -r $(SDL_PATH)/build/android/lib ion/src/sdl/android/libs # Then build epsilon for armv7 - rm -f ion/src/sdl/android/com.numworks.calculator/app/libs/armeabi-v7a/libepsilon.so - $(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/armeabi-v7a/libepsilon.so + rm -f ion/src/sdl/android/libs/armeabi-v7a/libepsilon.so + $(MAKE) ion/src/sdl/android/libs/armeabi-v7a/libepsilon.so # Then for arm64 - rm -f ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so - $(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so + rm -f ion/src/sdl/android/libs/arm64-v8a/libepsilon.so + $(MAKE) ion/src/sdl/android/libs/arm64-v8a/libepsilon.so # Then for x86 - rm -f ion/src/sdl/android/com.numworks.calculator/app/libs/x86/libepsilon.so - $(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/x86/libepsilon.so + rm -f ion/src/sdl/android/libs/x86/libepsilon.so + $(MAKE) ion/src/sdl/android/libs/x86/libepsilon.so # And eventually for x86_64 - rm -f ion/src/sdl/android/com.numworks.calculator/app/libs/x86_64/libepsilon.so - $(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/x86_64/libepsilon.so + rm -f ion/src/sdl/android/libs/x86_64/libepsilon.so + $(MAKE) ion/src/sdl/android/libs/x86_64/libepsilon.so # Last but not least, copy the sources from SDL - cp -r $(SDL_PATH)/android-project/app/src/main/java/org ion/src/sdl/android/com.numworks.calculator/app/src/main/java + cp -r $(SDL_PATH)/android-project/app/src/main/java/org ion/src/sdl/android/src/java epsilon.apk: #rm ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so #$(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so - cd ion/src/sdl/android/com.numworks.calculator ; ANDROID_HOME=/usr/local/android ./gradlew assembleRelease - cp ion/src/sdl/android/com.numworks.calculator/app/build/outputs/apk/release/app-release-unsigned.apk epsilon.apk + cd ion/src/sdl/android ; ANDROID_HOME=/usr/local/android gradle assembleRelease + cp ion/src/sdl/android/build/outputs/apk/release/app-release-unsigned.apk epsilon.apk epsilon_run: #rm ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so #$(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so - cd ion/src/sdl/android/com.numworks.calculator ; ANDROID_HOME=/usr/local/android ./gradlew installDebug + cd ion/src/sdl/android ; ANDROID_HOME=/usr/local/android gradle installDebug diff --git a/ion/src/sdl/android/Makefile b/ion/src/sdl/android/Makefile index 8c140d7eb..d67008e42 100644 --- a/ion/src/sdl/android/Makefile +++ b/ion/src/sdl/android/Makefile @@ -1,11 +1,11 @@ -objs += $(addprefix ion/src/sdl/android/, \ +objs += $(addprefix ion/src/sdl/android/src/cpp/, \ images.o \ ) -ANDROID_LIB_PATH = ion/src/sdl/android/com.numworks.calculator/app/libs +ANDROID_LIB_PATH = ion/src/sdl/android/libs $(ANDROID_LIB_PATH)/%/libepsilon.so: -ion/src/sdl/android/com.numworks.calculator/app/libs/%/libepsilon.so: +ion/src/sdl/android/libs/%/libepsilon.so: $(Q) $(MAKE) MODEL=android NDK_ABI=$* clean $(Q) $(MAKE) MODEL=android NDK_ABI=$* epsilon.so $(Q) mv epsilon.so $@ diff --git a/ion/src/sdl/android/build.gradle b/ion/src/sdl/android/build.gradle index 6b99d09cf..302d69cfb 100644 --- a/ion/src/sdl/android/build.gradle +++ b/ion/src/sdl/android/build.gradle @@ -40,7 +40,7 @@ android { res.srcDir 'src/res' java.srcDir 'src' jniLibs.srcDir 'libs' - assets.srcDir '../../../assets' + assets.srcDir '../assets' } } From fefb8f9927f38a00d17df130f863510bfaa9b7e7 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Thu, 28 Feb 2019 09:28:39 +0100 Subject: [PATCH 11/78] [ion/sdl] Discard unused file --- ion/src/sdl/Makefile | 1 - ion/src/sdl/shared/keyboard.cpp | 63 --------------------------------- ion/src/sdl/shared/keyboard.h | 19 ---------- 3 files changed, 83 deletions(-) delete mode 100644 ion/src/sdl/shared/keyboard.cpp delete mode 100644 ion/src/sdl/shared/keyboard.h diff --git a/ion/src/sdl/Makefile b/ion/src/sdl/Makefile index 8917a7692..ee90789dd 100644 --- a/ion/src/sdl/Makefile +++ b/ion/src/sdl/Makefile @@ -20,7 +20,6 @@ objs += $(addprefix ion/src/sdl/shared/, \ display.o \ events.o \ main.o \ - keyboard.o \ layout.o \ ) diff --git a/ion/src/sdl/shared/keyboard.cpp b/ion/src/sdl/shared/keyboard.cpp deleted file mode 100644 index 7ebf1b8a6..000000000 --- a/ion/src/sdl/shared/keyboard.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "keyboard.h" -#include - -#include - -#include "images.h" - -#define APPNAME "MyApp" - -namespace Ion { -namespace Keyboard { - -State scan() { - return State(0); -} - -} -} - -namespace Ion { -namespace SDL { -namespace Keyboard { - -using namespace Ion::Keyboard; - -static SDL_Rect s_rect; - -void init(SDL_Rect rect) { - s_rect = rect; -} - -static SDL_Rect rectForKey(Key k) { - int i = static_cast(k); - return {i%10, i/10, 10, 10}; -} - -void drawOnSurface(SDL_Surface * surface) { - for (int i=0; iformat, 0xFF, 0x00, 0xFF)); - } -} - -Key keyAt(SDL_Point p) { - __android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "Looking up %dx%d", p.x, p.y); - for (int i=0; i -#include -#include - -namespace Ion { -namespace SDL { -namespace Keyboard { - -void drawOnSurface(SDL_Surface * surface); -Ion::Keyboard::Key keyAt(SDL_Point p); - -} -} -} - -#endif From d3f5407189e1081d28fbef135de0053443bd1c7f Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 6 Mar 2019 20:05:52 +0100 Subject: [PATCH 12/78] [scripts] Give precedence to platform-specific targets --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7bf60b37a..01907f05c 100644 --- a/Makefile +++ b/Makefile @@ -76,6 +76,10 @@ objs = $(call object_for,$(src)) # but allows correct yet optimal incremental builds. -include $(objs:.o=.d) +# Load platform-specific targets +# We include them before the standard ones to give them precedence. +-include scripts/targets.$(PLATFORM).mak + # Define rules for executables # Those can be built directly with make executable.exe as a shortcut. They also # depends on $(objs) @@ -133,4 +137,3 @@ cowsay_%: .PHONY: clena clena: cowsay_CLENA clean --include scripts/targets.$(PLATFORM).mak From bb86435a99935b4008106c570e851c49a896a8be Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 6 Mar 2019 20:06:29 +0100 Subject: [PATCH 13/78] [scripts] .gitignore ignores only the build directory --- .gitignore | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 0d3f04cd3..378eac25d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1 @@ -# Quiz output -quiz/src/symbols.c - -# Ignore SDL files -ion/src/sdl/android/build -ion/src/sdl/android/libs -ion/src/sdl/android/src/java/org +build From bb95deff9784d73695a5bf9edd4d19b6f601c8ee Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 6 Mar 2019 20:09:58 +0100 Subject: [PATCH 14/78] [ion/sdl] Use the newer scripts path --- {build => scripts}/platform.sdl.android.mak | 0 {build => scripts}/platform.sdl.mak | 0 {build => scripts}/targets.sdl.android.mak | 0 {build => scripts}/targets.sdl.mak | 0 {build => scripts}/toolchain.android.mak | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename {build => scripts}/platform.sdl.android.mak (100%) rename {build => scripts}/platform.sdl.mak (100%) rename {build => scripts}/targets.sdl.android.mak (100%) rename {build => scripts}/targets.sdl.mak (100%) rename {build => scripts}/toolchain.android.mak (100%) diff --git a/build/platform.sdl.android.mak b/scripts/platform.sdl.android.mak similarity index 100% rename from build/platform.sdl.android.mak rename to scripts/platform.sdl.android.mak diff --git a/build/platform.sdl.mak b/scripts/platform.sdl.mak similarity index 100% rename from build/platform.sdl.mak rename to scripts/platform.sdl.mak diff --git a/build/targets.sdl.android.mak b/scripts/targets.sdl.android.mak similarity index 100% rename from build/targets.sdl.android.mak rename to scripts/targets.sdl.android.mak diff --git a/build/targets.sdl.mak b/scripts/targets.sdl.mak similarity index 100% rename from build/targets.sdl.mak rename to scripts/targets.sdl.mak diff --git a/build/toolchain.android.mak b/scripts/toolchain.android.mak similarity index 100% rename from build/toolchain.android.mak rename to scripts/toolchain.android.mak From 570ecd1da50c5a6cfca67083362926494284648c Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 6 Mar 2019 20:55:58 +0100 Subject: [PATCH 15/78] [scripts] Fix a typo --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 01907f05c..091cf0645 100644 --- a/Makefile +++ b/Makefile @@ -109,7 +109,7 @@ $(eval $(call rule_for, \ $(eval $(call rule_for, \ CXX, %.o, %.cpp, \ - $$(CC) $$(SFLAGS) $$(CXXFLAGS) -c $$< -o $$@, \ + $$(CXX) $$(SFLAGS) $$(CXXFLAGS) -c $$< -o $$@, \ with_local_version \ )) From cbda08ac3cdbf7af80a73345281e73fe30233d15 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 6 Mar 2019 20:56:22 +0100 Subject: [PATCH 16/78] [scripts] Add a default rule for Objective-C --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index 091cf0645..0844831f1 100644 --- a/Makefile +++ b/Makefile @@ -113,6 +113,11 @@ $(eval $(call rule_for, \ with_local_version \ )) +$(eval $(call rule_for, \ + OCC, %.o, %.m, \ + $$(CC) $$(SFLAGS) $$(CFLAGS) -c $$< -o $$@ \ +)) + $(eval $(call rule_for, \ LD, %.$$(EXE), , \ $$(LD) $$^ $$(LDFLAGS) -o $$@ \ From e2bb859e854c269a162b4b6e72053c7615cb0196 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 6 Mar 2019 20:59:43 +0100 Subject: [PATCH 17/78] [ion/sdl] Fix the Makefiles --- ion/src/sdl/Makefile | 49 ++++++++++++++------------------ ion/src/sdl/android/Makefile | 6 ++-- scripts/platform.sdl.android.mak | 5 ++-- scripts/platform.sdl.mak | 12 ++++---- scripts/targets.sdl.android.mak | 39 ++++++++----------------- scripts/targets.sdl.mak | 2 +- scripts/toolchain.android.mak | 21 ++++---------- 7 files changed, 53 insertions(+), 81 deletions(-) diff --git a/ion/src/sdl/Makefile b/ion/src/sdl/Makefile index ee90789dd..de3c5b517 100644 --- a/ion/src/sdl/Makefile +++ b/ion/src/sdl/Makefile @@ -1,34 +1,27 @@ # TODO -objs += $(addprefix ion/src/shared/, \ - crc32.o \ - crc32_padded.o \ - events.o \ - events_modifier.o \ - power.o \ - random.o \ - timing.o \ - dummy/backlight.o \ - dummy/battery.o \ - dummy/fcc_id.o \ - dummy/led.o \ - dummy/serial_number.o \ - dummy/stack.o \ - dummy/usb.o \ +src += $(addprefix ion/src/shared/, \ + crc32.cpp \ + crc32_padded.cpp \ + events.cpp \ + events_modifier.cpp \ + power.cpp \ + random.cpp \ + timing.cpp \ + dummy/backlight.cpp \ + dummy/battery.cpp \ + dummy/fcc_id.cpp \ + dummy/led.cpp \ + dummy/serial_number.cpp \ + dummy/stack.cpp \ + dummy/usb.cpp \ ) -objs += $(addprefix ion/src/sdl/shared/, \ - display.o \ - events.o \ - main.o \ - layout.o \ +src += $(addprefix ion/src/sdl/shared/, \ + display.cpp \ + events.cpp \ + main.cpp \ + layout.cpp \ ) include ion/src/sdl/$(MODEL)/Makefile - -#objs += $(addprefix ion/src/sdl/android/, \ -# images.o \ -#) - -#SFLAGS += -I/Users/romain/Sources/SDL/include - -#include ion/src/sdl/external/Makefile +include ion/src/sdl/external/Makefile diff --git a/ion/src/sdl/android/Makefile b/ion/src/sdl/android/Makefile index d67008e42..0b6a45bcc 100644 --- a/ion/src/sdl/android/Makefile +++ b/ion/src/sdl/android/Makefile @@ -1,7 +1,9 @@ -objs += $(addprefix ion/src/sdl/android/src/cpp/, \ - images.o \ +src += $(addprefix ion/src/sdl/android/src/cpp/, \ + images.cpp \ ) +LDFLAGS += -ljnigraphics -llog + ANDROID_LIB_PATH = ion/src/sdl/android/libs $(ANDROID_LIB_PATH)/%/libepsilon.so: diff --git a/scripts/platform.sdl.android.mak b/scripts/platform.sdl.android.mak index 334ea68b6..72fd677ec 100644 --- a/scripts/platform.sdl.android.mak +++ b/scripts/platform.sdl.android.mak @@ -1,5 +1,6 @@ TOOLCHAIN ?= android EXE = so -LDFLAGS += -L$(SDL_PATH)/build/android/lib/$(NDK_ABI) -LDFLAGS += -lSDL2 -ljnigraphics -llog +ifdef NDK_ABI +BUILD_DIR := $(BUILD_DIR)/$(NDK_ABI) +endif diff --git a/scripts/platform.sdl.mak b/scripts/platform.sdl.mak index ab070854e..90ee686b2 100644 --- a/scripts/platform.sdl.mak +++ b/scripts/platform.sdl.mak @@ -3,14 +3,16 @@ EPSILON_ONBOARDING_APP = 0 SFLAGS += -fPIE EXE = elf -ifndef SDL_PATH - $(error SDL_PATH should point to the SDL2 sources. A recent snapshot is required.) -endif +#ifndef SDL_PATH +# $(error SDL_PATH should point to the SDL2 sources. A recent snapshot is required.) +#endif -SFLAGS += -I$(SDL_PATH)/include +#SFLAGS += -I$(SDL_PATH)/include ifndef MODEL $(error MODEL should be defined) endif -include build/platform.sdl.$(MODEL).mak +BUILD_DIR := $(BUILD_DIR)/$(MODEL) + +include scripts/platform.sdl.$(MODEL).mak diff --git a/scripts/targets.sdl.android.mak b/scripts/targets.sdl.android.mak index 8c865bb27..35a2662a3 100644 --- a/scripts/targets.sdl.android.mak +++ b/scripts/targets.sdl.android.mak @@ -1,33 +1,18 @@ -# CAUTION: All the following builds have to happen in a sequential order so we -# cannot use standard Make dependencies -.PHONY: dependencies -dependencies: - rm -rf ion/src/sdl/android/libs - # First, build libsdl - cd $(SDL_PATH) && build-scripts/androidbuildlibs.sh - cp -r $(SDL_PATH)/build/android/lib ion/src/sdl/android/libs - # Then build epsilon for armv7 - rm -f ion/src/sdl/android/libs/armeabi-v7a/libepsilon.so - $(MAKE) ion/src/sdl/android/libs/armeabi-v7a/libepsilon.so - # Then for arm64 - rm -f ion/src/sdl/android/libs/arm64-v8a/libepsilon.so - $(MAKE) ion/src/sdl/android/libs/arm64-v8a/libepsilon.so - # Then for x86 - rm -f ion/src/sdl/android/libs/x86/libepsilon.so - $(MAKE) ion/src/sdl/android/libs/x86/libepsilon.so - # And eventually for x86_64 - rm -f ion/src/sdl/android/libs/x86_64/libepsilon.so - $(MAKE) ion/src/sdl/android/libs/x86_64/libepsilon.so - # Last but not least, copy the sources from SDL - cp -r $(SDL_PATH)/android-project/app/src/main/java/org ion/src/sdl/android/src/java +$(BUILD_DIR)/app/libs/%/libepsilon.so: $$(@D)/. + $(Q) echo "MAKE NDK_ABI=$*" + $(Q) $(MAKE) NDK_ABI=$* epsilon.so + $(Q) cp build/sdl/android/$*/epsilon.so $@ -epsilon.apk: - #rm ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so - #$(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so - cd ion/src/sdl/android ; ANDROID_HOME=/usr/local/android gradle assembleRelease - cp ion/src/sdl/android/build/outputs/apk/release/app-release-unsigned.apk epsilon.apk +NDK_ABIS = arm64-v8a x86_64 +.PHONY: epsilon.apk +epsilon.apk: $(patsubst %,$(BUILD_DIR)/app/libs/%/libepsilon.so,$(NDK_ABIS)) + @echo "GRADLE ion/src/sdl/android/build.gradle" + $(Q) ANDROID_HOME=/usr/local/android gradle -b ion/src/sdl/android/build.gradle assembleRelease + +.PHONY: epsilon_run epsilon_run: + ANDROID_HOME=/usr/local/android gradle -b ion/src/sdl/android/build.gradle assembleRelease #rm ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so #$(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so cd ion/src/sdl/android ; ANDROID_HOME=/usr/local/android gradle installDebug diff --git a/scripts/targets.sdl.mak b/scripts/targets.sdl.mak index 54afd6954..50996eab2 100644 --- a/scripts/targets.sdl.mak +++ b/scripts/targets.sdl.mak @@ -1 +1 @@ -include build/targets.sdl.$(MODEL).mak +include scripts/targets.sdl.$(MODEL).mak diff --git a/scripts/toolchain.android.mak b/scripts/toolchain.android.mak index 98aa981ee..98a74a85e 100644 --- a/scripts/toolchain.android.mak +++ b/scripts/toolchain.android.mak @@ -3,8 +3,6 @@ NDK_HOST_TAG ?= darwin-x86_64 NDK_TOOLCHAIN_PATH = $(NDK_PATH)/toolchains/llvm/prebuilt/$(NDK_HOST_TAG)/bin -NDK_ABI ?= arm64-v8a - ifeq ($(NDK_ABI),armeabi-v7a) NDK_TARGET = armv7a-linux-androideabi else ifeq ($(NDK_ABI),arm64-v8a) @@ -13,29 +11,20 @@ else ifeq ($(NDK_ABI),x86) NDK_TARGET = i686-linux-android else ifeq ($(NDK_ABI),x86_64) NDK_TARGET = x86_64-linux-android -else - $(error Unknown NDK_ABI) endif +ifdef NDK_TARGET + NDK_VERSION ?= 21 + CC = $(NDK_TOOLCHAIN_PATH)/$(NDK_TARGET)$(NDK_VERSION)-clang CXX = $(NDK_TOOLCHAIN_PATH)/$(NDK_TARGET)$(NDK_VERSION)-clang++ LD = $(NDK_TOOLCHAIN_PATH)/$(NDK_TARGET)$(NDK_VERSION)-clang++ SFLAGS += -fPIC -#GDB = arm-none-eabi-gdb -#OBJCOPY = arm-none-eabi-objcopy -#SIZE = arm-none-eabi-size - - -#SFLAGS += -fdata-sections -ffunction-sections -#LDFLAGS += -Wl,--gc-sections -#SFLAGS += -fvisibility=hidden -#LDFLAGS += -Wl,-export-symbols,ion/src/sdl/export_list.txt - LDFLAGS += -shared LDFLAGS += -static-libstdc++ -#LDFLAGS += -Wl,-z,defs # Warn on missing symbols when linking the dynamic library +LDFLAGS += -Wl,-z,defs # Warn on missing symbols when linking the dynamic library -#LDFLAGS += -lsdl +endif From 1b292d123db315a144f2fa249b0beb29ed039df9 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 6 Mar 2019 21:01:22 +0100 Subject: [PATCH 18/78] [ion/sdl] Cleanup the Makefiles --- scripts/platform.sdl.mak | 6 ------ 1 file changed, 6 deletions(-) diff --git a/scripts/platform.sdl.mak b/scripts/platform.sdl.mak index 90ee686b2..65a591802 100644 --- a/scripts/platform.sdl.mak +++ b/scripts/platform.sdl.mak @@ -3,12 +3,6 @@ EPSILON_ONBOARDING_APP = 0 SFLAGS += -fPIE EXE = elf -#ifndef SDL_PATH -# $(error SDL_PATH should point to the SDL2 sources. A recent snapshot is required.) -#endif - -#SFLAGS += -I$(SDL_PATH)/include - ifndef MODEL $(error MODEL should be defined) endif From 7c5c10060fc6ce02788c0b03cdd82c9a2ac99ed8 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 6 Mar 2019 21:55:04 +0100 Subject: [PATCH 19/78] [ion/sdl] Add an iOS version --- ion/src/sdl/ios/Info.plist | 79 ++++++++++++++++++++++++++++++++++++ ion/src/sdl/ios/Makefile | 3 ++ ion/src/sdl/ios/images.m | 49 ++++++++++++++++++++++ scripts/platform.sdl.ios.mak | 10 +++++ scripts/targets.sdl.ios.mak | 19 +++++++++ scripts/toolchain.ios.mak | 14 +++++++ 6 files changed, 174 insertions(+) create mode 100644 ion/src/sdl/ios/Info.plist create mode 100644 ion/src/sdl/ios/Makefile create mode 100644 ion/src/sdl/ios/images.m create mode 100644 scripts/platform.sdl.ios.mak create mode 100644 scripts/targets.sdl.ios.mak create mode 100644 scripts/toolchain.ios.mak diff --git a/ion/src/sdl/ios/Info.plist b/ion/src/sdl/ios/Info.plist new file mode 100644 index 000000000..f58788bc5 --- /dev/null +++ b/ion/src/sdl/ios/Info.plist @@ -0,0 +1,79 @@ + + + + + BuildMachineOSBuild + 18B75 + CFBundleDevelopmentRegion + en + CFBundleExecutable + Epsilon + CFBundleIdentifier + com.blabla.PlifPlouf + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + PlifPlouf + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSupportedPlatforms + + iPhoneSimulator + + CFBundleVersion + 1 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 10B61 + DTPlatformName + iphonesimulator + DTPlatformVersion + 12.1 + DTSDKBuild + 16B91 + DTSDKName + iphonesimulator12.1 + LSRequiresIPhoneOS + + MinimumOSVersion + 12.1 + UIDeviceFamily + + 1 + 2 + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + FOO-UILaunchImages + + + UILaunchImageMinimumOSVersion + 8.0 + UILaunchImageName + Default + UILaunchImageOrientation + Portrait + UILaunchImageSize + {828, 1792} + + + + diff --git a/ion/src/sdl/ios/Makefile b/ion/src/sdl/ios/Makefile new file mode 100644 index 000000000..54e786282 --- /dev/null +++ b/ion/src/sdl/ios/Makefile @@ -0,0 +1,3 @@ +src += $(addprefix ion/src/sdl/ios/, \ + images.m \ +) diff --git a/ion/src/sdl/ios/images.m b/ion/src/sdl/ios/images.m new file mode 100644 index 000000000..6338d16c6 --- /dev/null +++ b/ion/src/sdl/ios/images.m @@ -0,0 +1,49 @@ +#include +#include + +SDL_Texture * loadImage(SDL_Renderer * renderer, const char * identifier) { + CGImageRef cgImage = [[UIImage imageNamed:[NSString stringWithUTF8String:identifier]] CGImage]; + if (cgImage == NULL) { + return NULL; + } + size_t width = CGImageGetWidth(cgImage); + size_t height = CGImageGetHeight(cgImage); + + + size_t bytesPerPixel = 4; + size_t bytesPerRow = bytesPerPixel * width; + size_t bitsPerComponent = 8; + + void * bitmapData = malloc(height * width * bytesPerPixel); + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGContextRef context = CGBitmapContextCreate( + bitmapData, width, height, + bitsPerComponent, bytesPerRow, colorSpace, + kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big + ); + + CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage); + + CGContextRelease(context); + CGColorSpaceRelease(colorSpace); + + SDL_Texture * texture = SDL_CreateTexture( + renderer, + SDL_PIXELFORMAT_ABGR8888, + SDL_TEXTUREACCESS_STATIC, + width, + height + ); + + SDL_UpdateTexture( + texture, + NULL, + bitmapData, + 4 * width + ); + + free(bitmapData); + + return texture; +} diff --git a/scripts/platform.sdl.ios.mak b/scripts/platform.sdl.ios.mak new file mode 100644 index 000000000..d242ff6ff --- /dev/null +++ b/scripts/platform.sdl.ios.mak @@ -0,0 +1,10 @@ +TOOLCHAIN ?= ios +EXE = bin + +SDK ?= iphonesimulator + +BUILD_DIR := $(BUILD_DIR)/$(SDK) + +ifdef ARCH +BUILD_DIR := $(BUILD_DIR)/$(ARCH) +endif diff --git a/scripts/targets.sdl.ios.mak b/scripts/targets.sdl.ios.mak new file mode 100644 index 000000000..d26ede79b --- /dev/null +++ b/scripts/targets.sdl.ios.mak @@ -0,0 +1,19 @@ +$(BUILD_DIR)/%/epsilon.bin: + $(Q) echo "MAKE ARCH=$*" + $(Q) $(MAKE) ARCH=$* + +ARCHS ?= x86_64 + +.PHONY: epsilon.ipa +epsilon.ipa: $(BUILD_DIR)/app/Epsilon.app + +$(BUILD_DIR)/app/Epsilon.app: $(patsubst %,$(BUILD_DIR)/%/epsilon.bin,$(ARCHS)) + rm -rf $@ + mkdir -p $@ + lipo -create $^ -output $@/Epsilon + cp ion/src/sdl/ios/Info.plist $@/ + cp ion/src/sdl/assets/* $@/ + # TODO: Generate launch images + +epsilon_run: + xcrun simctl install C75D15A1-45F7-4F94-8DBD-6D02A95C9514 $(BUILD_DIR)/app/Epsilon.app diff --git a/scripts/toolchain.ios.mak b/scripts/toolchain.ios.mak new file mode 100644 index 000000000..dba1e01d6 --- /dev/null +++ b/scripts/toolchain.ios.mak @@ -0,0 +1,14 @@ +CC = clang +CXX = clang++ +LD = clang++ + +#ARCH = x86_64 +#SDK = iphonesimulator +# ARCH = arm64 +# SDK = iphoneos + +SYSROOT = $(shell xcrun --sdk $(SDK) --show-sdk-path) + +SFLAGS += -fPIC -arch $(ARCH) -isysroot $(SYSROOT) +LDFLAGS += -arch $(ARCH) -isysroot $(SYSROOT) +#-framework Foundation -framework OpenGLES -framework UIKit -framework AVFoundation -framework AudioToolbox -framework QuartzCore -framework GameController -framework CoreGraphics -framework CoreMotion From 2ac9f3efbd0ab4862c2fe5b009c786585498edf3 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Thu, 7 Mar 2019 22:58:57 +0100 Subject: [PATCH 20/78] [ion/sdl] Add macOS support --- ion/src/sdl/macos/Info.plist | 48 +++++++++++++++++++++++++++++++ ion/src/sdl/macos/Makefile | 3 ++ ion/src/sdl/macos/images.m | 52 ++++++++++++++++++++++++++++++++++ scripts/platform.sdl.macos.mak | 6 ++++ scripts/targets.sdl.macos.mak | 19 +++++++++++++ scripts/toolchain.macos.mak | 14 +++++++++ 6 files changed, 142 insertions(+) create mode 100644 ion/src/sdl/macos/Info.plist create mode 100644 ion/src/sdl/macos/Makefile create mode 100644 ion/src/sdl/macos/images.m create mode 100644 scripts/platform.sdl.macos.mak create mode 100644 scripts/targets.sdl.macos.mak create mode 100644 scripts/toolchain.macos.mak diff --git a/ion/src/sdl/macos/Info.plist b/ion/src/sdl/macos/Info.plist new file mode 100644 index 000000000..18bda6b15 --- /dev/null +++ b/ion/src/sdl/macos/Info.plist @@ -0,0 +1,48 @@ + + + + + BuildMachineOSBuild + 18B75 + CFBundleDevelopmentRegion + en + CFBundleExecutable + Epsilon + CFBundleIdentifier + com.blabla.FooBarCocoa + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + FooBarCocoa + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSupportedPlatforms + + MacOSX + + CFBundleVersion + 1 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 10B61 + DTPlatformVersion + GM + DTSDKBuild + 18B71 + DTSDKName + macosx10.14 + DTXcode + 1010 + DTXcodeBuild + 10B61 + LSMinimumSystemVersion + 10.14 + NSHumanReadableCopyright + Copyright © 2019 Romain Goyet. All rights reserved. + NSPrincipalClass + NSApplication + + diff --git a/ion/src/sdl/macos/Makefile b/ion/src/sdl/macos/Makefile new file mode 100644 index 000000000..7168edb55 --- /dev/null +++ b/ion/src/sdl/macos/Makefile @@ -0,0 +1,3 @@ +src += $(addprefix ion/src/sdl/macos/, \ + images.m \ +) diff --git a/ion/src/sdl/macos/images.m b/ion/src/sdl/macos/images.m new file mode 100644 index 000000000..783cfee6a --- /dev/null +++ b/ion/src/sdl/macos/images.m @@ -0,0 +1,52 @@ +#include +#include + +SDL_Texture * loadImage(SDL_Renderer * renderer, const char * identifier) { + NSImage * nsImage = [NSImage imageNamed:[NSString stringWithUTF8String:identifier]]; + CGImageRef cgImage = [nsImage CGImageForProposedRect:NULL + context:NULL + hints:0]; + if (cgImage == NULL) { + return NULL; + } + size_t width = CGImageGetWidth(cgImage); + size_t height = CGImageGetHeight(cgImage); + + + size_t bytesPerPixel = 4; + size_t bytesPerRow = bytesPerPixel * width; + size_t bitsPerComponent = 8; + + void * bitmapData = malloc(height * width * bytesPerPixel); + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGContextRef context = CGBitmapContextCreate( + bitmapData, width, height, + bitsPerComponent, bytesPerRow, colorSpace, + kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big + ); + + CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage); + + CGContextRelease(context); + CGColorSpaceRelease(colorSpace); + + SDL_Texture * texture = SDL_CreateTexture( + renderer, + SDL_PIXELFORMAT_ABGR8888, + SDL_TEXTUREACCESS_STATIC, + width, + height + ); + + SDL_UpdateTexture( + texture, + NULL, + bitmapData, + 4 * width + ); + + free(bitmapData); + + return texture; +} diff --git a/scripts/platform.sdl.macos.mak b/scripts/platform.sdl.macos.mak new file mode 100644 index 000000000..63a276a77 --- /dev/null +++ b/scripts/platform.sdl.macos.mak @@ -0,0 +1,6 @@ +TOOLCHAIN ?= macos +EXE = bin + +ifdef ARCH +BUILD_DIR := $(BUILD_DIR)/$(ARCH) +endif diff --git a/scripts/targets.sdl.macos.mak b/scripts/targets.sdl.macos.mak new file mode 100644 index 000000000..8c96cb868 --- /dev/null +++ b/scripts/targets.sdl.macos.mak @@ -0,0 +1,19 @@ +$(BUILD_DIR)/%/epsilon.bin: + $(Q) echo "MAKE ARCH=$*" + $(Q) $(MAKE) ARCH=$* + +ARCHS ?= x86_64 + +.PHONY: epsilon.ipa +epsilon.ipa: $(BUILD_DIR)/app/Epsilon.app + +$(BUILD_DIR)/app/Epsilon.app: $(patsubst %,$(BUILD_DIR)/%/epsilon.bin,$(ARCHS)) + rm -rf $@ + mkdir -p $@/Contents/MacOS + lipo -create $^ -output $@/Contents/MacOS/Epsilon + cp ion/src/sdl/macos/Info.plist $@/Contents + mkdir -p $@/Contents/Resources + cp ion/src/sdl/assets/* $@/Contents/Resources + +epsilon_run: + open $(BUILD_DIR)/app/Epsilon.app diff --git a/scripts/toolchain.macos.mak b/scripts/toolchain.macos.mak new file mode 100644 index 000000000..8496f3ad9 --- /dev/null +++ b/scripts/toolchain.macos.mak @@ -0,0 +1,14 @@ +CC = clang +CXX = clang++ +LD = clang++ + +#ARCH = x86_64 +#SDK = iphonesimulator +# ARCH = arm64 +# SDK = iphoneos + +SYSROOT = $(shell xcrun --sdk macosx --show-sdk-path) + +SFLAGS += -fPIC -arch $(ARCH) -isysroot $(SYSROOT) +LDFLAGS += -arch $(ARCH) -isysroot $(SYSROOT) +#-framework Foundation -framework OpenGLES -framework UIKit -framework AVFoundation -framework AudioToolbox -framework QuartzCore -framework GameController -framework CoreGraphics -framework CoreMotion From acb6c65e35c60a7b1d90f00f7a9c4cb0070b3e5d Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Thu, 7 Mar 2019 23:01:45 +0100 Subject: [PATCH 21/78] [ion/sdl] Add a Windows version --- ion/src/sdl/windows/Makefile | 6 +++ ion/src/sdl/windows/images.cpp | 92 ++++++++++++++++++++++++++++++++ ion/src/sdl/windows/resources.rc | 25 +++++++++ scripts/platform.sdl.windows.mak | 2 + scripts/targets.sdl.windows.mak | 4 ++ scripts/toolchain.windows.mak | 10 ++++ 6 files changed, 139 insertions(+) create mode 100644 ion/src/sdl/windows/Makefile create mode 100644 ion/src/sdl/windows/images.cpp create mode 100644 ion/src/sdl/windows/resources.rc create mode 100644 scripts/platform.sdl.windows.mak create mode 100644 scripts/targets.sdl.windows.mak create mode 100644 scripts/toolchain.windows.mak diff --git a/ion/src/sdl/windows/Makefile b/ion/src/sdl/windows/Makefile new file mode 100644 index 000000000..e683a5bb8 --- /dev/null +++ b/ion/src/sdl/windows/Makefile @@ -0,0 +1,6 @@ +src += $(addprefix ion/src/sdl/windows/, \ + images.cpp \ + resources.rc \ +) + +LDFLAGS += -lgdiplus diff --git a/ion/src/sdl/windows/images.cpp b/ion/src/sdl/windows/images.cpp new file mode 100644 index 000000000..6316d722f --- /dev/null +++ b/ion/src/sdl/windows/images.cpp @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include +#include + +HRESULT CreateStreamOnResource(const char * name, LPSTREAM * stream) { + assert(name != nullptr); + assert(stream != nullptr); + HINSTANCE hInstance = GetModuleHandle(0); + *stream = NULL; + HRSRC hC = FindResource(hInstance, name, RT_RCDATA); + if (!hC) { + SDL_Log("Could not find resource %s", name); + return E_INVALIDARG; + } + // This is not really a HGLOBAL http://msdn.microsoft.com/en-us/library/windows/desktop/ms648046(v=vs.85).aspx + HGLOBAL hG = LoadResource(hInstance, hC); + if (!hG) { + SDL_Log("Could not load resource %s", name); + return E_INVALIDARG; + } + void* bytes = LockResource(hG); + ULONG size = SizeofResource(hInstance, hC); + // Create a new empty stream. + HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, stream); + if (SUCCEEDED(hr)) { + ULONG written; + // Copy the resource into it. + hr = (*stream)->Write(bytes, size, &written); + } + return hr; +} + +extern "C" SDL_Texture * loadImage(SDL_Renderer * renderer, const char * identifier) { +//SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Loading image"); + SDL_Log("chabite"); + + Gdiplus::GdiplusStartupInput gdiplusStartupInput; + ULONG_PTR gdiplusToken; + Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr); + + LPSTREAM stream; + const char * resname = MAKEINTRESOURCE(300); + CreateStreamOnResource(resname, &stream); +#if 0 + IPicture * picture = nullptr; + OleLoadPicture( + stream, + 0, + false, + IID_IPicture, + (void**)&picture + ); +#endif + + Gdiplus::Bitmap * image = Gdiplus::Bitmap::FromStream(stream); + + SDL_Log("Gdiplus::Bitmap is %p", image); + + int w = (int)image->GetWidth(); + int h = (int)image->GetHeight(); + Gdiplus::Rect rc(0, 0, w, h); + + SDL_Log("Gdiplus::Bitmap is %dx%d", w,h); + + Gdiplus::BitmapData * bitmapData = new Gdiplus::BitmapData; + image->LockBits(&rc, Gdiplus::ImageLockModeRead, PixelFormat32bppARGB, bitmapData); + + SDL_Texture * texture = SDL_CreateTexture( + renderer, + SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STATIC, + w, + h + ); + + SDL_UpdateTexture( + texture, + NULL, + bitmapData->Scan0, + 4 * w + ); + + image->UnlockBits(bitmapData); + delete bitmapData; + delete image; + Gdiplus::GdiplusShutdown(gdiplusToken); + + return texture; +} diff --git a/ion/src/sdl/windows/resources.rc b/ion/src/sdl/windows/resources.rc new file mode 100644 index 000000000..024eb2d13 --- /dev/null +++ b/ion/src/sdl/windows/resources.rc @@ -0,0 +1,25 @@ +300 RCDATA "../assets/background.jpg" + +1 VERSIONINFO +FILEVERSION 1,0,0,0 +PRODUCTVERSION 1,0,0,0 +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "CompanyName", "NumWorks" + VALUE "FileDescription", "Graphing calculator simulator" + VALUE "FileVersion", "1.0" + VALUE "InternalName", "epsilon" + VALUE "LegalCopyright", "NumWorks" + VALUE "OriginalFilename", "epsilon.exe" + VALUE "ProductName", "Epsilon" + VALUE "ProductVersion", "1.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END diff --git a/scripts/platform.sdl.windows.mak b/scripts/platform.sdl.windows.mak new file mode 100644 index 000000000..40320cd9d --- /dev/null +++ b/scripts/platform.sdl.windows.mak @@ -0,0 +1,2 @@ +TOOLCHAIN ?= windows +EXE = exe diff --git a/scripts/targets.sdl.windows.mak b/scripts/targets.sdl.windows.mak new file mode 100644 index 000000000..51f8c7518 --- /dev/null +++ b/scripts/targets.sdl.windows.mak @@ -0,0 +1,4 @@ +$(eval $(call rule_for, \ + WINDRES, %.o, %.rc, \ + $$(WINDRES) $$< -O coff -o $$@ \ +)) diff --git a/scripts/toolchain.windows.mak b/scripts/toolchain.windows.mak new file mode 100644 index 000000000..3973705de --- /dev/null +++ b/scripts/toolchain.windows.mak @@ -0,0 +1,10 @@ +CC = x86_64-w64-mingw32-gcc +CXX = x86_64-w64-mingw32-g++ +LD = x86_64-w64-mingw32-g++ +EXE = exe + +SFLAGS += -D_USE_MATH_DEFINES +#LDFLAGS += -static -mwindows +LDFLAGS += -static + +WINDRES = x86_64-w64-mingw32-windres From f0aec2a6e68accc35bc0ba480b822046e0859c99 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 8 Mar 2019 11:40:33 +0100 Subject: [PATCH 22/78] [ion/sdl] Lazy texture refresh --- ion/src/sdl/shared/display.cpp | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/ion/src/sdl/shared/display.cpp b/ion/src/sdl/shared/display.cpp index 947ada2a2..71440e6ad 100644 --- a/ion/src/sdl/shared/display.cpp +++ b/ion/src/sdl/shared/display.cpp @@ -1,9 +1,19 @@ #include "display.h" -#include "keyboard.h" +#include "main.h" #include #include +/* Drawing on an SDL texture + * In SDL2, drawing bitmap data happens through textures, whose data lives in + * the GPU's memory. Reading data back from a texture is not possible, so we + * simply maintain a framebuffer in RAM since Ion::Display::pullRect expects to + * be able to read pixel data back. + * A side effect is that we rewrite the whole texture when redrawing the screen. + * This might not be the most efficient way since sending pixels to the GPU is + * rather expensive. */ + static KDColor sPixels[Ion::Display::Width * Ion::Display::Height]; +static bool sFramebufferNeedsRedraw = false; namespace Ion { namespace Display { @@ -11,10 +21,12 @@ namespace Display { static KDFrameBuffer sFrameBuffer = KDFrameBuffer(sPixels, KDSize(Ion::Display::Width, Ion::Display::Height)); void pushRect(KDRect r, const KDColor * pixels) { + sFramebufferNeedsRedraw = true; sFrameBuffer.pushRect(r, pixels); } void pushRectUniform(KDRect r, KDColor c) { + sFramebufferNeedsRedraw = true; sFrameBuffer.pushRectUniform(r, c); } @@ -51,18 +63,17 @@ void quit() { } void draw(SDL_Renderer * renderer, SDL_Rect * rect) { -#if 1 - int pitch = 0; - void * pixels = nullptr; - SDL_LockTexture(sFramebufferTexture, nullptr, &pixels, &pitch); - assert(pitch == 2*Ion::Display::Width); - memcpy(pixels, sPixels, sizeof(sPixels)); - SDL_UnlockTexture(sFramebufferTexture); + if (sFramebufferNeedsRedraw) { + int pitch = 0; + void * pixels = nullptr; + SDL_LockTexture(sFramebufferTexture, nullptr, &pixels, &pitch); + assert(pitch == 2*Ion::Display::Width); + memcpy(pixels, sPixels, sizeof(sPixels)); + SDL_UnlockTexture(sFramebufferTexture); + } + sFramebufferNeedsRedraw = false; SDL_RenderCopy(renderer, sFramebufferTexture, nullptr, rect); -#else - SDL_UpdateTexture(sFramebufferTexture, nullptr, sPixels, 2*Ion::Display::Width); -#endif } } From 35d8af1315e4879970a1e23f196767e0a98a5bfb Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 8 Mar 2019 11:41:13 +0100 Subject: [PATCH 23/78] [ion/sdl/windows] Clean the image loading code --- ion/src/sdl/windows/images.cpp | 64 ++++++++++++---------------------- 1 file changed, 23 insertions(+), 41 deletions(-) diff --git a/ion/src/sdl/windows/images.cpp b/ion/src/sdl/windows/images.cpp index 6316d722f..4fb49e013 100644 --- a/ion/src/sdl/windows/images.cpp +++ b/ion/src/sdl/windows/images.cpp @@ -1,16 +1,17 @@ #include #include #include -#include #include -#include + +/* Loading images using GDI+ + * On Windows, we decompress JPEG images using GDI+ which is widely available. + * Note that this adds an extra runtime dependency (as compared to just SDL), + * but this should not be an issue. */ HRESULT CreateStreamOnResource(const char * name, LPSTREAM * stream) { - assert(name != nullptr); - assert(stream != nullptr); HINSTANCE hInstance = GetModuleHandle(0); - *stream = NULL; - HRSRC hC = FindResource(hInstance, name, RT_RCDATA); + *stream = nullptr; + HRSRC hResource = FindResource(hInstance, name, RT_RCDATA); if (!hC) { SDL_Log("Could not find resource %s", name); return E_INVALIDARG; @@ -21,22 +22,17 @@ HRESULT CreateStreamOnResource(const char * name, LPSTREAM * stream) { SDL_Log("Could not load resource %s", name); return E_INVALIDARG; } - void* bytes = LockResource(hG); + void * bytes = LockResource(hG); ULONG size = SizeofResource(hInstance, hC); - // Create a new empty stream. - HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, stream); + HRESULT hr = CreateStreamOnHGlobal(NULL, true, stream); if (SUCCEEDED(hr)) { ULONG written; - // Copy the resource into it. hr = (*stream)->Write(bytes, size, &written); } return hr; } extern "C" SDL_Texture * loadImage(SDL_Renderer * renderer, const char * identifier) { -//SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Loading image"); - SDL_Log("chabite"); - Gdiplus::GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr); @@ -44,44 +40,30 @@ extern "C" SDL_Texture * loadImage(SDL_Renderer * renderer, const char * identif LPSTREAM stream; const char * resname = MAKEINTRESOURCE(300); CreateStreamOnResource(resname, &stream); -#if 0 - IPicture * picture = nullptr; - OleLoadPicture( - stream, - 0, - false, - IID_IPicture, - (void**)&picture - ); -#endif Gdiplus::Bitmap * image = Gdiplus::Bitmap::FromStream(stream); - SDL_Log("Gdiplus::Bitmap is %p", image); - - int w = (int)image->GetWidth(); - int h = (int)image->GetHeight(); - Gdiplus::Rect rc(0, 0, w, h); - - SDL_Log("Gdiplus::Bitmap is %dx%d", w,h); + int width = (int)image->GetWidth(); + int height = (int)image->GetHeight(); + Gdiplus::Rect rc(0, 0, width, height); Gdiplus::BitmapData * bitmapData = new Gdiplus::BitmapData; image->LockBits(&rc, Gdiplus::ImageLockModeRead, PixelFormat32bppARGB, bitmapData); SDL_Texture * texture = SDL_CreateTexture( - renderer, - SDL_PIXELFORMAT_ARGB8888, - SDL_TEXTUREACCESS_STATIC, - w, - h - ); + renderer, + SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STATIC, + width, + height + ); SDL_UpdateTexture( - texture, - NULL, - bitmapData->Scan0, - 4 * w - ); + texture, + NULL, + bitmapData->Scan0, + 4 * width + ); image->UnlockBits(bitmapData); delete bitmapData; From f11133e716a4eccb9809b9c7f2fcab60f6f7bdb2 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 8 Mar 2019 11:42:03 +0100 Subject: [PATCH 24/78] [ion/sdl] loadImage is a C symbol --- ion/src/sdl/shared/images.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ion/src/sdl/shared/images.h b/ion/src/sdl/shared/images.h index ef3e1cc55..9fb79f23a 100644 --- a/ion/src/sdl/shared/images.h +++ b/ion/src/sdl/shared/images.h @@ -2,4 +2,4 @@ // #include -SDL_Texture * loadImage(SDL_Renderer * renderer, const char * identifier); +extern "C" SDL_Texture * loadImage(SDL_Renderer * renderer, const char * identifier); From 2be45fd28b0c94a625a8753fe472c08abfd9e4a0 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 8 Mar 2019 11:42:35 +0100 Subject: [PATCH 25/78] [scripts/windows] Load -mwindows only in non-debug builds --- scripts/toolchain.windows.mak | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/toolchain.windows.mak b/scripts/toolchain.windows.mak index 3973705de..29376ccf4 100644 --- a/scripts/toolchain.windows.mak +++ b/scripts/toolchain.windows.mak @@ -4,7 +4,13 @@ LD = x86_64-w64-mingw32-g++ EXE = exe SFLAGS += -D_USE_MATH_DEFINES -#LDFLAGS += -static -mwindows LDFLAGS += -static +ifeq ($(DEBUG),1) +# Defining "-mwindows" prevents the app from launching an instance of cmd.exe +# when running the app. That terminal will receive stderr and stdout. The +# downside of not defining "-mwindows" is that you lose stdout and stderr. +LDFLAGS += -mwindows +endif + WINDRES = x86_64-w64-mingw32-windres From 21f8129c1e7c6a08b755a3e15368369f67e72022 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 8 Mar 2019 11:44:03 +0100 Subject: [PATCH 26/78] [ion/sdl/windows] Fix a typo --- ion/src/sdl/windows/images.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ion/src/sdl/windows/images.cpp b/ion/src/sdl/windows/images.cpp index 4fb49e013..d970c8c6d 100644 --- a/ion/src/sdl/windows/images.cpp +++ b/ion/src/sdl/windows/images.cpp @@ -11,7 +11,7 @@ HRESULT CreateStreamOnResource(const char * name, LPSTREAM * stream) { HINSTANCE hInstance = GetModuleHandle(0); *stream = nullptr; - HRSRC hResource = FindResource(hInstance, name, RT_RCDATA); + HRSRC hC = FindResource(hInstance, name, RT_RCDATA); if (!hC) { SDL_Log("Could not find resource %s", name); return E_INVALIDARG; From 0e86de40ad9d83b4ab29396f712e5a68bc8c794a Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 8 Mar 2019 11:44:42 +0100 Subject: [PATCH 27/78] [ion/sdl] Cleanup on quit --- ion/src/sdl/shared/main.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ion/src/sdl/shared/main.cpp b/ion/src/sdl/shared/main.cpp index 31abfe63f..6b8c0f3ca 100644 --- a/ion/src/sdl/shared/main.cpp +++ b/ion/src/sdl/shared/main.cpp @@ -1,12 +1,11 @@ #include "main.h" #include "display.h" -#include #include "images.h" #include "layout.h" +#include #include #include - #include void Ion::Timing::msleep(uint32_t ms) { @@ -16,7 +15,7 @@ void Ion::Timing::msleep(uint32_t ms) { int main(int argc, char * argv[]) { Ion::SDL::Main::init(); ion_main(argc, argv); - //Ion::SDL::Main::quit(); + Ion::SDL::Main::quit(); return 0; } @@ -30,7 +29,7 @@ static SDL_Texture * sBackgroundTexture = nullptr; void init() { if (SDL_Init(SDL_INIT_VIDEO) != 0) { - // Error... + SDL_Log("Could not init video"); return; } @@ -67,8 +66,6 @@ void relayout() { SDL_RenderPresent(sRenderer); refresh(); - - //SDL_UpdateWindowSurface(sWindow); } void refresh() { @@ -82,6 +79,11 @@ void refresh() { SDL_RenderPresent(sRenderer); } +void quit() { + SDL_DestroyWindow(sWindow); + SDL_Quit(); +} + } } } From 7744830880521b4f0db6580b70df16604a484a4c Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 8 Mar 2019 11:47:15 +0100 Subject: [PATCH 28/78] [ion/sdl/android] Clean the gradle script --- ion/src/sdl/android/build.gradle | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ion/src/sdl/android/build.gradle b/ion/src/sdl/android/build.gradle index 302d69cfb..4ebdcd6c1 100644 --- a/ion/src/sdl/android/build.gradle +++ b/ion/src/sdl/android/build.gradle @@ -1,3 +1,5 @@ +def BUILD_DIR = '../../../../build/sdl/android/app' + buildscript { repositories { jcenter() @@ -15,6 +17,7 @@ allprojects { jcenter() google() } + buildDir = BUILD_DIR } apply plugin: 'com.android.application' @@ -39,7 +42,7 @@ android { manifest.srcFile 'src/AndroidManifest.xml' res.srcDir 'src/res' java.srcDir 'src' - jniLibs.srcDir 'libs' + jniLibs.srcDir BUILD_DIR + '/libs' assets.srcDir '../assets' } } From c36b8bfa7c0b7ae8774d32251ce333694717a453 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 8 Mar 2019 11:47:41 +0100 Subject: [PATCH 29/78] [ion/sdl/android] Misc small fixes --- ion/src/sdl/android/src/cpp/images.cpp | 1 + .../src/java/com/numworks/calculator/EpsilonActivity.java | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/ion/src/sdl/android/src/cpp/images.cpp b/ion/src/sdl/android/src/cpp/images.cpp index 49d5b87d3..d2da9be86 100644 --- a/ion/src/sdl/android/src/cpp/images.cpp +++ b/ion/src/sdl/android/src/cpp/images.cpp @@ -1,5 +1,6 @@ // This shall be implemented per-platform // +#include "../../../shared/images.h" #include #include #include diff --git a/ion/src/sdl/android/src/java/com/numworks/calculator/EpsilonActivity.java b/ion/src/sdl/android/src/java/com/numworks/calculator/EpsilonActivity.java index b07febd5b..a3600addf 100644 --- a/ion/src/sdl/android/src/java/com/numworks/calculator/EpsilonActivity.java +++ b/ion/src/sdl/android/src/java/com/numworks/calculator/EpsilonActivity.java @@ -11,7 +11,6 @@ import org.libsdl.app.SDLActivity; public class EpsilonActivity extends SDLActivity { protected String[] getLibraries() { return new String[] { - "SDL2", "epsilon" }; } From be10157c494f45ceebcdbb9d5c957f0287cf2f31 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 8 Mar 2019 11:48:02 +0100 Subject: [PATCH 30/78] [scripts] Cleanups --- scripts/targets.sdl.mak | 2 +- scripts/toolchain.arm-gcc.mak | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts/targets.sdl.mak b/scripts/targets.sdl.mak index 50996eab2..3414568a2 100644 --- a/scripts/targets.sdl.mak +++ b/scripts/targets.sdl.mak @@ -1 +1 @@ -include scripts/targets.sdl.$(MODEL).mak +-include scripts/targets.sdl.$(MODEL).mak diff --git a/scripts/toolchain.arm-gcc.mak b/scripts/toolchain.arm-gcc.mak index 14995b2d1..6ef06ead1 100644 --- a/scripts/toolchain.arm-gcc.mak +++ b/scripts/toolchain.arm-gcc.mak @@ -8,8 +8,6 @@ SIZE = arm-none-eabi-size # Always generate debug information SFLAGS += -ggdb3 - -# LTO ?= NOT(DEBUG) ifeq ($(DEBUG),1) LTO ?= 0 else From 607c3cef4bfeaf073ce64c3dce0c1a2c1ffd7569 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 8 Mar 2019 11:58:34 +0100 Subject: [PATCH 31/78] [ion/sdl] Update the renderer's size on relayout --- ion/src/sdl/shared/main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ion/src/sdl/shared/main.cpp b/ion/src/sdl/shared/main.cpp index 6b8c0f3ca..3db48a272 100644 --- a/ion/src/sdl/shared/main.cpp +++ b/ion/src/sdl/shared/main.cpp @@ -58,6 +58,8 @@ void relayout() { int windowWidth = 0; int windowHeight = 0; SDL_GetWindowSize(sWindow, &windowWidth, &windowHeight); + SDL_RenderSetLogicalSize(sRenderer, windowWidth, windowHeight); + Layout::recompute(windowWidth, windowHeight); SDL_Rect backgroundRect; Layout::getBackgroundRect(&backgroundRect); From ca6cb39292109dca0dd7710412818e3b76d89097 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 8 Mar 2019 12:09:32 +0100 Subject: [PATCH 32/78] [ion/sdl] Fullscreen is configured per platform --- ion/src/sdl/android/Makefile | 2 ++ ion/src/sdl/ios/Makefile | 2 ++ ion/src/sdl/shared/main.cpp | 11 +++++++---- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/ion/src/sdl/android/Makefile b/ion/src/sdl/android/Makefile index 0b6a45bcc..70580f79c 100644 --- a/ion/src/sdl/android/Makefile +++ b/ion/src/sdl/android/Makefile @@ -2,6 +2,8 @@ src += $(addprefix ion/src/sdl/android/src/cpp/, \ images.cpp \ ) +$(call object_for,ion/src/sdl/shared/main.cpp) : SFLAGS += -DEPSILON_SDL_FULLSCREEN=1 + LDFLAGS += -ljnigraphics -llog ANDROID_LIB_PATH = ion/src/sdl/android/libs diff --git a/ion/src/sdl/ios/Makefile b/ion/src/sdl/ios/Makefile index 54e786282..9d5c9642e 100644 --- a/ion/src/sdl/ios/Makefile +++ b/ion/src/sdl/ios/Makefile @@ -1,3 +1,5 @@ src += $(addprefix ion/src/sdl/ios/, \ images.m \ ) + +$(call object_for,ion/src/sdl/shared/main.cpp) : SFLAGS += -DEPSILON_SDL_FULLSCREEN=1 diff --git a/ion/src/sdl/shared/main.cpp b/ion/src/sdl/shared/main.cpp index 3db48a272..ecb58177d 100644 --- a/ion/src/sdl/shared/main.cpp +++ b/ion/src/sdl/shared/main.cpp @@ -37,12 +37,15 @@ void init() { "Epsilon", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - 0, 0, - SDL_WINDOW_RESIZABLE + 800, 600, + SDL_WINDOW_ALLOW_HIGHDPI +#if EPSILON_SDL_FULLSCREEN + | SDL_WINDOW_FULLSCREEN +#else + | SDL_WINDOW_RESIZABLE +#endif ); - SDL_SetWindowFullscreen(sWindow, 0); - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); sRenderer = SDL_CreateRenderer(sWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); From 91c17a6b98939f95b9b707d698e84411616df136 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 8 Mar 2019 12:09:57 +0100 Subject: [PATCH 33/78] [ion/sdl] Update the screen position --- ion/src/sdl/shared/layout.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ion/src/sdl/shared/layout.cpp b/ion/src/sdl/shared/layout.cpp index 9268e195f..2a7f10dfa 100644 --- a/ion/src/sdl/shared/layout.cpp +++ b/ion/src/sdl/shared/layout.cpp @@ -11,7 +11,7 @@ static constexpr float X(int x) { return static_cast(x)/static_cast(y)/static_cast(backgroundHeight); } static constexpr SDL_FRect areaOfInterest = {X(110), Y(30), X(940), Y(2150)}; -static constexpr SDL_FRect screenRect = {X(191), Y(189), X(779), Y(582)}; +static constexpr SDL_FRect screenRect = {X(192), Y(191), X(779), Y(582)}; static SDL_Rect sFrame; From 3050fe0912e5ce160eba12abc8b22dbcd607516e Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 8 Mar 2019 12:16:22 +0100 Subject: [PATCH 34/78] [ion/sdl] Clear the background and better default size --- ion/src/sdl/shared/main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ion/src/sdl/shared/main.cpp b/ion/src/sdl/shared/main.cpp index ecb58177d..7642e2e87 100644 --- a/ion/src/sdl/shared/main.cpp +++ b/ion/src/sdl/shared/main.cpp @@ -37,7 +37,7 @@ void init() { "Epsilon", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - 800, 600, + 290, 555, SDL_WINDOW_ALLOW_HIGHDPI #if EPSILON_SDL_FULLSCREEN | SDL_WINDOW_FULLSCREEN @@ -79,6 +79,8 @@ void refresh() { SDL_Rect backgroundRect; Layout::getBackgroundRect(&backgroundRect); + SDL_SetRenderDrawColor(sRenderer, 194, 194, 194, 255); + SDL_RenderClear(sRenderer); SDL_RenderCopy(sRenderer, sBackgroundTexture, nullptr, &backgroundRect); Display::draw(sRenderer, &screenRect); SDL_RenderPresent(sRenderer); From 87d0ccf0b216f860edafe0b98dc5f5d56e78e45b Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 8 Mar 2019 13:59:35 +0100 Subject: [PATCH 35/78] [ion] Share the keyboard-to-event code between platforms --- ion/src/device/Makefile | 3 +- ion/src/device/events_keyboard_platform.cpp | 29 +++++++++++ ion/src/sdl/Makefile | 4 +- ion/src/sdl/shared/events.cpp | 43 ---------------- .../sdl/shared/events_keyboard_platform.cpp | 28 +++++++++++ ion/src/sdl/shared/keyboard.cpp | 49 +++++++++++++++++++ .../events.cpp => shared/events_keyboard.cpp} | 26 +++------- 7 files changed, 119 insertions(+), 63 deletions(-) create mode 100644 ion/src/device/events_keyboard_platform.cpp delete mode 100644 ion/src/sdl/shared/events.cpp create mode 100644 ion/src/sdl/shared/events_keyboard_platform.cpp create mode 100644 ion/src/sdl/shared/keyboard.cpp rename ion/src/{device/events.cpp => shared/events_keyboard.cpp} (78%) diff --git a/ion/src/device/Makefile b/ion/src/device/Makefile index d40d8aa51..94ee65dc2 100644 --- a/ion/src/device/Makefile +++ b/ion/src/device/Makefile @@ -7,6 +7,7 @@ $(call object_for,ion/src/shared/platform_info.cpp): SFLAGS += -DHEADER_SECTION= src += $(addprefix ion/src/shared/, \ console_line.cpp \ crc32_padded.cpp \ + events_keyboard.cpp \ events_modifier.cpp \ ) @@ -20,7 +21,7 @@ src += $(addprefix ion/src/device/, \ console.cpp \ device.cpp \ display.cpp \ - events.cpp \ + events_keyboard_platform.cpp \ flash.cpp \ keyboard.cpp \ led.cpp \ diff --git a/ion/src/device/events_keyboard_platform.cpp b/ion/src/device/events_keyboard_platform.cpp new file mode 100644 index 000000000..f5e29798f --- /dev/null +++ b/ion/src/device/events_keyboard_platform.cpp @@ -0,0 +1,29 @@ +#include +#include + +namespace Ion { +namespace Events { + +bool sLastUSBPlugged = false; +bool sLastUSBEnumerated = false; + +Event getPlatformEvent() { + // First, check if the USB plugged status has changed + bool usbPlugged = USB::isPlugged(); + if (usbPlugged != sLastUSBPlugged) { + sLastUSBPlugged = usbPlugged; + return Events::USBPlug; + } + + // Second, check if the USB device has been connected to an USB host + bool usbEnumerated = USB::isEnumerated(); + if (usbEnumerated != sLastUSBEnumerated) { + sLastUSBEnumerated = usbEnumerated; + if (usbEnumerated) { + return Events::USBEnumeration; + } + } +} + +} +} diff --git a/ion/src/sdl/Makefile b/ion/src/sdl/Makefile index de3c5b517..c54ceca19 100644 --- a/ion/src/sdl/Makefile +++ b/ion/src/sdl/Makefile @@ -3,6 +3,7 @@ src += $(addprefix ion/src/shared/, \ crc32.cpp \ crc32_padded.cpp \ events.cpp \ + events_keyboard.cpp \ events_modifier.cpp \ power.cpp \ random.cpp \ @@ -18,7 +19,8 @@ src += $(addprefix ion/src/shared/, \ src += $(addprefix ion/src/sdl/shared/, \ display.cpp \ - events.cpp \ + events_keyboard_platform.cpp \ + keyboard.cpp \ main.cpp \ layout.cpp \ ) diff --git a/ion/src/sdl/shared/events.cpp b/ion/src/sdl/shared/events.cpp deleted file mode 100644 index d985c5925..000000000 --- a/ion/src/sdl/shared/events.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "main.h" -#include "layout.h" - -#include - -#include - -namespace Ion { -namespace Events { - -static bool needsRefresh = true; - -Event getEvent(int * timeout) { - - if (needsRefresh) { - Ion::SDL::Main::refresh(); - needsRefresh = false; - } - - SDL_Event event; - - if (!SDL_WaitEventTimeout(&event, *timeout)) { - return None; - } - - if (event.type == SDL_MOUSEBUTTONDOWN) { - SDL_Point p; - p.x = event.button.x; - p.y = event.button.y; - Keyboard::Key key = SDL::Layout::keyAt(&p); - if (key == Keyboard::Key::None) { - return None; - } - needsRefresh = true; - Event event = Event(key, isShiftActive(), isAlphaActive()); - return event; - } - - return None; -} - -} -} diff --git a/ion/src/sdl/shared/events_keyboard_platform.cpp b/ion/src/sdl/shared/events_keyboard_platform.cpp new file mode 100644 index 000000000..6ea4be589 --- /dev/null +++ b/ion/src/sdl/shared/events_keyboard_platform.cpp @@ -0,0 +1,28 @@ +#include "main.h" + +#include +#include + +namespace Ion { +namespace Events { + +Event getPlatformEvent() { + Ion::SDL::Main::refresh(); + + SDL_Event event; + while (SDL_PollEvent(&event)) { + // The while is important: it'll do a fast-pass over all useless SDL events + if (event.type == SDL_WINDOWEVENT) { + if (event.window.event == SDL_WINDOWEVENT_RESIZED) { + Ion::SDL::Main::relayout(); + } + } + if (event.type == SDL_QUIT) { + return Termination; + } + } + return None; +} + +} +} diff --git a/ion/src/sdl/shared/keyboard.cpp b/ion/src/sdl/shared/keyboard.cpp new file mode 100644 index 000000000..ae7d88a8c --- /dev/null +++ b/ion/src/sdl/shared/keyboard.cpp @@ -0,0 +1,49 @@ +#include "main.h" +#include "layout.h" + +#include +#include + +namespace Ion { +namespace Keyboard { + +State scan() { + // We need to tell SDL to get new state from the host OS + SDL_PumpEvents(); + + // Grab this opportunity to refresh the display if needed + Ion::SDL::Main::refresh(); + + // Start with a "clean" state + State state; + + // Register a key for the mouse, if any + SDL_Point p; + Uint32 mouseState = SDL_GetMouseState(&p.x, &p.y); + if (mouseState & SDL_BUTTON(SDL_BUTTON_LEFT)) { + Key k = SDL::Layout::keyAt(&p); + state.setKey(k); + } + +#if 0 + // SDL exposes the first finger as a mouse! + // Register a key for each finger, if any + int numberOfTouchDevices = SDL_GetNumTouchDevices(); + for (int i=0; i +#include +#include #include namespace Ion { @@ -18,16 +19,16 @@ static bool sleepWithTimeout(int duration, int * timeout) { Event sLastEvent = Events::None; Keyboard::State sLastKeyboardState; -bool sLastUSBPlugged = false; -bool sLastUSBEnumerated = false; bool sEventIsRepeating = 0; constexpr int delayBeforeRepeat = 200; constexpr int delayBetweenRepeat = 50; -bool canRepeatEvent(Event e) { +static bool canRepeatEvent(Event e) { return (e == Events::Left || e == Events::Up || e == Events::Down || e == Events::Right || e == Events::Backspace); } +Event getPlatformEvent(); + Event getEvent(int * timeout) { assert(*timeout > delayBeforeRepeat); assert(*timeout > delayBetweenRepeat); @@ -35,20 +36,9 @@ Event getEvent(int * timeout) { uint64_t keysSeenUp = 0; uint64_t keysSeenTransitionningFromUpToDown = 0; while (true) { - // First, check if the USB plugged status has changed - bool usbPlugged = USB::isPlugged(); - if (usbPlugged != sLastUSBPlugged) { - sLastUSBPlugged = usbPlugged; - return Events::USBPlug; - } - - // Second, check if the USB device has been connected to an USB host - bool usbEnumerated = USB::isEnumerated(); - if (usbEnumerated != sLastUSBEnumerated) { - sLastUSBEnumerated = usbEnumerated; - if (usbEnumerated) { - return Events::USBEnumeration; - } + Event platformEvent = getPlatformEvent(); + if (platformEvent != None) { + return platformEvent; } Keyboard::State state = Keyboard::scan(); From adf490170873c9eb2cb175dec05eae9787165264 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 8 Mar 2019 14:04:58 +0100 Subject: [PATCH 36/78] [ion/src] Avoid refreshing the screen when not needed --- ion/src/sdl/shared/display.cpp | 20 ++++++++----------- .../sdl/shared/events_keyboard_platform.cpp | 2 -- ion/src/sdl/shared/keyboard.cpp | 2 +- ion/src/sdl/shared/main.cpp | 10 +++++++++- ion/src/sdl/shared/main.h | 1 + 5 files changed, 19 insertions(+), 16 deletions(-) diff --git a/ion/src/sdl/shared/display.cpp b/ion/src/sdl/shared/display.cpp index 71440e6ad..7302cb3c1 100644 --- a/ion/src/sdl/shared/display.cpp +++ b/ion/src/sdl/shared/display.cpp @@ -13,7 +13,6 @@ * rather expensive. */ static KDColor sPixels[Ion::Display::Width * Ion::Display::Height]; -static bool sFramebufferNeedsRedraw = false; namespace Ion { namespace Display { @@ -21,12 +20,12 @@ namespace Display { static KDFrameBuffer sFrameBuffer = KDFrameBuffer(sPixels, KDSize(Ion::Display::Width, Ion::Display::Height)); void pushRect(KDRect r, const KDColor * pixels) { - sFramebufferNeedsRedraw = true; + SDL::Main::setNeedsRefresh(); sFrameBuffer.pushRect(r, pixels); } void pushRectUniform(KDRect r, KDColor c) { - sFramebufferNeedsRedraw = true; + SDL::Main::setNeedsRefresh(); sFrameBuffer.pushRectUniform(r, c); } @@ -63,15 +62,12 @@ void quit() { } void draw(SDL_Renderer * renderer, SDL_Rect * rect) { - if (sFramebufferNeedsRedraw) { - int pitch = 0; - void * pixels = nullptr; - SDL_LockTexture(sFramebufferTexture, nullptr, &pixels, &pitch); - assert(pitch == 2*Ion::Display::Width); - memcpy(pixels, sPixels, sizeof(sPixels)); - SDL_UnlockTexture(sFramebufferTexture); - } - sFramebufferNeedsRedraw = false; + int pitch = 0; + void * pixels = nullptr; + SDL_LockTexture(sFramebufferTexture, nullptr, &pixels, &pitch); + assert(pitch == 2*Ion::Display::Width); + memcpy(pixels, sPixels, sizeof(sPixels)); + SDL_UnlockTexture(sFramebufferTexture); SDL_RenderCopy(renderer, sFramebufferTexture, nullptr, rect); } diff --git a/ion/src/sdl/shared/events_keyboard_platform.cpp b/ion/src/sdl/shared/events_keyboard_platform.cpp index 6ea4be589..b533130c1 100644 --- a/ion/src/sdl/shared/events_keyboard_platform.cpp +++ b/ion/src/sdl/shared/events_keyboard_platform.cpp @@ -7,8 +7,6 @@ namespace Ion { namespace Events { Event getPlatformEvent() { - Ion::SDL::Main::refresh(); - SDL_Event event; while (SDL_PollEvent(&event)) { // The while is important: it'll do a fast-pass over all useless SDL events diff --git a/ion/src/sdl/shared/keyboard.cpp b/ion/src/sdl/shared/keyboard.cpp index ae7d88a8c..ed416c889 100644 --- a/ion/src/sdl/shared/keyboard.cpp +++ b/ion/src/sdl/shared/keyboard.cpp @@ -12,7 +12,7 @@ State scan() { SDL_PumpEvents(); // Grab this opportunity to refresh the display if needed - Ion::SDL::Main::refresh(); + SDL::Main::refresh(); // Start with a "clean" state State state; diff --git a/ion/src/sdl/shared/main.cpp b/ion/src/sdl/shared/main.cpp index 7642e2e87..08789bd6f 100644 --- a/ion/src/sdl/shared/main.cpp +++ b/ion/src/sdl/shared/main.cpp @@ -26,6 +26,7 @@ namespace Main { static SDL_Window * sWindow = nullptr; static SDL_Renderer * sRenderer = nullptr; static SDL_Texture * sBackgroundTexture = nullptr; +static bool sNeedsRefresh = false; void init() { if (SDL_Init(SDL_INIT_VIDEO) != 0) { @@ -70,10 +71,17 @@ void relayout() { SDL_RenderCopy(sRenderer, sBackgroundTexture, nullptr, &backgroundRect); SDL_RenderPresent(sRenderer); - refresh(); + setNeedsRefresh(); +} + +void setNeedsRefresh() { + sNeedsRefresh = true; } void refresh() { + if (!sNeedsRefresh) { + return; + } SDL_Rect screenRect; Layout::getScreenRect(&screenRect); SDL_Rect backgroundRect; diff --git a/ion/src/sdl/shared/main.h b/ion/src/sdl/shared/main.h index c6950da6a..8d5959017 100644 --- a/ion/src/sdl/shared/main.h +++ b/ion/src/sdl/shared/main.h @@ -8,6 +8,7 @@ namespace Main { void init(); void quit(); +void setNeedsRefresh(); void refresh(); void relayout(); From 9bd4922389f61fecabf3ad5ab1de200ccca6508b Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 8 Mar 2019 14:05:23 +0100 Subject: [PATCH 37/78] [ion] Define Ion::Keyboard::Key::None --- ion/include/ion/keyboard.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ion/include/ion/keyboard.h b/ion/include/ion/keyboard.h index 73581a5fa..384954d14 100644 --- a/ion/include/ion/keyboard.h +++ b/ion/include/ion/keyboard.h @@ -19,6 +19,7 @@ enum class Key : uint8_t { G1=36, G2=37, G3=38, G4=39, G5=40, // G6=41, H1=42, H2=43, H3=44, H4=45, H5=46, // H6=47, I1=48, I2=49, I3=50, I4=51, I5=52, // I6=53, + None = 54 }; constexpr Key ValidKeys[] = { From 92471be2dbc609350af199aed8abb8dc7ef6c204 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 8 Mar 2019 14:06:05 +0100 Subject: [PATCH 38/78] [ion/sdl] Remove dead code --- ion/src/sdl/shared/display.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/ion/src/sdl/shared/display.cpp b/ion/src/sdl/shared/display.cpp index 7302cb3c1..c2461ae8e 100644 --- a/ion/src/sdl/shared/display.cpp +++ b/ion/src/sdl/shared/display.cpp @@ -43,7 +43,6 @@ namespace Ion { namespace SDL { namespace Display { -static SDL_Surface * sFramebufferSurface = nullptr; static SDL_Texture * sFramebufferTexture = nullptr; void init(SDL_Renderer * renderer) { From eb8061a2dc30c39a81a07983afe951170f74db1e Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 8 Mar 2019 14:24:01 +0100 Subject: [PATCH 39/78] [ion/sdl] Support keyboard events --- .../sdl/shared/events_keyboard_platform.cpp | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/ion/src/sdl/shared/events_keyboard_platform.cpp b/ion/src/sdl/shared/events_keyboard_platform.cpp index b533130c1..0efe4f631 100644 --- a/ion/src/sdl/shared/events_keyboard_platform.cpp +++ b/ion/src/sdl/shared/events_keyboard_platform.cpp @@ -1,11 +1,128 @@ #include "main.h" +#include #include #include namespace Ion { namespace Events { + + +static Event eventFromSDLKeyboardEvent(SDL_KeyboardEvent event) { + if (event.keysym.mod & KMOD_CTRL) { + switch (event.keysym.sym) { + case SDLK_BACKSPACE: + return Clear; + case SDLK_x: + return Cut; + case SDLK_c: + return Copy; + case SDLK_v: + return Paste; + } + } + if (event.keysym.mod & KMOD_ALT) { + if (event.keysym.mod & KMOD_SHIFT) { + switch (event.keysym.sym) { + case SDLK_s: + return Arcsine; + case SDLK_c: + return Arccosine; + case SDLK_t: + return Arctangent; + } + } + switch (event.keysym.sym) { + case SDLK_ESCAPE: + return Home; + case SDLK_RETURN: + return OK; + case SDLK_v: + return Var; + case SDLK_BACKSPACE: + return Clear; + case SDLK_x: + return Exp; + case SDLK_n: + return Ln; + case SDLK_l: + return Log; + case SDLK_i: + return Imaginary; + case SDLK_EQUALS: + return Sto; + case SDLK_s: + return Sine; + case SDLK_c: + return Cosine; + case SDLK_t: + return Tangent; + case SDLK_p: + return Pi; + case SDLK_r: + return Sqrt; + case SDLK_2: + return Square; + case SDLK_e: + return EE; + case SDLK_a: + return Ans; + } + } + switch(event.keysym.sym) { + case SDLK_UP: + return Up; + case SDLK_DOWN: + return Down; + case SDLK_LEFT: + return Left; + case SDLK_RIGHT: + return Right; + case SDLK_RETURN: + return EXE; + case SDLK_ESCAPE: + return Back; + case SDLK_TAB: + return Toolbox; + case SDLK_BACKSPACE: + return Backspace; + } + /* + if (event.keysym.unicode >= 32 && event.keysym.unicode < 127) { + return sEventForASCIICharAbove32[event.keysym.unicode-32]; + } + */ + return None; +} + +static constexpr Event sEventForASCIICharAbove32[95] = { + Space, Exclamation, DoubleQuotes, None, None, None, None, None, + LeftParenthesis, RightParenthesis, Multiplication, Plus, Comma, Minus, Dot, Division, + Zero, One, Two, Three, Four, Five, Six, Seven, + Eight, Nine, Colon, SemiColon, Lower, Equal, Greater, Question, + None, UpperA, UpperB, UpperC, UpperD, UpperE, UpperF, UpperG, + UpperH, UpperI, UpperJ, UpperK, UpperL, UpperM, UpperN, UpperO, + UpperP, UpperQ, UpperR, UpperS, UpperT, UpperU, UpperV, UpperW, + UpperX, UpperY, UpperZ, LeftBracket, None, RightBracket, Power, Underscore, + None, LowerA, LowerB, LowerC, LowerD, LowerE, LowerF, LowerG, + LowerH, LowerI, LowerJ, LowerK, LowerL, LowerM, LowerN, LowerO, + LowerP, LowerQ, LowerR, LowerS, LowerT, LowerU, LowerV, LowerW, + LowerX, LowerY, LowerZ, LeftBrace, None, RightBrace, None +}; + +static Event eventFromSDLTextInputEvent(SDL_TextInputEvent event) { + if (strlen(event.text) != 1) { + return None; + } + char character = event.text[0]; + if (character >= 32 && character < 127) { + return sEventForASCIICharAbove32[character-32]; + } + return None; +} + + Event getPlatformEvent() { SDL_Event event; while (SDL_PollEvent(&event)) { @@ -18,6 +135,12 @@ Event getPlatformEvent() { if (event.type == SDL_QUIT) { return Termination; } + if (event.type == SDL_KEYDOWN) { + return eventFromSDLKeyboardEvent(event.key); + } + if (event.type == SDL_TEXTINPUT) { + return eventFromSDLTextInputEvent(event.text); + } } return None; } From c7187926573bd7e6a7c883d64c997151d4aefa05 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 8 Mar 2019 17:19:52 +0100 Subject: [PATCH 40/78] [ion/sdl/ios] Add a launchscreen --- ion/src/sdl/ios/Info.plist | 28 +++------------------------- ion/src/sdl/ios/launch.storyboard | 25 +++++++++++++++++++++++++ scripts/targets.sdl.ios.mak | 2 +- 3 files changed, 29 insertions(+), 26 deletions(-) create mode 100644 ion/src/sdl/ios/launch.storyboard diff --git a/ion/src/sdl/ios/Info.plist b/ion/src/sdl/ios/Info.plist index f58788bc5..ae06bbe5a 100644 --- a/ion/src/sdl/ios/Info.plist +++ b/ion/src/sdl/ios/Info.plist @@ -49,31 +49,9 @@ armv7 + UILaunchStoryboardName + launch UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - FOO-UILaunchImages - - - UILaunchImageMinimumOSVersion - 8.0 - UILaunchImageName - Default - UILaunchImageOrientation - Portrait - UILaunchImageSize - {828, 1792} - - + diff --git a/ion/src/sdl/ios/launch.storyboard b/ion/src/sdl/ios/launch.storyboard new file mode 100644 index 000000000..847eeb538 --- /dev/null +++ b/ion/src/sdl/ios/launch.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/targets.sdl.ios.mak b/scripts/targets.sdl.ios.mak index d26ede79b..87513aa8f 100644 --- a/scripts/targets.sdl.ios.mak +++ b/scripts/targets.sdl.ios.mak @@ -13,7 +13,7 @@ $(BUILD_DIR)/app/Epsilon.app: $(patsubst %,$(BUILD_DIR)/%/epsilon.bin,$(ARCHS)) lipo -create $^ -output $@/Epsilon cp ion/src/sdl/ios/Info.plist $@/ cp ion/src/sdl/assets/* $@/ - # TODO: Generate launch images + ibtool --compile $@/launch.storyboardc ion/src/sdl/ios/launch.storyboard epsilon_run: xcrun simctl install C75D15A1-45F7-4F94-8DBD-6D02A95C9514 $(BUILD_DIR)/app/Epsilon.app From a214e9d6f94e8eba79b177045b6023a1f68f0c65 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 8 Mar 2019 17:20:15 +0100 Subject: [PATCH 41/78] [ion/sdl] Proper dependency tracking for multi-arch projects --- scripts/targets.sdl.android.mak | 18 +++++++++++------- scripts/targets.sdl.ios.mak | 4 +++- scripts/targets.sdl.macos.mak | 4 +++- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/scripts/targets.sdl.android.mak b/scripts/targets.sdl.android.mak index 35a2662a3..4e3f87137 100644 --- a/scripts/targets.sdl.android.mak +++ b/scripts/targets.sdl.android.mak @@ -1,7 +1,13 @@ -$(BUILD_DIR)/app/libs/%/libepsilon.so: $$(@D)/. +# Makefile is loaded for a single architecture at once. If NDK_ABI is not +# defined, we assume we want to build all of them. But in that case, we cannot +# track per-ABI dependencies. So as a shortcut, we force a re-make of each ABI. + +.PHONY: force_rebuild_libepsilon + +$(BUILD_DIR)/app/libs/%/libepsilon.so: force_rebuild_libepsilon $(Q) echo "MAKE NDK_ABI=$*" $(Q) $(MAKE) NDK_ABI=$* epsilon.so - $(Q) cp build/sdl/android/$*/epsilon.so $@ + $(Q) cp $(BUILD_DIR)/$*/epsilon.so $@ NDK_ABIS = arm64-v8a x86_64 @@ -11,8 +17,6 @@ epsilon.apk: $(patsubst %,$(BUILD_DIR)/app/libs/%/libepsilon.so,$(NDK_ABIS)) $(Q) ANDROID_HOME=/usr/local/android gradle -b ion/src/sdl/android/build.gradle assembleRelease .PHONY: epsilon_run -epsilon_run: - ANDROID_HOME=/usr/local/android gradle -b ion/src/sdl/android/build.gradle assembleRelease - #rm ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so - #$(MAKE) ion/src/sdl/android/com.numworks.calculator/app/libs/arm64-v8a/libepsilon.so - cd ion/src/sdl/android ; ANDROID_HOME=/usr/local/android gradle installDebug +epsilon_run: $(patsubst %,$(BUILD_DIR)/app/libs/%/libepsilon.so,$(NDK_ABIS)) + @echo "GRADLE ion/src/sdl/android/build.gradle" + $(Q) ANDROID_HOME=/usr/local/android gradle -b ion/src/sdl/android/build.gradle installDebug diff --git a/scripts/targets.sdl.ios.mak b/scripts/targets.sdl.ios.mak index 87513aa8f..9fe75ee10 100644 --- a/scripts/targets.sdl.ios.mak +++ b/scripts/targets.sdl.ios.mak @@ -1,4 +1,6 @@ -$(BUILD_DIR)/%/epsilon.bin: +.PHONY: force_rebuild_libepsilon + +$(BUILD_DIR)/%/epsilon.bin: force_rebuild_libepsilon $(Q) echo "MAKE ARCH=$*" $(Q) $(MAKE) ARCH=$* diff --git a/scripts/targets.sdl.macos.mak b/scripts/targets.sdl.macos.mak index 8c96cb868..c2210583a 100644 --- a/scripts/targets.sdl.macos.mak +++ b/scripts/targets.sdl.macos.mak @@ -1,4 +1,6 @@ -$(BUILD_DIR)/%/epsilon.bin: +.PHONY: force_rebuild_libepsilon + +$(BUILD_DIR)/%/epsilon.bin: force_rebuild_libepsilon $(Q) echo "MAKE ARCH=$*" $(Q) $(MAKE) ARCH=$* From bf2c0459b3b1af707d3a29c1f71e5fd4c1c874fe Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 8 Mar 2019 17:21:09 +0100 Subject: [PATCH 42/78] [scripts] Fix a typo --- scripts/toolchain.windows.mak | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/toolchain.windows.mak b/scripts/toolchain.windows.mak index 29376ccf4..f8ff215c6 100644 --- a/scripts/toolchain.windows.mak +++ b/scripts/toolchain.windows.mak @@ -6,7 +6,7 @@ EXE = exe SFLAGS += -D_USE_MATH_DEFINES LDFLAGS += -static -ifeq ($(DEBUG),1) +ifeq ($(DEBUG),0) # Defining "-mwindows" prevents the app from launching an instance of cmd.exe # when running the app. That terminal will receive stderr and stdout. The # downside of not defining "-mwindows" is that you lose stdout and stderr. From 9c9da1ff197eb2e74693a726c4460a89589d161c Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 8 Mar 2019 17:23:23 +0100 Subject: [PATCH 43/78] [ion/sdl/ios] Target the booted simulator --- scripts/targets.sdl.ios.mak | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/targets.sdl.ios.mak b/scripts/targets.sdl.ios.mak index 9fe75ee10..fca46bc5d 100644 --- a/scripts/targets.sdl.ios.mak +++ b/scripts/targets.sdl.ios.mak @@ -17,5 +17,5 @@ $(BUILD_DIR)/app/Epsilon.app: $(patsubst %,$(BUILD_DIR)/%/epsilon.bin,$(ARCHS)) cp ion/src/sdl/assets/* $@/ ibtool --compile $@/launch.storyboardc ion/src/sdl/ios/launch.storyboard -epsilon_run: - xcrun simctl install C75D15A1-45F7-4F94-8DBD-6D02A95C9514 $(BUILD_DIR)/app/Epsilon.app +epsilon_run: $(BUILD_DIR)/app/Epsilon.app + xcrun simctl install booted $(BUILD_DIR)/app/Epsilon.app From 535406d9cf30ef8312880c0928bac2f22ab1f3fd Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 8 Mar 2019 18:10:18 +0100 Subject: [PATCH 44/78] [ion/sdl] Add SDL build scripts --- ion/src/sdl/external/Makefile | 97 +++++++++++++++++++++++++ ion/src/sdl/external/config.android.mak | 39 ++++++++++ ion/src/sdl/external/config.ios.mak | 44 +++++++++++ ion/src/sdl/external/config.macos.mak | 65 +++++++++++++++++ ion/src/sdl/external/config.windows.mak | 59 +++++++++++++++ 5 files changed, 304 insertions(+) create mode 100644 ion/src/sdl/external/Makefile create mode 100644 ion/src/sdl/external/config.android.mak create mode 100644 ion/src/sdl/external/config.ios.mak create mode 100644 ion/src/sdl/external/config.macos.mak create mode 100644 ion/src/sdl/external/config.windows.mak diff --git a/ion/src/sdl/external/Makefile b/ion/src/sdl/external/Makefile new file mode 100644 index 000000000..998817a7d --- /dev/null +++ b/ion/src/sdl/external/Makefile @@ -0,0 +1,97 @@ +SFLAGS += -Iion/src/sdl/external/sdl/include + +sdl_src += $(addprefix ion/src/sdl/external/sdl/src/, \ + SDL.c \ + SDL_assert.c \ + SDL_dataqueue.c \ + SDL_error.c \ + SDL_hints.c \ + SDL_log.c \ + atomic/SDL_atomic.c \ + atomic/SDL_spinlock.c \ + audio/SDL_audio.c \ + audio/SDL_audiocvt.c \ + audio/SDL_audiodev.c \ + audio/SDL_audiotypecvt.c \ + audio/SDL_mixer.c \ + audio/SDL_wave.c \ + audio/dummy/SDL_dummyaudio.c \ + cpuinfo/SDL_cpuinfo.c \ + dynapi/SDL_dynapi.c \ + events/SDL_clipboardevents.c \ + events/SDL_displayevents.c \ + events/SDL_dropevents.c \ + events/SDL_events.c \ + events/SDL_gesture.c \ + events/SDL_keyboard.c \ + events/SDL_mouse.c \ + events/SDL_quit.c \ + events/SDL_touch.c \ + events/SDL_windowevents.c \ + file/SDL_rwops.c \ + haptic/SDL_haptic.c \ + haptic/dummy/SDL_syshaptic.c \ + joystick/SDL_gamecontroller.c \ + joystick/SDL_joystick.c \ + joystick/hidapi/SDL_hidapi_ps4.c \ + joystick/hidapi/SDL_hidapi_switch.c \ + joystick/hidapi/SDL_hidapi_xbox360.c \ + joystick/hidapi/SDL_hidapi_xboxone.c \ + joystick/hidapi/SDL_hidapijoystick.c \ + power/SDL_power.c \ + render/SDL_render.c \ + render/SDL_yuv_sw.c \ + render/opengl/SDL_render_gl.c \ + render/opengl/SDL_shaders_gl.c \ + render/opengles/SDL_render_gles.c \ + render/opengles2/SDL_render_gles2.c \ + render/opengles2/SDL_shaders_gles2.c \ + render/software/SDL_blendfillrect.c \ + render/software/SDL_blendline.c \ + render/software/SDL_blendpoint.c \ + render/software/SDL_drawline.c \ + render/software/SDL_drawpoint.c \ + render/software/SDL_render_sw.c \ + render/software/SDL_rotate.c \ + sensor/SDL_sensor.c \ + sensor/dummy/SDL_dummysensor.c \ + stdlib/SDL_getenv.c \ + stdlib/SDL_iconv.c \ + stdlib/SDL_malloc.c \ + stdlib/SDL_qsort.c \ + stdlib/SDL_stdlib.c \ + stdlib/SDL_string.c \ + thread/SDL_thread.c \ + timer/SDL_timer.c \ + video/SDL_RLEaccel.c \ + video/SDL_blit.c \ + video/SDL_blit_0.c \ + video/SDL_blit_1.c \ + video/SDL_blit_A.c \ + video/SDL_blit_N.c \ + video/SDL_blit_auto.c \ + video/SDL_blit_copy.c \ + video/SDL_blit_slow.c \ + video/SDL_bmp.c \ + video/SDL_clipboard.c \ + video/SDL_egl.c \ + video/SDL_fillrect.c \ + video/SDL_pixels.c \ + video/SDL_rect.c \ + video/SDL_shape.c \ + video/SDL_stretch.c \ + video/SDL_surface.c \ + video/SDL_video.c \ + video/SDL_vulkan_utils.c \ + video/SDL_yuv.c \ + video/dummy/SDL_nullevents.c \ + video/dummy/SDL_nullframebuffer.c \ + video/dummy/SDL_nullvideo.c \ + video/yuv2rgb/yuv_rgb.c \ +) + +include ion/src/sdl/external/config.$(MODEL).mak + +$(call object_for,$(sdl_src)): SFLAGS += $(SDL_SFLAGS) + +src += $(sdl_src) diff --git a/ion/src/sdl/external/config.android.mak b/ion/src/sdl/external/config.android.mak new file mode 100644 index 000000000..8bee64b45 --- /dev/null +++ b/ion/src/sdl/external/config.android.mak @@ -0,0 +1,39 @@ +SDL_SFLAGS = -DGL_GLEXT_PROTOTYPES + +sdl_src += $(addprefix ion/src/sdl/external/sdl/src/, \ + audio/android/SDL_androidaudio.c \ + audio/openslES/SDL_openslES.c \ + core/android/SDL_android.c \ + filesystem/android/SDL_sysfilesystem.c \ + haptic/android/SDL_syshaptic.c \ + joystick/android/SDL_sysjoystick.c \ + loadso/dlopen/SDL_sysloadso.c \ + power/android/SDL_syspower.c \ + sensor/android/SDL_androidsensor.c \ + thread/pthread/SDL_syscond.c \ + thread/pthread/SDL_sysmutex.c \ + thread/pthread/SDL_syssem.c \ + thread/pthread/SDL_systhread.c \ + thread/pthread/SDL_systls.c \ + timer/unix/SDL_systimer.c \ + video/android/SDL_androidclipboard.c \ + video/android/SDL_androidevents.c \ + video/android/SDL_androidgl.c \ + video/android/SDL_androidkeyboard.c \ + video/android/SDL_androidmessagebox.c \ + video/android/SDL_androidmouse.c \ + video/android/SDL_androidtouch.c \ + video/android/SDL_androidvideo.c \ + video/android/SDL_androidvulkan.c \ + video/android/SDL_androidwindow.c \ +) + +sdl_src += ion/src/sdl/external/sdl/src/hidapi/android/hid.c + +LDFLAGS += -lGLESv1_CM +LDFLAGS += -lGLESv2 +LDFLAGS += -lOpenSLES +LDFLAGS += -landroid +LDFLAGS += -ldl +LDFLAGS += -ljnigraphics +LDFLAGS += -llog diff --git a/ion/src/sdl/external/config.ios.mak b/ion/src/sdl/external/config.ios.mak new file mode 100644 index 000000000..8f24e926f --- /dev/null +++ b/ion/src/sdl/external/config.ios.mak @@ -0,0 +1,44 @@ +# This file is built manually by mimic-ing SDL's Xcode project + +SDL_SFLAGS = -fasm-blocks -fstrict-aliasing -fobjc-arc + +sdl_src += $(addprefix ion/src/sdl/external/sdl/src/, \ + audio/coreaudio/SDL_coreaudio.m \ + file/cocoa/SDL_rwopsbundlesupport.m \ + filesystem/cocoa/SDL_sysfilesystem.m \ + joystick/iphoneos/SDL_sysjoystick.m \ + loadso/dlopen/SDL_sysloadso.c \ + power/uikit/SDL_syspower.m \ + render/metal/SDL_render_metal.m \ + sensor/coremotion/SDL_coremotionsensor.m \ + thread/pthread/SDL_syscond.c \ + thread/pthread/SDL_sysmutex.c \ + thread/pthread/SDL_syssem.c \ + thread/pthread/SDL_systhread.c \ + thread/pthread/SDL_systls.c \ + timer/unix/SDL_systimer.c \ + video/uikit/SDL_uikitappdelegate.m \ + video/uikit/SDL_uikitclipboard.m \ + video/uikit/SDL_uikitevents.m \ + video/uikit/SDL_uikitmessagebox.m \ + video/uikit/SDL_uikitmetalview.m \ + video/uikit/SDL_uikitmodes.m \ + video/uikit/SDL_uikitopengles.m \ + video/uikit/SDL_uikitopenglview.m \ + video/uikit/SDL_uikitvideo.m \ + video/uikit/SDL_uikitview.m \ + video/uikit/SDL_uikitviewcontroller.m \ + video/uikit/SDL_uikitvulkan.m \ + video/uikit/SDL_uikitwindow.m \ +) + +LDFLAGS += -framework AVFoundation +LDFLAGS += -framework AudioToolbox +LDFLAGS += -framework CoreGraphics +LDFLAGS += -framework CoreMotion +LDFLAGS += -framework Foundation +LDFLAGS += -framework GameController +LDFLAGS += -framework Metal +LDFLAGS += -framework OpenGLES +LDFLAGS += -framework QuartzCore +LDFLAGS += -framework UIKit diff --git a/ion/src/sdl/external/config.macos.mak b/ion/src/sdl/external/config.macos.mak new file mode 100644 index 000000000..edd4a9498 --- /dev/null +++ b/ion/src/sdl/external/config.macos.mak @@ -0,0 +1,65 @@ +# This file is built manually by mimic-ing SDL's Xcode project + +SDL_SFLAGS = -fasm-blocks -fstrict-aliasing +SDL_SFLAGS += -Iion/src/sdl/external/sdl/src/video/khronos +SDL_SFLAGS += -Iion/src/sdl/external/sdl/src/hidapi/hidapi + +sdl_src += $(addprefix ion/src/sdl/external/sdl/src/, \ + audio/coreaudio/SDL_coreaudio.m \ + audio/disk/SDL_diskaudio.c \ + file/cocoa/SDL_rwopsbundlesupport.m \ + filesystem/cocoa/SDL_sysfilesystem.m \ + haptic/darwin/SDL_syshaptic.c \ + hidapi/mac/hid.c \ + joystick/darwin/SDL_sysjoystick.c \ + loadso/dlopen/SDL_sysloadso.c \ + power/macosx/SDL_syspower.c \ + render/metal/SDL_render_metal.m \ + thread/pthread/SDL_syscond.c \ + thread/pthread/SDL_sysmutex.c \ + thread/pthread/SDL_syssem.c \ + thread/pthread/SDL_systhread.c \ + thread/pthread/SDL_systls.c \ + timer/unix/SDL_systimer.c \ + video/cocoa/SDL_cocoaclipboard.m \ + video/cocoa/SDL_cocoaevents.m \ + video/cocoa/SDL_cocoakeyboard.m \ + video/cocoa/SDL_cocoamessagebox.m \ + video/cocoa/SDL_cocoametalview.m \ + video/cocoa/SDL_cocoamodes.m \ + video/cocoa/SDL_cocoamouse.m \ + video/cocoa/SDL_cocoamousetap.m \ + video/cocoa/SDL_cocoaopengl.m \ + video/cocoa/SDL_cocoaopengles.m \ + video/cocoa/SDL_cocoashape.m \ + video/cocoa/SDL_cocoavideo.m \ + video/cocoa/SDL_cocoavulkan.m \ + video/cocoa/SDL_cocoawindow.m \ + video/x11/SDL_x11clipboard.c \ + video/x11/SDL_x11dyn.c \ + video/x11/SDL_x11events.c \ + video/x11/SDL_x11framebuffer.c \ + video/x11/SDL_x11keyboard.c \ + video/x11/SDL_x11messagebox.c \ + video/x11/SDL_x11modes.c \ + video/x11/SDL_x11mouse.c \ + video/x11/SDL_x11opengl.c \ + video/x11/SDL_x11opengles.c \ + video/x11/SDL_x11shape.c \ + video/x11/SDL_x11touch.c \ + video/x11/SDL_x11video.c \ + video/x11/SDL_x11window.c \ + video/x11/SDL_x11xinput2.c \ + video/x11/imKStoUCS.c \ +) + +LDFLAGS += -weak_framework Metal +LDFLAGS += -weak_framework QuartzCore +LDFLAGS += -framework CoreAudio +LDFLAGS += -framework CoreVideo +LDFLAGS += -framework Cocoa +LDFLAGS += -framework IOKit +LDFLAGS += -framework CoreFoundation +LDFLAGS += -framework Carbon +LDFLAGS += -framework ForceFeedback +LDFLAGS += -framework AudioToolbox diff --git a/ion/src/sdl/external/config.windows.mak b/ion/src/sdl/external/config.windows.mak new file mode 100644 index 000000000..e80c55f60 --- /dev/null +++ b/ion/src/sdl/external/config.windows.mak @@ -0,0 +1,59 @@ +SDL_SFLAGS += -Iion/src/sdl/external/sdl/src/video/khronos +SDL_SFLAGS += -DHAVE_LIBC + +sdl_src += $(addprefix ion/src/sdl/external/sdl/src/, \ + audio/directsound/SDL_directsound.c \ + audio/disk/SDL_diskaudio.c \ + audio/wasapi/SDL_wasapi.c \ + audio/wasapi/SDL_wasapi_win32.c \ + audio/winmm/SDL_winmm.c \ + core/windows/SDL_windows.c \ + core/windows/SDL_xinput.c \ + filesystem/windows/SDL_sysfilesystem.c \ + haptic/windows/SDL_dinputhaptic.c \ + haptic/windows/SDL_windowshaptic.c \ + haptic/windows/SDL_xinputhaptic.c \ + hidapi/windows/hid.c \ + joystick/windows/SDL_dinputjoystick.c \ + joystick/windows/SDL_mmjoystick.c \ + joystick/windows/SDL_windowsjoystick.c \ + joystick/windows/SDL_xinputjoystick.c \ + loadso/windows/SDL_sysloadso.c \ + main/windows/SDL_windows_main.c \ + power/windows/SDL_syspower.c \ + render/direct3d/SDL_render_d3d.c \ + render/direct3d/SDL_shaders_d3d.c \ + render/direct3d11/SDL_render_d3d11.c \ + render/direct3d11/SDL_shaders_d3d11.c \ + render/SDL_d3dmath.c \ + thread/generic/SDL_syscond.c \ + thread/windows/SDL_sysmutex.c \ + thread/windows/SDL_syssem.c \ + thread/windows/SDL_systhread.c \ + thread/windows/SDL_systls.c \ + timer/windows/SDL_systimer.c \ + video/windows/SDL_windowsclipboard.c \ + video/windows/SDL_windowsevents.c \ + video/windows/SDL_windowsframebuffer.c \ + video/windows/SDL_windowskeyboard.c \ + video/windows/SDL_windowsmessagebox.c \ + video/windows/SDL_windowsmodes.c \ + video/windows/SDL_windowsmouse.c \ + video/windows/SDL_windowsopengl.c \ + video/windows/SDL_windowsopengles.c \ + video/windows/SDL_windowsshape.c \ + video/windows/SDL_windowsvideo.c \ + video/windows/SDL_windowsvulkan.c \ + video/windows/SDL_windowswindow.c \ +) + +LDFLAGS += -lgdi32 +LDFLAGS += -limm32 +LDFLAGS += -lole32 +LDFLAGS += -loleaut32 +LDFLAGS += -lsetupapi +LDFLAGS += -lshell32 +LDFLAGS += -luser32 +LDFLAGS += -lversion +LDFLAGS += -lwinmm +LDFLAGS += -static-libgcc From 937c6c5775d45e5939136796585c7e1f1264d6b8 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 8 Mar 2019 18:11:16 +0100 Subject: [PATCH 45/78] [ion/sdl] Add a background image asset --- ion/src/sdl/assets/background.jpg | Bin 0 -> 320442 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 ion/src/sdl/assets/background.jpg diff --git a/ion/src/sdl/assets/background.jpg b/ion/src/sdl/assets/background.jpg new file mode 100644 index 0000000000000000000000000000000000000000..95e5c82146342e791278e29e85d5d7af8410bb01 GIT binary patch literal 320442 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�oA9#|cP#yDd;|El@snim#5g)l*ZC763A$6hJ0W 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 z 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 z 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 >qt=tFR(#5dT*|nuw4lc9sO#0B!5>t_#R&k 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 literal 0 HcmV?d00001 From 880851f75b280b3c297b70b7ca8e43477652a807 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 8 Mar 2019 18:17:12 +0100 Subject: [PATCH 46/78] [ion/sdl] Import SDL version 12616:8a160ecca90f --- .../src/java/org/libsdl/app/HIDDevice.java | 19 + .../app/HIDDeviceBLESteamController.java | 644 + .../java/org/libsdl/app/HIDDeviceManager.java | 682 + .../src/java/org/libsdl/app/HIDDeviceUSB.java | 307 + .../android/src/java/org/libsdl/app/SDL.java | 84 + .../src/java/org/libsdl/app/SDLActivity.java | 2157 + .../java/org/libsdl/app/SDLAudioManager.java | 387 + .../org/libsdl/app/SDLControllerManager.java | 800 + ion/src/sdl/external/README.md | 2 + ion/src/sdl/external/sdl/include/SDL.h | 135 + ion/src/sdl/external/sdl/include/SDL_assert.h | 291 + ion/src/sdl/external/sdl/include/SDL_atomic.h | 277 + ion/src/sdl/external/sdl/include/SDL_audio.h | 826 + ion/src/sdl/external/sdl/include/SDL_bits.h | 121 + .../sdl/external/sdl/include/SDL_blendmode.h | 120 + .../sdl/external/sdl/include/SDL_clipboard.h | 71 + ion/src/sdl/external/sdl/include/SDL_config.h | 55 + .../external/sdl/include/SDL_config_android.h | 178 + .../sdl/include/SDL_config_iphoneos.h | 201 + .../external/sdl/include/SDL_config_macosx.h | 240 + .../external/sdl/include/SDL_config_windows.h | 257 + .../sdl/external/sdl/include/SDL_cpuinfo.h | 199 + ion/src/sdl/external/sdl/include/SDL_egl.h | 1673 + ion/src/sdl/external/sdl/include/SDL_endian.h | 260 + ion/src/sdl/external/sdl/include/SDL_error.h | 76 + ion/src/sdl/external/sdl/include/SDL_events.h | 788 + .../sdl/external/sdl/include/SDL_filesystem.h | 136 + .../external/sdl/include/SDL_gamecontroller.h | 390 + .../sdl/external/sdl/include/SDL_gesture.h | 87 + ion/src/sdl/external/sdl/include/SDL_haptic.h | 1238 + ion/src/sdl/external/sdl/include/SDL_hints.h | 1170 + .../sdl/external/sdl/include/SDL_joystick.h | 408 + .../sdl/external/sdl/include/SDL_keyboard.h | 217 + .../sdl/external/sdl/include/SDL_keycode.h | 349 + ion/src/sdl/external/sdl/include/SDL_loadso.h | 81 + ion/src/sdl/external/sdl/include/SDL_log.h | 211 + ion/src/sdl/external/sdl/include/SDL_main.h | 168 + .../sdl/external/sdl/include/SDL_messagebox.h | 144 + ion/src/sdl/external/sdl/include/SDL_mouse.h | 302 + ion/src/sdl/external/sdl/include/SDL_mutex.h | 251 + ion/src/sdl/external/sdl/include/SDL_name.h | 33 + ion/src/sdl/external/sdl/include/SDL_opengl.h | 2183 + .../external/sdl/include/SDL_opengl_glext.h | 11177 ++++ .../sdl/external/sdl/include/SDL_opengles.h | 39 + .../sdl/external/sdl/include/SDL_opengles2.h | 52 + .../external/sdl/include/SDL_opengles2_gl2.h | 621 + .../sdl/include/SDL_opengles2_gl2ext.h | 2050 + .../sdl/include/SDL_opengles2_gl2platform.h | 30 + .../sdl/include/SDL_opengles2_khrplatform.h | 282 + ion/src/sdl/external/sdl/include/SDL_pixels.h | 470 + .../sdl/external/sdl/include/SDL_platform.h | 198 + ion/src/sdl/external/sdl/include/SDL_power.h | 75 + ion/src/sdl/external/sdl/include/SDL_quit.h | 58 + ion/src/sdl/external/sdl/include/SDL_rect.h | 174 + ion/src/sdl/external/sdl/include/SDL_render.h | 1098 + .../sdl/external/sdl/include/SDL_revision.h | 2 + ion/src/sdl/external/sdl/include/SDL_rwops.h | 254 + .../sdl/external/sdl/include/SDL_scancode.h | 413 + ion/src/sdl/external/sdl/include/SDL_sensor.h | 251 + ion/src/sdl/external/sdl/include/SDL_shape.h | 144 + ion/src/sdl/external/sdl/include/SDL_stdinc.h | 607 + .../sdl/external/sdl/include/SDL_surface.h | 554 + ion/src/sdl/external/sdl/include/SDL_system.h | 279 + ion/src/sdl/external/sdl/include/SDL_syswm.h | 327 + ion/src/sdl/external/sdl/include/SDL_thread.h | 343 + ion/src/sdl/external/sdl/include/SDL_timer.h | 115 + ion/src/sdl/external/sdl/include/SDL_touch.h | 99 + ion/src/sdl/external/sdl/include/SDL_types.h | 29 + .../sdl/external/sdl/include/SDL_version.h | 162 + ion/src/sdl/external/sdl/include/SDL_video.h | 1276 + ion/src/sdl/external/sdl/include/SDL_vulkan.h | 278 + ion/src/sdl/external/sdl/include/begin_code.h | 167 + ion/src/sdl/external/sdl/include/close_code.h | 40 + ion/src/sdl/external/sdl/src/SDL.c | 520 + ion/src/sdl/external/sdl/src/SDL_assert.c | 452 + ion/src/sdl/external/sdl/src/SDL_assert_c.h | 29 + ion/src/sdl/external/sdl/src/SDL_dataqueue.c | 339 + ion/src/sdl/external/sdl/src/SDL_dataqueue.h | 55 + ion/src/sdl/external/sdl/src/SDL_error.c | 319 + ion/src/sdl/external/sdl/src/SDL_error_c.h | 65 + ion/src/sdl/external/sdl/src/SDL_hints.c | 236 + ion/src/sdl/external/sdl/src/SDL_internal.h | 56 + ion/src/sdl/external/sdl/src/SDL_log.c | 452 + .../sdl/external/sdl/src/atomic/SDL_atomic.c | 304 + .../external/sdl/src/atomic/SDL_spinlock.c | 176 + .../sdl/external/sdl/src/audio/SDL_audio.c | 1709 + .../sdl/external/sdl/src/audio/SDL_audio_c.h | 79 + .../sdl/external/sdl/src/audio/SDL_audiocvt.c | 1673 + .../sdl/external/sdl/src/audio/SDL_audiodev.c | 124 + .../external/sdl/src/audio/SDL_audiodev_c.h | 44 + .../external/sdl/src/audio/SDL_audiotypecvt.c | 1431 + .../sdl/external/sdl/src/audio/SDL_mixer.c | 369 + .../sdl/external/sdl/src/audio/SDL_sysaudio.h | 214 + ion/src/sdl/external/sdl/src/audio/SDL_wave.c | 694 + ion/src/sdl/external/sdl/src/audio/SDL_wave.h | 77 + .../sdl/src/audio/alsa/SDL_alsa_audio.c | 990 + .../sdl/src/audio/alsa/SDL_alsa_audio.h | 48 + .../sdl/src/audio/android/SDL_androidaudio.c | 211 + .../sdl/src/audio/android/SDL_androidaudio.h | 42 + .../sdl/src/audio/arts/SDL_artsaudio.c | 365 + .../sdl/src/audio/arts/SDL_artsaudio.h | 53 + .../sdl/src/audio/coreaudio/SDL_coreaudio.h | 66 + .../sdl/src/audio/coreaudio/SDL_coreaudio.m | 861 + .../src/audio/directsound/SDL_directsound.c | 604 + .../src/audio/directsound/SDL_directsound.h | 47 + .../sdl/src/audio/disk/SDL_diskaudio.c | 207 + .../sdl/src/audio/disk/SDL_diskaudio.h | 41 + .../external/sdl/src/audio/dsp/SDL_dspaudio.c | 320 + .../external/sdl/src/audio/dsp/SDL_dspaudio.h | 43 + .../sdl/src/audio/dummy/SDL_dummyaudio.c | 65 + .../sdl/src/audio/dummy/SDL_dummyaudio.h | 41 + .../audio/emscripten/SDL_emscriptenaudio.c | 389 + .../audio/emscripten/SDL_emscriptenaudio.h | 38 + .../external/sdl/src/audio/esd/SDL_esdaudio.c | 335 + .../external/sdl/src/audio/esd/SDL_esdaudio.h | 51 + .../sdl/src/audio/fusionsound/SDL_fsaudio.c | 328 + .../sdl/src/audio/fusionsound/SDL_fsaudio.h | 50 + .../sdl/src/audio/haiku/SDL_haikuaudio.cc | 248 + .../sdl/src/audio/haiku/SDL_haikuaudio.h | 38 + .../sdl/src/audio/jack/SDL_jackaudio.c | 446 + .../sdl/src/audio/jack/SDL_jackaudio.h | 41 + .../sdl/src/audio/nacl/SDL_naclaudio.c | 165 + .../sdl/src/audio/nacl/SDL_naclaudio.h | 43 + .../external/sdl/src/audio/nas/SDL_nasaudio.c | 463 + .../external/sdl/src/audio/nas/SDL_nasaudio.h | 56 + .../sdl/src/audio/netbsd/SDL_netbsdaudio.c | 412 + .../sdl/src/audio/netbsd/SDL_netbsdaudio.h | 48 + .../sdl/src/audio/openslES/SDL_openslES.c | 613 + .../sdl/src/audio/openslES/SDL_openslES.h | 50 + .../sdl/src/audio/paudio/SDL_paudio.c | 516 + .../sdl/src/audio/paudio/SDL_paudio.h | 48 + .../external/sdl/src/audio/psp/SDL_pspaudio.c | 181 + .../external/sdl/src/audio/psp/SDL_pspaudio.h | 45 + .../sdl/src/audio/pulseaudio/SDL_pulseaudio.c | 782 + .../sdl/src/audio/pulseaudio/SDL_pulseaudio.h | 52 + .../sdl/src/audio/qsa/SDL_qsa_audio.c | 666 + .../sdl/src/audio/qsa/SDL_qsa_audio.h | 57 + .../sdl/src/audio/sndio/SDL_sndioaudio.c | 382 + .../sdl/src/audio/sndio/SDL_sndioaudio.h | 49 + .../external/sdl/src/audio/sun/SDL_sunaudio.c | 419 + .../external/sdl/src/audio/sun/SDL_sunaudio.h | 47 + .../sdl/src/audio/wasapi/SDL_wasapi.c | 785 + .../sdl/src/audio/wasapi/SDL_wasapi.h | 85 + .../sdl/src/audio/wasapi/SDL_wasapi_win32.c | 457 + .../sdl/src/audio/wasapi/SDL_wasapi_winrt.cpp | 285 + .../external/sdl/src/audio/winmm/SDL_winmm.c | 456 + .../external/sdl/src/audio/winmm/SDL_winmm.h | 45 + .../sdl/src/core/android/SDL_android.c | 2613 + .../sdl/src/core/android/SDL_android.h | 136 + .../sdl/src/core/android/keyinfotable.h | 175 + .../external/sdl/src/core/linux/SDL_dbus.c | 347 + .../external/sdl/src/core/linux/SDL_dbus.h | 96 + .../external/sdl/src/core/linux/SDL_evdev.c | 786 + .../external/sdl/src/core/linux/SDL_evdev.h | 39 + .../sdl/src/core/linux/SDL_evdev_kbd.c | 843 + .../sdl/src/core/linux/SDL_evdev_kbd.h | 34 + .../linux/SDL_evdev_kbd_default_accents.h | 284 + .../core/linux/SDL_evdev_kbd_default_keymap.h | 4763 ++ .../external/sdl/src/core/linux/SDL_fcitx.c | 373 + .../external/sdl/src/core/linux/SDL_fcitx.h | 40 + .../external/sdl/src/core/linux/SDL_ibus.c | 584 + .../external/sdl/src/core/linux/SDL_ibus.h | 58 + .../sdl/external/sdl/src/core/linux/SDL_ime.c | 152 + .../sdl/external/sdl/src/core/linux/SDL_ime.h | 40 + .../sdl/src/core/linux/SDL_threadprio.c | 111 + .../external/sdl/src/core/linux/SDL_udev.c | 569 + .../external/sdl/src/core/linux/SDL_udev.h | 125 + .../sdl/external/sdl/src/core/unix/SDL_poll.c | 87 + .../sdl/external/sdl/src/core/unix/SDL_poll.h | 34 + .../sdl/src/core/windows/SDL_directx.h | 111 + .../sdl/src/core/windows/SDL_windows.c | 233 + .../sdl/src/core/windows/SDL_windows.h | 75 + .../sdl/src/core/windows/SDL_xinput.c | 139 + .../sdl/src/core/windows/SDL_xinput.h | 177 + .../src/core/winrt/SDL_winrtapp_common.cpp | 66 + .../sdl/src/core/winrt/SDL_winrtapp_common.h | 31 + .../src/core/winrt/SDL_winrtapp_direct3d.cpp | 855 + .../src/core/winrt/SDL_winrtapp_direct3d.h | 92 + .../sdl/src/core/winrt/SDL_winrtapp_xaml.cpp | 160 + .../sdl/src/core/winrt/SDL_winrtapp_xaml.h | 33 + .../external/sdl/src/cpuinfo/SDL_cpuinfo.c | 881 + .../sdl/external/sdl/src/cpuinfo/SDL_simd.h | 88 + .../sdl/external/sdl/src/dynapi/SDL_dynapi.c | 332 + .../sdl/external/sdl/src/dynapi/SDL_dynapi.h | 67 + .../sdl/src/dynapi/SDL_dynapi_overrides.h | 715 + .../sdl/src/dynapi/SDL_dynapi_procs.h | 769 + .../sdl/external/sdl/src/dynapi/gendynapi.pl | 141 + .../sdl/src/events/SDL_clipboardevents.c | 46 + .../sdl/src/events/SDL_clipboardevents_c.h | 30 + .../sdl/src/events/SDL_displayevents.c | 60 + .../sdl/src/events/SDL_displayevents_c.h | 30 + .../external/sdl/src/events/SDL_dropevents.c | 98 + .../sdl/src/events/SDL_dropevents_c.h | 32 + .../sdl/external/sdl/src/events/SDL_events.c | 978 + .../external/sdl/src/events/SDL_events_c.h | 58 + .../sdl/external/sdl/src/events/SDL_gesture.c | 708 + .../external/sdl/src/events/SDL_gesture_c.h | 35 + .../external/sdl/src/events/SDL_keyboard.c | 1033 + .../external/sdl/src/events/SDL_keyboard_c.h | 70 + .../sdl/external/sdl/src/events/SDL_mouse.c | 1057 + .../sdl/external/sdl/src/events/SDL_mouse_c.h | 140 + .../sdl/external/sdl/src/events/SDL_quit.c | 152 + .../external/sdl/src/events/SDL_sysevents.h | 36 + .../sdl/external/sdl/src/events/SDL_touch.c | 386 + .../sdl/external/sdl/src/events/SDL_touch_c.h | 62 + .../sdl/src/events/SDL_windowevents.c | 211 + .../sdl/src/events/SDL_windowevents_c.h | 31 + .../external/sdl/src/events/blank_cursor.h | 33 + .../external/sdl/src/events/default_cursor.h | 114 + .../sdl/src/events/scancodes_darwin.h | 159 + .../external/sdl/src/events/scancodes_linux.h | 263 + .../sdl/src/events/scancodes_windows.h | 55 + .../sdl/src/events/scancodes_xfree86.h | 512 + ion/src/sdl/external/sdl/src/file/SDL_rwops.c | 868 + .../src/file/cocoa/SDL_rwopsbundlesupport.h | 30 + .../src/file/cocoa/SDL_rwopsbundlesupport.m | 62 + .../filesystem/android/SDL_sysfilesystem.c | 62 + .../src/filesystem/cocoa/SDL_sysfilesystem.m | 117 + .../src/filesystem/dummy/SDL_sysfilesystem.c | 47 + .../filesystem/emscripten/SDL_sysfilesystem.c | 81 + .../src/filesystem/haiku/SDL_sysfilesystem.cc | 110 + .../src/filesystem/nacl/SDL_sysfilesystem.c | 43 + .../src/filesystem/unix/SDL_sysfilesystem.c | 250 + .../filesystem/windows/SDL_sysfilesystem.c | 192 + .../filesystem/winrt/SDL_sysfilesystem.cpp | 233 + .../sdl/external/sdl/src/haptic/SDL_haptic.c | 856 + .../external/sdl/src/haptic/SDL_haptic_c.h | 30 + .../external/sdl/src/haptic/SDL_syshaptic.h | 208 + .../sdl/src/haptic/android/SDL_syshaptic.c | 368 + .../sdl/src/haptic/android/SDL_syshaptic_c.h | 12 + .../sdl/src/haptic/darwin/SDL_syshaptic.c | 1417 + .../sdl/src/haptic/darwin/SDL_syshaptic_c.h | 26 + .../sdl/src/haptic/dummy/SDL_syshaptic.c | 186 + .../sdl/src/haptic/linux/SDL_syshaptic.c | 1165 + .../sdl/src/haptic/windows/SDL_dinputhaptic.c | 1305 + .../src/haptic/windows/SDL_dinputhaptic_c.h | 47 + .../src/haptic/windows/SDL_windowshaptic.c | 456 + .../src/haptic/windows/SDL_windowshaptic_c.h | 88 + .../sdl/src/haptic/windows/SDL_xinputhaptic.c | 487 + .../src/haptic/windows/SDL_xinputhaptic_c.h | 47 + .../external/sdl/src/hidapi/LICENSE-bsd.txt | 26 + .../external/sdl/src/hidapi/LICENSE-gpl3.txt | 674 + .../external/sdl/src/hidapi/LICENSE-orig.txt | 9 + .../sdl/external/sdl/src/hidapi/LICENSE.txt | 13 + .../external/sdl/src/hidapi/android/hid.cpp | 1159 + .../external/sdl/src/hidapi/hidapi/hidapi.h | 398 + ion/src/sdl/external/sdl/src/hidapi/ios/hid.m | 914 + .../sdl/external/sdl/src/hidapi/libusb/hid.c | 1620 + .../external/sdl/src/hidapi/libusb/hidusb.cpp | 3 + .../sdl/external/sdl/src/hidapi/linux/hid.c | 898 + .../sdl/external/sdl/src/hidapi/linux/hid.cpp | 333 + .../external/sdl/src/hidapi/linux/hidraw.cpp | 3 + ion/src/sdl/external/sdl/src/hidapi/mac/hid.c | 1191 + .../sdl/external/sdl/src/hidapi/windows/hid.c | 988 + .../sdl/src/joystick/SDL_gamecontroller.c | 2098 + .../sdl/src/joystick/SDL_gamecontrollerdb.h | 600 + .../external/sdl/src/joystick/SDL_joystick.c | 1699 + .../sdl/src/joystick/SDL_joystick_c.h | 106 + .../sdl/src/joystick/SDL_sysjoystick.h | 157 + .../src/joystick/android/SDL_sysjoystick.c | 710 + .../src/joystick/android/SDL_sysjoystick_c.h | 59 + .../sdl/src/joystick/bsd/SDL_sysjoystick.c | 710 + .../sdl/src/joystick/controller_type.h | 427 + .../sdl/src/joystick/darwin/SDL_sysjoystick.c | 1019 + .../src/joystick/darwin/SDL_sysjoystick_c.h | 79 + .../sdl/src/joystick/dummy/SDL_sysjoystick.c | 120 + .../src/joystick/emscripten/SDL_sysjoystick.c | 420 + .../joystick/emscripten/SDL_sysjoystick_c.h | 52 + .../src/joystick/haiku/SDL_haikujoystick.cc | 278 + .../sdl/src/joystick/hidapi/SDL_hidapi_ps4.c | 566 + .../src/joystick/hidapi/SDL_hidapi_switch.c | 905 + .../src/joystick/hidapi/SDL_hidapi_xbox360.c | 787 + .../src/joystick/hidapi/SDL_hidapi_xboxone.c | 324 + .../src/joystick/hidapi/SDL_hidapijoystick.c | 1071 + .../joystick/hidapi/SDL_hidapijoystick_c.h | 74 + .../src/joystick/iphoneos/SDL_sysjoystick.m | 737 + .../src/joystick/iphoneos/SDL_sysjoystick_c.h | 57 + .../sdl/src/joystick/linux/SDL_sysjoystick.c | 1022 + .../src/joystick/linux/SDL_sysjoystick_c.h | 69 + .../sdl/src/joystick/psp/SDL_sysjoystick.c | 264 + .../sdl/src/joystick/sort_controllers.py | 78 + .../src/joystick/steam/SDL_steamcontroller.c | 52 + .../src/joystick/steam/SDL_steamcontroller.h | 39 + .../src/joystick/windows/SDL_dinputjoystick.c | 1155 + .../joystick/windows/SDL_dinputjoystick_c.h | 31 + .../sdl/src/joystick/windows/SDL_mmjoystick.c | 452 + .../joystick/windows/SDL_windowsjoystick.c | 571 + .../joystick/windows/SDL_windowsjoystick_c.h | 95 + .../src/joystick/windows/SDL_xinputjoystick.c | 560 + .../joystick/windows/SDL_xinputjoystick_c.h | 34 + ion/src/sdl/external/sdl/src/libm/e_atan2.c | 134 + ion/src/sdl/external/sdl/src/libm/e_exp.c | 191 + ion/src/sdl/external/sdl/src/libm/e_fmod.c | 144 + ion/src/sdl/external/sdl/src/libm/e_log.c | 152 + ion/src/sdl/external/sdl/src/libm/e_log10.c | 106 + ion/src/sdl/external/sdl/src/libm/e_pow.c | 347 + .../sdl/external/sdl/src/libm/e_rem_pio2.c | 161 + ion/src/sdl/external/sdl/src/libm/e_sqrt.c | 457 + ion/src/sdl/external/sdl/src/libm/k_cos.c | 82 + .../sdl/external/sdl/src/libm/k_rem_pio2.c | 317 + ion/src/sdl/external/sdl/src/libm/k_sin.c | 65 + ion/src/sdl/external/sdl/src/libm/k_tan.c | 118 + ion/src/sdl/external/sdl/src/libm/math_libm.h | 47 + .../sdl/external/sdl/src/libm/math_private.h | 227 + ion/src/sdl/external/sdl/src/libm/s_atan.c | 118 + .../sdl/external/sdl/src/libm/s_copysign.c | 29 + ion/src/sdl/external/sdl/src/libm/s_cos.c | 73 + ion/src/sdl/external/sdl/src/libm/s_fabs.c | 29 + ion/src/sdl/external/sdl/src/libm/s_floor.c | 75 + ion/src/sdl/external/sdl/src/libm/s_scalbn.c | 73 + ion/src/sdl/external/sdl/src/libm/s_sin.c | 73 + ion/src/sdl/external/sdl/src/libm/s_tan.c | 67 + .../sdl/src/loadso/dlopen/SDL_sysloadso.c | 89 + .../sdl/src/loadso/dummy/SDL_sysloadso.c | 54 + .../sdl/src/loadso/windows/SDL_sysloadso.c | 80 + .../sdl/src/main/android/SDL_android_main.c | 7 + .../sdl/src/main/dummy/SDL_dummy_main.c | 28 + .../external/sdl/src/main/haiku/SDL_BApp.h | 399 + .../external/sdl/src/main/haiku/SDL_BeApp.cc | 157 + .../external/sdl/src/main/haiku/SDL_BeApp.h | 38 + .../sdl/src/main/nacl/SDL_nacl_main.c | 93 + .../external/sdl/src/main/psp/SDL_psp_main.c | 70 + .../sdl/src/main/windows/SDL_windows_main.c | 209 + .../external/sdl/src/main/windows/version.rc | 38 + .../winrt/SDL2-WinRTResource_BlankCursor.cur | Bin 0 -> 326 bytes .../sdl/src/main/winrt/SDL2-WinRTResources.rc | 3 + .../src/main/winrt/SDL_winrt_main_NonXAML.cpp | 54 + .../sdl/external/sdl/src/power/SDL_power.c | 116 + .../sdl/external/sdl/src/power/SDL_syspower.h | 49 + .../sdl/src/power/android/SDL_syspower.c | 64 + .../sdl/src/power/emscripten/SDL_syspower.c | 62 + .../sdl/src/power/haiku/SDL_syspower.c | 128 + .../sdl/src/power/linux/SDL_syspower.c | 642 + .../sdl/src/power/macosx/SDL_syspower.c | 192 + .../external/sdl/src/power/psp/SDL_syspower.c | 68 + .../sdl/src/power/uikit/SDL_syspower.h | 32 + .../sdl/src/power/uikit/SDL_syspower.m | 113 + .../sdl/src/power/windows/SDL_syspower.c | 76 + .../sdl/src/power/winrt/SDL_syspower.cpp | 44 + .../sdl/external/sdl/src/render/SDL_d3dmath.c | 136 + .../sdl/external/sdl/src/render/SDL_d3dmath.h | 72 + .../sdl/external/sdl/src/render/SDL_render.c | 3331 + .../external/sdl/src/render/SDL_sysrender.h | 270 + .../sdl/external/sdl/src/render/SDL_yuv_sw.c | 414 + .../external/sdl/src/render/SDL_yuv_sw_c.h | 73 + .../sdl/src/render/direct3d/SDL_render_d3d.c | 1840 + .../sdl/src/render/direct3d/SDL_shaders_d3d.c | 274 + .../sdl/src/render/direct3d/SDL_shaders_d3d.h | 34 + .../src/render/direct3d11/SDL_render_d3d11.c | 2564 + .../render/direct3d11/SDL_render_winrt.cpp | 116 + .../src/render/direct3d11/SDL_render_winrt.h | 40 + .../src/render/direct3d11/SDL_shaders_d3d11.c | 1957 + .../src/render/direct3d11/SDL_shaders_d3d11.h | 43 + .../sdl/src/render/metal/SDL_render_metal.m | 1793 + .../src/render/metal/SDL_shaders_metal.metal | 109 + .../src/render/metal/SDL_shaders_metal_ios.h | 1899 + .../src/render/metal/SDL_shaders_metal_osx.h | 1903 + .../src/render/metal/build-metal-shaders.sh | 18 + .../sdl/src/render/opengl/SDL_glfuncs.h | 478 + .../sdl/src/render/opengl/SDL_render_gl.c | 1743 + .../sdl/src/render/opengl/SDL_shaders_gl.c | 525 + .../sdl/src/render/opengl/SDL_shaders_gl.h | 54 + .../sdl/src/render/opengles/SDL_glesfuncs.h | 65 + .../sdl/src/render/opengles/SDL_render_gles.c | 1257 + .../sdl/src/render/opengles2/SDL_gles2funcs.h | 81 + .../src/render/opengles2/SDL_render_gles2.c | 2137 + .../src/render/opengles2/SDL_shaders_gles2.c | 573 + .../src/render/opengles2/SDL_shaders_gles2.h | 70 + .../sdl/src/render/psp/SDL_render_psp.c | 1047 + .../src/render/software/SDL_blendfillrect.c | 336 + .../src/render/software/SDL_blendfillrect.h | 33 + .../sdl/src/render/software/SDL_blendline.c | 777 + .../sdl/src/render/software/SDL_blendline.h | 33 + .../sdl/src/render/software/SDL_blendpoint.c | 341 + .../sdl/src/render/software/SDL_blendpoint.h | 33 + .../sdl/src/render/software/SDL_draw.h | 576 + .../sdl/src/render/software/SDL_drawline.c | 209 + .../sdl/src/render/software/SDL_drawline.h | 33 + .../sdl/src/render/software/SDL_drawpoint.c | 114 + .../sdl/src/render/software/SDL_drawpoint.h | 33 + .../sdl/src/render/software/SDL_render_sw.c | 857 + .../sdl/src/render/software/SDL_render_sw_c.h | 29 + .../sdl/src/render/software/SDL_rotate.c | 534 + .../sdl/src/render/software/SDL_rotate.h | 32 + .../sdl/external/sdl/src/sensor/SDL_sensor.c | 550 + .../external/sdl/src/sensor/SDL_sensor_c.h | 44 + .../external/sdl/src/sensor/SDL_syssensor.h | 105 + .../src/sensor/android/SDL_androidsensor.c | 211 + .../src/sensor/android/SDL_androidsensor.h | 31 + .../sensor/coremotion/SDL_coremotionsensor.h | 30 + .../sensor/coremotion/SDL_coremotionsensor.m | 234 + .../sdl/src/sensor/dummy/SDL_dummysensor.c | 110 + .../sdl/src/sensor/dummy/SDL_dummysensor.h | 23 + .../sdl/external/sdl/src/stdlib/SDL_getenv.c | 315 + .../sdl/external/sdl/src/stdlib/SDL_iconv.c | 934 + .../sdl/external/sdl/src/stdlib/SDL_malloc.c | 5435 ++ .../sdl/external/sdl/src/stdlib/SDL_qsort.c | 534 + .../sdl/external/sdl/src/stdlib/SDL_stdlib.c | 1151 + .../sdl/external/sdl/src/stdlib/SDL_string.c | 1819 + .../external/sdl/src/test/SDL_test_assert.c | 152 + .../external/sdl/src/test/SDL_test_common.c | 1826 + .../external/sdl/src/test/SDL_test_compare.c | 117 + .../external/sdl/src/test/SDL_test_crc32.c | 166 + .../sdl/external/sdl/src/test/SDL_test_font.c | 3250 + .../external/sdl/src/test/SDL_test_fuzzer.c | 534 + .../external/sdl/src/test/SDL_test_harness.c | 683 + .../sdl/src/test/SDL_test_imageBlit.c | 1559 + .../sdl/src/test/SDL_test_imageBlitBlend.c | 2845 + .../sdl/src/test/SDL_test_imageFace.c | 247 + .../sdl/src/test/SDL_test_imagePrimitives.c | 514 + .../src/test/SDL_test_imagePrimitivesBlend.c | 696 + .../sdl/external/sdl/src/test/SDL_test_log.c | 118 + .../sdl/external/sdl/src/test/SDL_test_md5.c | 338 + .../external/sdl/src/test/SDL_test_memory.c | 274 + .../external/sdl/src/test/SDL_test_random.c | 96 + .../external/sdl/src/thread/SDL_systhread.h | 70 + .../sdl/external/sdl/src/thread/SDL_thread.c | 505 + .../external/sdl/src/thread/SDL_thread_c.h | 95 + .../sdl/src/thread/generic/SDL_syscond.c | 220 + .../sdl/src/thread/generic/SDL_sysmutex.c | 165 + .../sdl/src/thread/generic/SDL_sysmutex_c.h | 22 + .../sdl/src/thread/generic/SDL_syssem.c | 217 + .../sdl/src/thread/generic/SDL_systhread.c | 71 + .../sdl/src/thread/generic/SDL_systhread_c.h | 26 + .../sdl/src/thread/generic/SDL_systls.c | 38 + .../external/sdl/src/thread/psp/SDL_syscond.c | 224 + .../sdl/src/thread/psp/SDL_sysmutex.c | 136 + .../sdl/src/thread/psp/SDL_sysmutex_c.h | 22 + .../external/sdl/src/thread/psp/SDL_syssem.c | 161 + .../sdl/src/thread/psp/SDL_systhread.c | 114 + .../sdl/src/thread/psp/SDL_systhread_c.h | 24 + .../sdl/src/thread/pthread/SDL_syscond.c | 158 + .../sdl/src/thread/pthread/SDL_sysmutex.c | 195 + .../sdl/src/thread/pthread/SDL_sysmutex_c.h | 32 + .../sdl/src/thread/pthread/SDL_syssem.c | 209 + .../sdl/src/thread/pthread/SDL_systhread.c | 247 + .../sdl/src/thread/pthread/SDL_systhread_c.h | 27 + .../sdl/src/thread/pthread/SDL_systls.c | 70 + .../sdl/src/thread/stdcpp/SDL_syscond.cpp | 164 + .../sdl/src/thread/stdcpp/SDL_sysmutex.cpp | 111 + .../sdl/src/thread/stdcpp/SDL_sysmutex_c.h | 30 + .../sdl/src/thread/stdcpp/SDL_systhread.cpp | 168 + .../sdl/src/thread/stdcpp/SDL_systhread_c.h | 26 + .../sdl/src/thread/windows/SDL_sysmutex.c | 110 + .../sdl/src/thread/windows/SDL_syssem.c | 152 + .../sdl/src/thread/windows/SDL_systhread.c | 260 + .../sdl/src/thread/windows/SDL_systhread_c.h | 32 + .../sdl/src/thread/windows/SDL_systls.c | 72 + .../sdl/external/sdl/src/timer/SDL_timer.c | 373 + .../sdl/external/sdl/src/timer/SDL_timer_c.h | 40 + .../sdl/src/timer/dummy/SDL_systimer.c | 75 + .../sdl/src/timer/haiku/SDL_systimer.c | 80 + .../external/sdl/src/timer/psp/SDL_systimer.c | 91 + .../sdl/src/timer/unix/SDL_systimer.c | 231 + .../sdl/src/timer/windows/SDL_systimer.c | 200 + .../sdl/external/sdl/src/video/SDL_RLEaccel.c | 1588 + .../external/sdl/src/video/SDL_RLEaccel_c.h | 38 + ion/src/sdl/external/sdl/src/video/SDL_blit.c | 296 + ion/src/sdl/external/sdl/src/video/SDL_blit.h | 553 + .../sdl/external/sdl/src/video/SDL_blit_0.c | 483 + .../sdl/external/sdl/src/video/SDL_blit_1.c | 552 + .../sdl/external/sdl/src/video/SDL_blit_A.c | 1404 + .../sdl/external/sdl/src/video/SDL_blit_N.c | 3331 + .../external/sdl/src/video/SDL_blit_auto.c | 7419 +++ .../external/sdl/src/video/SDL_blit_auto.h | 30 + .../external/sdl/src/video/SDL_blit_copy.c | 162 + .../external/sdl/src/video/SDL_blit_copy.h | 29 + .../external/sdl/src/video/SDL_blit_slow.c | 164 + .../external/sdl/src/video/SDL_blit_slow.h | 31 + ion/src/sdl/external/sdl/src/video/SDL_bmp.c | 706 + .../external/sdl/src/video/SDL_clipboard.c | 90 + ion/src/sdl/external/sdl/src/video/SDL_egl.c | 935 + .../sdl/external/sdl/src/video/SDL_egl_c.h | 152 + .../sdl/external/sdl/src/video/SDL_fillrect.c | 343 + .../sdl/external/sdl/src/video/SDL_pixels.c | 1125 + .../sdl/external/sdl/src/video/SDL_pixels_c.h | 46 + ion/src/sdl/external/sdl/src/video/SDL_rect.c | 531 + .../sdl/external/sdl/src/video/SDL_rect_c.h | 31 + .../sdl/external/sdl/src/video/SDL_shape.c | 309 + .../sdl/src/video/SDL_shape_internals.h | 69 + .../sdl/external/sdl/src/video/SDL_stretch.c | 353 + .../sdl/external/sdl/src/video/SDL_surface.c | 1278 + .../sdl/external/sdl/src/video/SDL_sysvideo.h | 466 + .../sdl/external/sdl/src/video/SDL_video.c | 4159 ++ .../sdl/src/video/SDL_vulkan_internal.h | 88 + .../external/sdl/src/video/SDL_vulkan_utils.c | 172 + ion/src/sdl/external/sdl/src/video/SDL_yuv.c | 1835 + .../sdl/external/sdl/src/video/SDL_yuv_c.h | 36 + .../src/video/android/SDL_androidclipboard.c | 48 + .../src/video/android/SDL_androidclipboard.h | 32 + .../sdl/src/video/android/SDL_androidevents.c | 195 + .../sdl/src/video/android/SDL_androidevents.h | 27 + .../sdl/src/video/android/SDL_androidgl.c | 70 + .../sdl/src/video/android/SDL_androidgl.h | 34 + .../src/video/android/SDL_androidkeyboard.c | 391 + .../src/video/android/SDL_androidkeyboard.h | 36 + .../src/video/android/SDL_androidmessagebox.c | 37 + .../src/video/android/SDL_androidmessagebox.h | 29 + .../sdl/src/video/android/SDL_androidmouse.c | 266 + .../sdl/src/video/android/SDL_androidmouse.h | 33 + .../sdl/src/video/android/SDL_androidtouch.c | 143 + .../sdl/src/video/android/SDL_androidtouch.h | 29 + .../sdl/src/video/android/SDL_androidvideo.c | 261 + .../sdl/src/video/android/SDL_androidvideo.h | 50 + .../sdl/src/video/android/SDL_androidvulkan.c | 175 + .../sdl/src/video/android/SDL_androidvulkan.h | 52 + .../sdl/src/video/android/SDL_androidwindow.c | 208 + .../sdl/src/video/android/SDL_androidwindow.h | 46 + .../sdl/src/video/cocoa/SDL_cocoaclipboard.h | 36 + .../sdl/src/video/cocoa/SDL_cocoaclipboard.m | 103 + .../sdl/src/video/cocoa/SDL_cocoaevents.h | 32 + .../sdl/src/video/cocoa/SDL_cocoaevents.m | 476 + .../sdl/src/video/cocoa/SDL_cocoakeyboard.h | 36 + .../sdl/src/video/cocoa/SDL_cocoakeyboard.m | 720 + .../sdl/src/video/cocoa/SDL_cocoamessagebox.h | 29 + .../sdl/src/video/cocoa/SDL_cocoamessagebox.m | 145 + .../sdl/src/video/cocoa/SDL_cocoametalview.h | 63 + .../sdl/src/video/cocoa/SDL_cocoametalview.m | 138 + .../sdl/src/video/cocoa/SDL_cocoamodes.h | 46 + .../sdl/src/video/cocoa/SDL_cocoamodes.m | 491 + .../sdl/src/video/cocoa/SDL_cocoamouse.h | 52 + .../sdl/src/video/cocoa/SDL_cocoamouse.m | 471 + .../sdl/src/video/cocoa/SDL_cocoamousetap.h | 34 + .../sdl/src/video/cocoa/SDL_cocoamousetap.m | 286 + .../sdl/src/video/cocoa/SDL_cocoaopengl.h | 73 + .../sdl/src/video/cocoa/SDL_cocoaopengl.m | 486 + .../sdl/src/video/cocoa/SDL_cocoaopengles.h | 49 + .../sdl/src/video/cocoa/SDL_cocoaopengles.m | 132 + .../sdl/src/video/cocoa/SDL_cocoashape.h | 45 + .../sdl/src/video/cocoa/SDL_cocoashape.m | 113 + .../sdl/src/video/cocoa/SDL_cocoavideo.h | 118 + .../sdl/src/video/cocoa/SDL_cocoavideo.m | 262 + .../sdl/src/video/cocoa/SDL_cocoavulkan.h | 55 + .../sdl/src/video/cocoa/SDL_cocoavulkan.m | 230 + .../sdl/src/video/cocoa/SDL_cocoawindow.h | 155 + .../sdl/src/video/cocoa/SDL_cocoawindow.m | 1926 + .../sdl/src/video/directfb/SDL_DirectFB_WM.c | 413 + .../sdl/src/video/directfb/SDL_DirectFB_WM.h | 56 + .../sdl/src/video/directfb/SDL_DirectFB_dyn.c | 117 + .../sdl/src/video/directfb/SDL_DirectFB_dyn.h | 41 + .../src/video/directfb/SDL_DirectFB_events.c | 748 + .../src/video/directfb/SDL_DirectFB_events.h | 34 + .../src/video/directfb/SDL_DirectFB_modes.c | 414 + .../src/video/directfb/SDL_DirectFB_modes.h | 59 + .../src/video/directfb/SDL_DirectFB_mouse.c | 389 + .../src/video/directfb/SDL_DirectFB_mouse.h | 44 + .../src/video/directfb/SDL_DirectFB_opengl.c | 332 + .../src/video/directfb/SDL_DirectFB_opengl.h | 64 + .../src/video/directfb/SDL_DirectFB_render.c | 1055 + .../src/video/directfb/SDL_DirectFB_render.h | 25 + .../src/video/directfb/SDL_DirectFB_shape.c | 131 + .../src/video/directfb/SDL_DirectFB_shape.h | 38 + .../src/video/directfb/SDL_DirectFB_video.c | 418 + .../src/video/directfb/SDL_DirectFB_video.h | 170 + .../src/video/directfb/SDL_DirectFB_window.c | 565 + .../src/video/directfb/SDL_DirectFB_window.h | 82 + .../sdl/src/video/dummy/SDL_nullevents.c | 41 + .../sdl/src/video/dummy/SDL_nullevents_c.h | 33 + .../sdl/src/video/dummy/SDL_nullframebuffer.c | 89 + .../src/video/dummy/SDL_nullframebuffer_c.h | 33 + .../sdl/src/video/dummy/SDL_nullvideo.c | 144 + .../sdl/src/video/dummy/SDL_nullvideo.h | 30 + .../video/emscripten/SDL_emscriptenevents.c | 730 + .../video/emscripten/SDL_emscriptenevents.h | 40 + .../emscripten/SDL_emscriptenframebuffer.c | 178 + .../emscripten/SDL_emscriptenframebuffer.h | 32 + .../video/emscripten/SDL_emscriptenmouse.c | 275 + .../video/emscripten/SDL_emscriptenmouse.h | 42 + .../video/emscripten/SDL_emscriptenopengles.c | 108 + .../video/emscripten/SDL_emscriptenopengles.h | 49 + .../video/emscripten/SDL_emscriptenvideo.c | 358 + .../video/emscripten/SDL_emscriptenvideo.h | 58 + .../external/sdl/src/video/haiku/SDL_BWin.h | 679 + .../sdl/src/video/haiku/SDL_bclipboard.cc | 95 + .../sdl/src/video/haiku/SDL_bclipboard.h | 33 + .../sdl/src/video/haiku/SDL_bevents.cc | 41 + .../sdl/src/video/haiku/SDL_bevents.h | 39 + .../sdl/src/video/haiku/SDL_bframebuffer.cc | 259 + .../sdl/src/video/haiku/SDL_bframebuffer.h | 47 + .../sdl/src/video/haiku/SDL_bkeyboard.cc | 190 + .../sdl/src/video/haiku/SDL_bkeyboard.h | 44 + .../sdl/src/video/haiku/SDL_bmodes.cc | 333 + .../external/sdl/src/video/haiku/SDL_bmodes.h | 48 + .../sdl/src/video/haiku/SDL_bopengl.cc | 176 + .../sdl/src/video/haiku/SDL_bopengl.h | 55 + .../sdl/src/video/haiku/SDL_bvideo.cc | 177 + .../external/sdl/src/video/haiku/SDL_bvideo.h | 44 + .../sdl/src/video/haiku/SDL_bwindow.cc | 233 + .../sdl/src/video/haiku/SDL_bwindow.h | 55 + .../external/sdl/src/video/khronos/EGL/egl.h | 303 + .../sdl/src/video/khronos/EGL/eglext.h | 1241 + .../sdl/src/video/khronos/EGL/eglplatform.h | 132 + .../sdl/src/video/khronos/GLES2/gl2.h | 675 + .../sdl/src/video/khronos/GLES2/gl2ext.h | 3505 + .../sdl/src/video/khronos/GLES2/gl2platform.h | 38 + .../sdl/src/video/khronos/KHR/khrplatform.h | 284 + .../sdl/src/video/khronos/vulkan/vk_icd.h | 170 + .../sdl/src/video/khronos/vulkan/vk_layer.h | 195 + .../src/video/khronos/vulkan/vk_platform.h | 92 + .../video/khronos/vulkan/vk_sdk_platform.h | 69 + .../sdl/src/video/khronos/vulkan/vulkan.h | 77 + .../sdl/src/video/khronos/vulkan/vulkan.hpp | 53056 ++++++++++++++++ .../src/video/khronos/vulkan/vulkan_android.h | 126 + .../src/video/khronos/vulkan/vulkan_core.h | 8823 +++ .../src/video/khronos/vulkan/vulkan_fuchsia.h | 58 + .../sdl/src/video/khronos/vulkan/vulkan_ios.h | 58 + .../src/video/khronos/vulkan/vulkan_macos.h | 58 + .../sdl/src/video/khronos/vulkan/vulkan_mir.h | 65 + .../sdl/src/video/khronos/vulkan/vulkan_vi.h | 58 + .../src/video/khronos/vulkan/vulkan_wayland.h | 65 + .../src/video/khronos/vulkan/vulkan_win32.h | 276 + .../sdl/src/video/khronos/vulkan/vulkan_xcb.h | 66 + .../src/video/khronos/vulkan/vulkan_xlib.h | 66 + .../video/khronos/vulkan/vulkan_xlib_xrandr.h | 54 + .../sdl/src/video/kmsdrm/SDL_kmsdrmdyn.c | 171 + .../sdl/src/video/kmsdrm/SDL_kmsdrmdyn.h | 53 + .../sdl/src/video/kmsdrm/SDL_kmsdrmevents.c | 42 + .../sdl/src/video/kmsdrm/SDL_kmsdrmevents.h | 31 + .../sdl/src/video/kmsdrm/SDL_kmsdrmmouse.c | 501 + .../sdl/src/video/kmsdrm/SDL_kmsdrmmouse.h | 45 + .../sdl/src/video/kmsdrm/SDL_kmsdrmopengles.c | 189 + .../sdl/src/video/kmsdrm/SDL_kmsdrmopengles.h | 48 + .../sdl/src/video/kmsdrm/SDL_kmsdrmsym.h | 99 + .../sdl/src/video/kmsdrm/SDL_kmsdrmvideo.c | 781 + .../sdl/src/video/kmsdrm/SDL_kmsdrmvideo.h | 125 + .../sdl/src/video/nacl/SDL_naclevents.c | 438 + .../sdl/src/video/nacl/SDL_naclevents_c.h | 30 + .../sdl/src/video/nacl/SDL_naclglue.c | 24 + .../sdl/src/video/nacl/SDL_naclopengles.c | 174 + .../sdl/src/video/nacl/SDL_naclopengles.h | 38 + .../sdl/src/video/nacl/SDL_naclvideo.c | 183 + .../sdl/src/video/nacl/SDL_naclvideo.h | 67 + .../sdl/src/video/nacl/SDL_naclwindow.c | 79 + .../sdl/src/video/nacl/SDL_naclwindow.h | 32 + .../sdl/src/video/pandora/SDL_pandora.c | 838 + .../sdl/src/video/pandora/SDL_pandora.h | 101 + .../src/video/pandora/SDL_pandora_events.c | 38 + .../src/video/pandora/SDL_pandora_events.h | 25 + .../sdl/src/video/psp/SDL_pspevents.c | 290 + .../sdl/src/video/psp/SDL_pspevents_c.h | 31 + .../external/sdl/src/video/psp/SDL_pspgl.c | 210 + .../external/sdl/src/video/psp/SDL_pspgl_c.h | 54 + .../external/sdl/src/video/psp/SDL_pspmouse.c | 41 + .../sdl/src/video/psp/SDL_pspmouse_c.h | 24 + .../external/sdl/src/video/psp/SDL_pspvideo.c | 333 + .../external/sdl/src/video/psp/SDL_pspvideo.h | 102 + ion/src/sdl/external/sdl/src/video/qnx/gl.c | 285 + .../sdl/external/sdl/src/video/qnx/keyboard.c | 133 + .../sdl/external/sdl/src/video/qnx/sdl_qnx.h | 48 + .../sdl/external/sdl/src/video/qnx/video.c | 364 + .../sdl/src/video/raspberry/SDL_rpievents.c | 45 + .../sdl/src/video/raspberry/SDL_rpievents_c.h | 31 + .../sdl/src/video/raspberry/SDL_rpimouse.c | 391 + .../sdl/src/video/raspberry/SDL_rpimouse.h | 43 + .../sdl/src/video/raspberry/SDL_rpiopengles.c | 71 + .../sdl/src/video/raspberry/SDL_rpiopengles.h | 49 + .../sdl/src/video/raspberry/SDL_rpivideo.c | 459 + .../sdl/src/video/raspberry/SDL_rpivideo.h | 104 + .../sdl/external/sdl/src/video/sdlgenblit.pl | 535 + .../src/video/uikit/SDL_uikitappdelegate.h | 47 + .../src/video/uikit/SDL_uikitappdelegate.m | 516 + .../sdl/src/video/uikit/SDL_uikitclipboard.h | 35 + .../sdl/src/video/uikit/SDL_uikitclipboard.m | 111 + .../sdl/src/video/uikit/SDL_uikitevents.h | 30 + .../sdl/src/video/uikit/SDL_uikitevents.m | 73 + .../sdl/src/video/uikit/SDL_uikitmessagebox.h | 31 + .../sdl/src/video/uikit/SDL_uikitmessagebox.m | 208 + .../sdl/src/video/uikit/SDL_uikitmetalview.h | 58 + .../sdl/src/video/uikit/SDL_uikitmetalview.m | 132 + .../sdl/src/video/uikit/SDL_uikitmodes.h | 54 + .../sdl/src/video/uikit/SDL_uikitmodes.m | 380 + .../sdl/src/video/uikit/SDL_uikitopengles.h | 40 + .../sdl/src/video/uikit/SDL_uikitopengles.m | 250 + .../sdl/src/video/uikit/SDL_uikitopenglview.h | 60 + .../sdl/src/video/uikit/SDL_uikitopenglview.m | 384 + .../sdl/src/video/uikit/SDL_uikitvideo.h | 46 + .../sdl/src/video/uikit/SDL_uikitvideo.m | 249 + .../sdl/src/video/uikit/SDL_uikitview.h | 41 + .../sdl/src/video/uikit/SDL_uikitview.m | 373 + .../src/video/uikit/SDL_uikitviewcontroller.h | 91 + .../src/video/uikit/SDL_uikitviewcontroller.m | 553 + .../sdl/src/video/uikit/SDL_uikitvulkan.h | 54 + .../sdl/src/video/uikit/SDL_uikitvulkan.m | 222 + .../sdl/src/video/uikit/SDL_uikitwindow.h | 56 + .../sdl/src/video/uikit/SDL_uikitwindow.m | 465 + .../sdl/src/video/uikit/keyinfotable.h | 174 + .../src/video/vivante/SDL_vivanteopengles.c | 47 + .../src/video/vivante/SDL_vivanteopengles.h | 48 + .../src/video/vivante/SDL_vivanteplatform.c | 54 + .../src/video/vivante/SDL_vivanteplatform.h | 47 + .../sdl/src/video/vivante/SDL_vivantevideo.c | 409 + .../sdl/src/video/vivante/SDL_vivantevideo.h | 91 + .../src/video/wayland/SDL_waylandclipboard.c | 123 + .../src/video/wayland/SDL_waylandclipboard.h | 32 + .../video/wayland/SDL_waylanddatamanager.c | 477 + .../video/wayland/SDL_waylanddatamanager.h | 103 + .../sdl/src/video/wayland/SDL_waylanddyn.c | 178 + .../sdl/src/video/wayland/SDL_waylanddyn.h | 107 + .../sdl/src/video/wayland/SDL_waylandevents.c | 1153 + .../src/video/wayland/SDL_waylandevents_c.h | 51 + .../sdl/src/video/wayland/SDL_waylandmouse.c | 396 + .../sdl/src/video/wayland/SDL_waylandmouse.h | 31 + .../src/video/wayland/SDL_waylandopengles.c | 125 + .../src/video/wayland/SDL_waylandopengles.h | 49 + .../sdl/src/video/wayland/SDL_waylandsym.h | 127 + .../sdl/src/video/wayland/SDL_waylandtouch.c | 265 + .../sdl/src/video/wayland/SDL_waylandtouch.h | 352 + .../sdl/src/video/wayland/SDL_waylandvideo.c | 526 + .../sdl/src/video/wayland/SDL_waylandvideo.h | 87 + .../sdl/src/video/wayland/SDL_waylandvulkan.c | 176 + .../sdl/src/video/wayland/SDL_waylandvulkan.h | 52 + .../sdl/src/video/wayland/SDL_waylandwindow.c | 759 + .../sdl/src/video/wayland/SDL_waylandwindow.h | 101 + .../sdl/src/video/windows/SDL_msctf.h | 242 + .../sdl/src/video/windows/SDL_vkeys.h | 76 + .../src/video/windows/SDL_windowsclipboard.c | 160 + .../src/video/windows/SDL_windowsclipboard.h | 36 + .../sdl/src/video/windows/SDL_windowsevents.c | 1242 + .../sdl/src/video/windows/SDL_windowsevents.h | 36 + .../video/windows/SDL_windowsframebuffer.c | 128 + .../video/windows/SDL_windowsframebuffer.h | 27 + .../src/video/windows/SDL_windowskeyboard.c | 1579 + .../src/video/windows/SDL_windowskeyboard.h | 40 + .../src/video/windows/SDL_windowsmessagebox.c | 905 + .../src/video/windows/SDL_windowsmessagebox.h | 29 + .../sdl/src/video/windows/SDL_windowsmodes.c | 407 + .../sdl/src/video/windows/SDL_windowsmodes.h | 47 + .../sdl/src/video/windows/SDL_windowsmouse.c | 321 + .../sdl/src/video/windows/SDL_windowsmouse.h | 33 + .../sdl/src/video/windows/SDL_windowsopengl.c | 895 + .../sdl/src/video/windows/SDL_windowsopengl.h | 142 + .../src/video/windows/SDL_windowsopengles.c | 131 + .../src/video/windows/SDL_windowsopengles.h | 49 + .../sdl/src/video/windows/SDL_windowsshape.c | 110 + .../sdl/src/video/windows/SDL_windowsshape.h | 40 + .../src/video/windows/SDL_windowstaskdialog.h | 156 + .../sdl/src/video/windows/SDL_windowsvideo.c | 452 + .../sdl/src/video/windows/SDL_windowsvideo.h | 199 + .../sdl/src/video/windows/SDL_windowsvulkan.c | 176 + .../sdl/src/video/windows/SDL_windowsvulkan.h | 52 + .../sdl/src/video/windows/SDL_windowswindow.c | 998 + .../sdl/src/video/windows/SDL_windowswindow.h | 87 + .../external/sdl/src/video/windows/wmmsg.h | 1052 + .../sdl/src/video/winrt/SDL_winrtevents.cpp | 154 + .../sdl/src/video/winrt/SDL_winrtevents_c.h | 82 + .../sdl/src/video/winrt/SDL_winrtgamebar.cpp | 196 + .../src/video/winrt/SDL_winrtgamebar_cpp.h | 35 + .../sdl/src/video/winrt/SDL_winrtkeyboard.cpp | 430 + .../src/video/winrt/SDL_winrtmessagebox.cpp | 112 + .../sdl/src/video/winrt/SDL_winrtmessagebox.h | 29 + .../sdl/src/video/winrt/SDL_winrtmouse.cpp | 224 + .../sdl/src/video/winrt/SDL_winrtmouse_c.h | 40 + .../sdl/src/video/winrt/SDL_winrtopengles.cpp | 203 + .../sdl/src/video/winrt/SDL_winrtopengles.h | 70 + .../src/video/winrt/SDL_winrtpointerinput.cpp | 415 + .../sdl/src/video/winrt/SDL_winrtvideo.cpp | 842 + .../sdl/src/video/winrt/SDL_winrtvideo_cpp.h | 106 + .../sdl/src/video/x11/SDL_x11clipboard.c | 199 + .../sdl/src/video/x11/SDL_x11clipboard.h | 33 + .../external/sdl/src/video/x11/SDL_x11dyn.c | 212 + .../external/sdl/src/video/x11/SDL_x11dyn.h | 111 + .../sdl/src/video/x11/SDL_x11events.c | 1500 + .../sdl/src/video/x11/SDL_x11events.h | 31 + .../sdl/src/video/x11/SDL_x11framebuffer.c | 257 + .../sdl/src/video/x11/SDL_x11framebuffer.h | 37 + .../sdl/src/video/x11/SDL_x11keyboard.c | 543 + .../sdl/src/video/x11/SDL_x11keyboard.h | 36 + .../sdl/src/video/x11/SDL_x11messagebox.c | 872 + .../sdl/src/video/x11/SDL_x11messagebox.h | 33 + .../external/sdl/src/video/x11/SDL_x11modes.c | 1112 + .../external/sdl/src/video/x11/SDL_x11modes.h | 85 + .../external/sdl/src/video/x11/SDL_x11mouse.c | 448 + .../external/sdl/src/video/x11/SDL_x11mouse.h | 31 + .../sdl/src/video/x11/SDL_x11opengl.c | 946 + .../sdl/src/video/x11/SDL_x11opengl.h | 84 + .../sdl/src/video/x11/SDL_x11opengles.c | 109 + .../sdl/src/video/x11/SDL_x11opengles.h | 56 + .../external/sdl/src/video/x11/SDL_x11shape.c | 115 + .../external/sdl/src/video/x11/SDL_x11shape.h | 39 + .../external/sdl/src/video/x11/SDL_x11sym.h | 337 + .../external/sdl/src/video/x11/SDL_x11touch.c | 54 + .../external/sdl/src/video/x11/SDL_x11touch.h | 32 + .../external/sdl/src/video/x11/SDL_x11video.c | 497 + .../external/sdl/src/video/x11/SDL_x11video.h | 156 + .../sdl/src/video/x11/SDL_x11vulkan.c | 243 + .../sdl/src/video/x11/SDL_x11vulkan.h | 48 + .../sdl/src/video/x11/SDL_x11window.c | 1619 + .../sdl/src/video/x11/SDL_x11window.h | 111 + .../sdl/src/video/x11/SDL_x11xinput2.c | 320 + .../sdl/src/video/x11/SDL_x11xinput2.h | 42 + .../external/sdl/src/video/x11/edid-parse.c | 754 + ion/src/sdl/external/sdl/src/video/x11/edid.h | 167 + .../external/sdl/src/video/x11/imKStoUCS.c | 350 + .../external/sdl/src/video/x11/imKStoUCS.h | 32 + .../external/sdl/src/video/yuv2rgb/LICENSE | 27 + .../external/sdl/src/video/yuv2rgb/README.md | 63 + .../external/sdl/src/video/yuv2rgb/yuv_rgb.c | 687 + .../external/sdl/src/video/yuv2rgb/yuv_rgb.h | 381 + .../sdl/src/video/yuv2rgb/yuv_rgb_sse_func.h | 498 + .../sdl/src/video/yuv2rgb/yuv_rgb_std_func.h | 228 + 800 files changed, 347871 insertions(+) create mode 100644 ion/src/sdl/android/src/java/org/libsdl/app/HIDDevice.java create mode 100644 ion/src/sdl/android/src/java/org/libsdl/app/HIDDeviceBLESteamController.java create mode 100644 ion/src/sdl/android/src/java/org/libsdl/app/HIDDeviceManager.java create mode 100644 ion/src/sdl/android/src/java/org/libsdl/app/HIDDeviceUSB.java create mode 100644 ion/src/sdl/android/src/java/org/libsdl/app/SDL.java create mode 100644 ion/src/sdl/android/src/java/org/libsdl/app/SDLActivity.java create mode 100644 ion/src/sdl/android/src/java/org/libsdl/app/SDLAudioManager.java create mode 100644 ion/src/sdl/android/src/java/org/libsdl/app/SDLControllerManager.java create mode 100644 ion/src/sdl/external/README.md create mode 100644 ion/src/sdl/external/sdl/include/SDL.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_assert.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_atomic.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_audio.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_bits.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_blendmode.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_clipboard.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_config.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_config_android.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_config_iphoneos.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_config_macosx.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_config_windows.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_cpuinfo.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_egl.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_endian.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_error.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_events.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_filesystem.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_gamecontroller.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_gesture.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_haptic.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_hints.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_joystick.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_keyboard.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_keycode.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_loadso.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_log.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_main.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_messagebox.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_mouse.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_mutex.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_name.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_opengl.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_opengl_glext.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_opengles.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_opengles2.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_opengles2_gl2.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_opengles2_gl2ext.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_opengles2_gl2platform.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_opengles2_khrplatform.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_pixels.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_platform.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_power.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_quit.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_rect.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_render.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_revision.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_rwops.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_scancode.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_sensor.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_shape.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_stdinc.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_surface.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_system.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_syswm.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_thread.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_timer.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_touch.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_types.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_version.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_video.h create mode 100644 ion/src/sdl/external/sdl/include/SDL_vulkan.h create mode 100644 ion/src/sdl/external/sdl/include/begin_code.h create mode 100644 ion/src/sdl/external/sdl/include/close_code.h create mode 100644 ion/src/sdl/external/sdl/src/SDL.c create mode 100644 ion/src/sdl/external/sdl/src/SDL_assert.c create mode 100644 ion/src/sdl/external/sdl/src/SDL_assert_c.h create mode 100644 ion/src/sdl/external/sdl/src/SDL_dataqueue.c create mode 100644 ion/src/sdl/external/sdl/src/SDL_dataqueue.h create mode 100644 ion/src/sdl/external/sdl/src/SDL_error.c create mode 100644 ion/src/sdl/external/sdl/src/SDL_error_c.h create mode 100644 ion/src/sdl/external/sdl/src/SDL_hints.c create mode 100644 ion/src/sdl/external/sdl/src/SDL_internal.h create mode 100644 ion/src/sdl/external/sdl/src/SDL_log.c create mode 100644 ion/src/sdl/external/sdl/src/atomic/SDL_atomic.c create mode 100644 ion/src/sdl/external/sdl/src/atomic/SDL_spinlock.c create mode 100644 ion/src/sdl/external/sdl/src/audio/SDL_audio.c create mode 100644 ion/src/sdl/external/sdl/src/audio/SDL_audio_c.h create mode 100644 ion/src/sdl/external/sdl/src/audio/SDL_audiocvt.c create mode 100644 ion/src/sdl/external/sdl/src/audio/SDL_audiodev.c create mode 100644 ion/src/sdl/external/sdl/src/audio/SDL_audiodev_c.h create mode 100644 ion/src/sdl/external/sdl/src/audio/SDL_audiotypecvt.c create mode 100644 ion/src/sdl/external/sdl/src/audio/SDL_mixer.c create mode 100644 ion/src/sdl/external/sdl/src/audio/SDL_sysaudio.h create mode 100644 ion/src/sdl/external/sdl/src/audio/SDL_wave.c create mode 100644 ion/src/sdl/external/sdl/src/audio/SDL_wave.h create mode 100644 ion/src/sdl/external/sdl/src/audio/alsa/SDL_alsa_audio.c create mode 100644 ion/src/sdl/external/sdl/src/audio/alsa/SDL_alsa_audio.h create mode 100644 ion/src/sdl/external/sdl/src/audio/android/SDL_androidaudio.c create mode 100644 ion/src/sdl/external/sdl/src/audio/android/SDL_androidaudio.h create mode 100644 ion/src/sdl/external/sdl/src/audio/arts/SDL_artsaudio.c create mode 100644 ion/src/sdl/external/sdl/src/audio/arts/SDL_artsaudio.h create mode 100644 ion/src/sdl/external/sdl/src/audio/coreaudio/SDL_coreaudio.h create mode 100644 ion/src/sdl/external/sdl/src/audio/coreaudio/SDL_coreaudio.m create mode 100644 ion/src/sdl/external/sdl/src/audio/directsound/SDL_directsound.c create mode 100644 ion/src/sdl/external/sdl/src/audio/directsound/SDL_directsound.h create mode 100644 ion/src/sdl/external/sdl/src/audio/disk/SDL_diskaudio.c create mode 100644 ion/src/sdl/external/sdl/src/audio/disk/SDL_diskaudio.h create mode 100644 ion/src/sdl/external/sdl/src/audio/dsp/SDL_dspaudio.c create mode 100644 ion/src/sdl/external/sdl/src/audio/dsp/SDL_dspaudio.h create mode 100644 ion/src/sdl/external/sdl/src/audio/dummy/SDL_dummyaudio.c create mode 100644 ion/src/sdl/external/sdl/src/audio/dummy/SDL_dummyaudio.h create mode 100644 ion/src/sdl/external/sdl/src/audio/emscripten/SDL_emscriptenaudio.c create mode 100644 ion/src/sdl/external/sdl/src/audio/emscripten/SDL_emscriptenaudio.h create mode 100644 ion/src/sdl/external/sdl/src/audio/esd/SDL_esdaudio.c create mode 100644 ion/src/sdl/external/sdl/src/audio/esd/SDL_esdaudio.h create mode 100644 ion/src/sdl/external/sdl/src/audio/fusionsound/SDL_fsaudio.c create mode 100644 ion/src/sdl/external/sdl/src/audio/fusionsound/SDL_fsaudio.h create mode 100644 ion/src/sdl/external/sdl/src/audio/haiku/SDL_haikuaudio.cc create mode 100644 ion/src/sdl/external/sdl/src/audio/haiku/SDL_haikuaudio.h create mode 100644 ion/src/sdl/external/sdl/src/audio/jack/SDL_jackaudio.c create mode 100644 ion/src/sdl/external/sdl/src/audio/jack/SDL_jackaudio.h create mode 100644 ion/src/sdl/external/sdl/src/audio/nacl/SDL_naclaudio.c create mode 100644 ion/src/sdl/external/sdl/src/audio/nacl/SDL_naclaudio.h create mode 100644 ion/src/sdl/external/sdl/src/audio/nas/SDL_nasaudio.c create mode 100644 ion/src/sdl/external/sdl/src/audio/nas/SDL_nasaudio.h create mode 100644 ion/src/sdl/external/sdl/src/audio/netbsd/SDL_netbsdaudio.c create mode 100644 ion/src/sdl/external/sdl/src/audio/netbsd/SDL_netbsdaudio.h create mode 100644 ion/src/sdl/external/sdl/src/audio/openslES/SDL_openslES.c create mode 100644 ion/src/sdl/external/sdl/src/audio/openslES/SDL_openslES.h create mode 100644 ion/src/sdl/external/sdl/src/audio/paudio/SDL_paudio.c create mode 100644 ion/src/sdl/external/sdl/src/audio/paudio/SDL_paudio.h create mode 100644 ion/src/sdl/external/sdl/src/audio/psp/SDL_pspaudio.c create mode 100644 ion/src/sdl/external/sdl/src/audio/psp/SDL_pspaudio.h create mode 100644 ion/src/sdl/external/sdl/src/audio/pulseaudio/SDL_pulseaudio.c create mode 100644 ion/src/sdl/external/sdl/src/audio/pulseaudio/SDL_pulseaudio.h create mode 100644 ion/src/sdl/external/sdl/src/audio/qsa/SDL_qsa_audio.c create mode 100644 ion/src/sdl/external/sdl/src/audio/qsa/SDL_qsa_audio.h create mode 100644 ion/src/sdl/external/sdl/src/audio/sndio/SDL_sndioaudio.c create mode 100644 ion/src/sdl/external/sdl/src/audio/sndio/SDL_sndioaudio.h create mode 100644 ion/src/sdl/external/sdl/src/audio/sun/SDL_sunaudio.c create mode 100644 ion/src/sdl/external/sdl/src/audio/sun/SDL_sunaudio.h create mode 100644 ion/src/sdl/external/sdl/src/audio/wasapi/SDL_wasapi.c create mode 100644 ion/src/sdl/external/sdl/src/audio/wasapi/SDL_wasapi.h create mode 100644 ion/src/sdl/external/sdl/src/audio/wasapi/SDL_wasapi_win32.c create mode 100644 ion/src/sdl/external/sdl/src/audio/wasapi/SDL_wasapi_winrt.cpp create mode 100644 ion/src/sdl/external/sdl/src/audio/winmm/SDL_winmm.c create mode 100644 ion/src/sdl/external/sdl/src/audio/winmm/SDL_winmm.h create mode 100644 ion/src/sdl/external/sdl/src/core/android/SDL_android.c create mode 100644 ion/src/sdl/external/sdl/src/core/android/SDL_android.h create mode 100644 ion/src/sdl/external/sdl/src/core/android/keyinfotable.h create mode 100644 ion/src/sdl/external/sdl/src/core/linux/SDL_dbus.c create mode 100644 ion/src/sdl/external/sdl/src/core/linux/SDL_dbus.h create mode 100644 ion/src/sdl/external/sdl/src/core/linux/SDL_evdev.c create mode 100644 ion/src/sdl/external/sdl/src/core/linux/SDL_evdev.h create mode 100644 ion/src/sdl/external/sdl/src/core/linux/SDL_evdev_kbd.c create mode 100644 ion/src/sdl/external/sdl/src/core/linux/SDL_evdev_kbd.h create mode 100644 ion/src/sdl/external/sdl/src/core/linux/SDL_evdev_kbd_default_accents.h create mode 100644 ion/src/sdl/external/sdl/src/core/linux/SDL_evdev_kbd_default_keymap.h create mode 100644 ion/src/sdl/external/sdl/src/core/linux/SDL_fcitx.c create mode 100644 ion/src/sdl/external/sdl/src/core/linux/SDL_fcitx.h create mode 100644 ion/src/sdl/external/sdl/src/core/linux/SDL_ibus.c create mode 100644 ion/src/sdl/external/sdl/src/core/linux/SDL_ibus.h create mode 100644 ion/src/sdl/external/sdl/src/core/linux/SDL_ime.c create mode 100644 ion/src/sdl/external/sdl/src/core/linux/SDL_ime.h create mode 100644 ion/src/sdl/external/sdl/src/core/linux/SDL_threadprio.c create mode 100644 ion/src/sdl/external/sdl/src/core/linux/SDL_udev.c create mode 100644 ion/src/sdl/external/sdl/src/core/linux/SDL_udev.h create mode 100644 ion/src/sdl/external/sdl/src/core/unix/SDL_poll.c create mode 100644 ion/src/sdl/external/sdl/src/core/unix/SDL_poll.h create mode 100644 ion/src/sdl/external/sdl/src/core/windows/SDL_directx.h create mode 100644 ion/src/sdl/external/sdl/src/core/windows/SDL_windows.c create mode 100644 ion/src/sdl/external/sdl/src/core/windows/SDL_windows.h create mode 100644 ion/src/sdl/external/sdl/src/core/windows/SDL_xinput.c create mode 100644 ion/src/sdl/external/sdl/src/core/windows/SDL_xinput.h create mode 100644 ion/src/sdl/external/sdl/src/core/winrt/SDL_winrtapp_common.cpp create mode 100644 ion/src/sdl/external/sdl/src/core/winrt/SDL_winrtapp_common.h create mode 100644 ion/src/sdl/external/sdl/src/core/winrt/SDL_winrtapp_direct3d.cpp create mode 100644 ion/src/sdl/external/sdl/src/core/winrt/SDL_winrtapp_direct3d.h create mode 100644 ion/src/sdl/external/sdl/src/core/winrt/SDL_winrtapp_xaml.cpp create mode 100644 ion/src/sdl/external/sdl/src/core/winrt/SDL_winrtapp_xaml.h create mode 100644 ion/src/sdl/external/sdl/src/cpuinfo/SDL_cpuinfo.c create mode 100644 ion/src/sdl/external/sdl/src/cpuinfo/SDL_simd.h create mode 100644 ion/src/sdl/external/sdl/src/dynapi/SDL_dynapi.c create mode 100644 ion/src/sdl/external/sdl/src/dynapi/SDL_dynapi.h create mode 100644 ion/src/sdl/external/sdl/src/dynapi/SDL_dynapi_overrides.h create mode 100644 ion/src/sdl/external/sdl/src/dynapi/SDL_dynapi_procs.h create mode 100755 ion/src/sdl/external/sdl/src/dynapi/gendynapi.pl create mode 100644 ion/src/sdl/external/sdl/src/events/SDL_clipboardevents.c create mode 100644 ion/src/sdl/external/sdl/src/events/SDL_clipboardevents_c.h create mode 100644 ion/src/sdl/external/sdl/src/events/SDL_displayevents.c create mode 100644 ion/src/sdl/external/sdl/src/events/SDL_displayevents_c.h create mode 100644 ion/src/sdl/external/sdl/src/events/SDL_dropevents.c create mode 100644 ion/src/sdl/external/sdl/src/events/SDL_dropevents_c.h create mode 100644 ion/src/sdl/external/sdl/src/events/SDL_events.c create mode 100644 ion/src/sdl/external/sdl/src/events/SDL_events_c.h create mode 100644 ion/src/sdl/external/sdl/src/events/SDL_gesture.c create mode 100644 ion/src/sdl/external/sdl/src/events/SDL_gesture_c.h create mode 100644 ion/src/sdl/external/sdl/src/events/SDL_keyboard.c create mode 100644 ion/src/sdl/external/sdl/src/events/SDL_keyboard_c.h create mode 100644 ion/src/sdl/external/sdl/src/events/SDL_mouse.c create mode 100644 ion/src/sdl/external/sdl/src/events/SDL_mouse_c.h create mode 100644 ion/src/sdl/external/sdl/src/events/SDL_quit.c create mode 100644 ion/src/sdl/external/sdl/src/events/SDL_sysevents.h create mode 100644 ion/src/sdl/external/sdl/src/events/SDL_touch.c create mode 100644 ion/src/sdl/external/sdl/src/events/SDL_touch_c.h create mode 100644 ion/src/sdl/external/sdl/src/events/SDL_windowevents.c create mode 100644 ion/src/sdl/external/sdl/src/events/SDL_windowevents_c.h create mode 100644 ion/src/sdl/external/sdl/src/events/blank_cursor.h create mode 100644 ion/src/sdl/external/sdl/src/events/default_cursor.h create mode 100644 ion/src/sdl/external/sdl/src/events/scancodes_darwin.h create mode 100644 ion/src/sdl/external/sdl/src/events/scancodes_linux.h create mode 100644 ion/src/sdl/external/sdl/src/events/scancodes_windows.h create mode 100644 ion/src/sdl/external/sdl/src/events/scancodes_xfree86.h create mode 100644 ion/src/sdl/external/sdl/src/file/SDL_rwops.c create mode 100644 ion/src/sdl/external/sdl/src/file/cocoa/SDL_rwopsbundlesupport.h create mode 100644 ion/src/sdl/external/sdl/src/file/cocoa/SDL_rwopsbundlesupport.m create mode 100644 ion/src/sdl/external/sdl/src/filesystem/android/SDL_sysfilesystem.c create mode 100644 ion/src/sdl/external/sdl/src/filesystem/cocoa/SDL_sysfilesystem.m create mode 100644 ion/src/sdl/external/sdl/src/filesystem/dummy/SDL_sysfilesystem.c create mode 100644 ion/src/sdl/external/sdl/src/filesystem/emscripten/SDL_sysfilesystem.c create mode 100644 ion/src/sdl/external/sdl/src/filesystem/haiku/SDL_sysfilesystem.cc create mode 100644 ion/src/sdl/external/sdl/src/filesystem/nacl/SDL_sysfilesystem.c create mode 100644 ion/src/sdl/external/sdl/src/filesystem/unix/SDL_sysfilesystem.c create mode 100644 ion/src/sdl/external/sdl/src/filesystem/windows/SDL_sysfilesystem.c create mode 100644 ion/src/sdl/external/sdl/src/filesystem/winrt/SDL_sysfilesystem.cpp create mode 100644 ion/src/sdl/external/sdl/src/haptic/SDL_haptic.c create mode 100644 ion/src/sdl/external/sdl/src/haptic/SDL_haptic_c.h create mode 100644 ion/src/sdl/external/sdl/src/haptic/SDL_syshaptic.h create mode 100644 ion/src/sdl/external/sdl/src/haptic/android/SDL_syshaptic.c create mode 100644 ion/src/sdl/external/sdl/src/haptic/android/SDL_syshaptic_c.h create mode 100644 ion/src/sdl/external/sdl/src/haptic/darwin/SDL_syshaptic.c create mode 100644 ion/src/sdl/external/sdl/src/haptic/darwin/SDL_syshaptic_c.h create mode 100644 ion/src/sdl/external/sdl/src/haptic/dummy/SDL_syshaptic.c create mode 100644 ion/src/sdl/external/sdl/src/haptic/linux/SDL_syshaptic.c create mode 100644 ion/src/sdl/external/sdl/src/haptic/windows/SDL_dinputhaptic.c create mode 100644 ion/src/sdl/external/sdl/src/haptic/windows/SDL_dinputhaptic_c.h create mode 100644 ion/src/sdl/external/sdl/src/haptic/windows/SDL_windowshaptic.c create mode 100644 ion/src/sdl/external/sdl/src/haptic/windows/SDL_windowshaptic_c.h create mode 100644 ion/src/sdl/external/sdl/src/haptic/windows/SDL_xinputhaptic.c create mode 100644 ion/src/sdl/external/sdl/src/haptic/windows/SDL_xinputhaptic_c.h create mode 100644 ion/src/sdl/external/sdl/src/hidapi/LICENSE-bsd.txt create mode 100644 ion/src/sdl/external/sdl/src/hidapi/LICENSE-gpl3.txt create mode 100644 ion/src/sdl/external/sdl/src/hidapi/LICENSE-orig.txt create mode 100644 ion/src/sdl/external/sdl/src/hidapi/LICENSE.txt create mode 100644 ion/src/sdl/external/sdl/src/hidapi/android/hid.cpp create mode 100644 ion/src/sdl/external/sdl/src/hidapi/hidapi/hidapi.h create mode 100644 ion/src/sdl/external/sdl/src/hidapi/ios/hid.m create mode 100644 ion/src/sdl/external/sdl/src/hidapi/libusb/hid.c create mode 100644 ion/src/sdl/external/sdl/src/hidapi/libusb/hidusb.cpp create mode 100644 ion/src/sdl/external/sdl/src/hidapi/linux/hid.c create mode 100644 ion/src/sdl/external/sdl/src/hidapi/linux/hid.cpp create mode 100644 ion/src/sdl/external/sdl/src/hidapi/linux/hidraw.cpp create mode 100644 ion/src/sdl/external/sdl/src/hidapi/mac/hid.c create mode 100644 ion/src/sdl/external/sdl/src/hidapi/windows/hid.c create mode 100644 ion/src/sdl/external/sdl/src/joystick/SDL_gamecontroller.c create mode 100644 ion/src/sdl/external/sdl/src/joystick/SDL_gamecontrollerdb.h create mode 100644 ion/src/sdl/external/sdl/src/joystick/SDL_joystick.c create mode 100644 ion/src/sdl/external/sdl/src/joystick/SDL_joystick_c.h create mode 100644 ion/src/sdl/external/sdl/src/joystick/SDL_sysjoystick.h create mode 100644 ion/src/sdl/external/sdl/src/joystick/android/SDL_sysjoystick.c create mode 100644 ion/src/sdl/external/sdl/src/joystick/android/SDL_sysjoystick_c.h create mode 100644 ion/src/sdl/external/sdl/src/joystick/bsd/SDL_sysjoystick.c create mode 100644 ion/src/sdl/external/sdl/src/joystick/controller_type.h create mode 100644 ion/src/sdl/external/sdl/src/joystick/darwin/SDL_sysjoystick.c create mode 100644 ion/src/sdl/external/sdl/src/joystick/darwin/SDL_sysjoystick_c.h create mode 100644 ion/src/sdl/external/sdl/src/joystick/dummy/SDL_sysjoystick.c create mode 100644 ion/src/sdl/external/sdl/src/joystick/emscripten/SDL_sysjoystick.c create mode 100644 ion/src/sdl/external/sdl/src/joystick/emscripten/SDL_sysjoystick_c.h create mode 100644 ion/src/sdl/external/sdl/src/joystick/haiku/SDL_haikujoystick.cc create mode 100644 ion/src/sdl/external/sdl/src/joystick/hidapi/SDL_hidapi_ps4.c create mode 100644 ion/src/sdl/external/sdl/src/joystick/hidapi/SDL_hidapi_switch.c create mode 100644 ion/src/sdl/external/sdl/src/joystick/hidapi/SDL_hidapi_xbox360.c create mode 100644 ion/src/sdl/external/sdl/src/joystick/hidapi/SDL_hidapi_xboxone.c create mode 100644 ion/src/sdl/external/sdl/src/joystick/hidapi/SDL_hidapijoystick.c create mode 100644 ion/src/sdl/external/sdl/src/joystick/hidapi/SDL_hidapijoystick_c.h create mode 100644 ion/src/sdl/external/sdl/src/joystick/iphoneos/SDL_sysjoystick.m create mode 100644 ion/src/sdl/external/sdl/src/joystick/iphoneos/SDL_sysjoystick_c.h create mode 100644 ion/src/sdl/external/sdl/src/joystick/linux/SDL_sysjoystick.c create mode 100644 ion/src/sdl/external/sdl/src/joystick/linux/SDL_sysjoystick_c.h create mode 100644 ion/src/sdl/external/sdl/src/joystick/psp/SDL_sysjoystick.c create mode 100755 ion/src/sdl/external/sdl/src/joystick/sort_controllers.py create mode 100644 ion/src/sdl/external/sdl/src/joystick/steam/SDL_steamcontroller.c create mode 100644 ion/src/sdl/external/sdl/src/joystick/steam/SDL_steamcontroller.h create mode 100644 ion/src/sdl/external/sdl/src/joystick/windows/SDL_dinputjoystick.c create mode 100644 ion/src/sdl/external/sdl/src/joystick/windows/SDL_dinputjoystick_c.h create mode 100644 ion/src/sdl/external/sdl/src/joystick/windows/SDL_mmjoystick.c create mode 100644 ion/src/sdl/external/sdl/src/joystick/windows/SDL_windowsjoystick.c create mode 100644 ion/src/sdl/external/sdl/src/joystick/windows/SDL_windowsjoystick_c.h create mode 100644 ion/src/sdl/external/sdl/src/joystick/windows/SDL_xinputjoystick.c create mode 100644 ion/src/sdl/external/sdl/src/joystick/windows/SDL_xinputjoystick_c.h create mode 100644 ion/src/sdl/external/sdl/src/libm/e_atan2.c create mode 100644 ion/src/sdl/external/sdl/src/libm/e_exp.c create mode 100644 ion/src/sdl/external/sdl/src/libm/e_fmod.c create mode 100644 ion/src/sdl/external/sdl/src/libm/e_log.c create mode 100644 ion/src/sdl/external/sdl/src/libm/e_log10.c create mode 100644 ion/src/sdl/external/sdl/src/libm/e_pow.c create mode 100644 ion/src/sdl/external/sdl/src/libm/e_rem_pio2.c create mode 100644 ion/src/sdl/external/sdl/src/libm/e_sqrt.c create mode 100644 ion/src/sdl/external/sdl/src/libm/k_cos.c create mode 100644 ion/src/sdl/external/sdl/src/libm/k_rem_pio2.c create mode 100644 ion/src/sdl/external/sdl/src/libm/k_sin.c create mode 100644 ion/src/sdl/external/sdl/src/libm/k_tan.c create mode 100644 ion/src/sdl/external/sdl/src/libm/math_libm.h create mode 100644 ion/src/sdl/external/sdl/src/libm/math_private.h create mode 100644 ion/src/sdl/external/sdl/src/libm/s_atan.c create mode 100644 ion/src/sdl/external/sdl/src/libm/s_copysign.c create mode 100644 ion/src/sdl/external/sdl/src/libm/s_cos.c create mode 100644 ion/src/sdl/external/sdl/src/libm/s_fabs.c create mode 100644 ion/src/sdl/external/sdl/src/libm/s_floor.c create mode 100644 ion/src/sdl/external/sdl/src/libm/s_scalbn.c create mode 100644 ion/src/sdl/external/sdl/src/libm/s_sin.c create mode 100644 ion/src/sdl/external/sdl/src/libm/s_tan.c create mode 100644 ion/src/sdl/external/sdl/src/loadso/dlopen/SDL_sysloadso.c create mode 100644 ion/src/sdl/external/sdl/src/loadso/dummy/SDL_sysloadso.c create mode 100644 ion/src/sdl/external/sdl/src/loadso/windows/SDL_sysloadso.c create mode 100644 ion/src/sdl/external/sdl/src/main/android/SDL_android_main.c create mode 100644 ion/src/sdl/external/sdl/src/main/dummy/SDL_dummy_main.c create mode 100644 ion/src/sdl/external/sdl/src/main/haiku/SDL_BApp.h create mode 100644 ion/src/sdl/external/sdl/src/main/haiku/SDL_BeApp.cc create mode 100644 ion/src/sdl/external/sdl/src/main/haiku/SDL_BeApp.h create mode 100644 ion/src/sdl/external/sdl/src/main/nacl/SDL_nacl_main.c create mode 100644 ion/src/sdl/external/sdl/src/main/psp/SDL_psp_main.c create mode 100644 ion/src/sdl/external/sdl/src/main/windows/SDL_windows_main.c create mode 100644 ion/src/sdl/external/sdl/src/main/windows/version.rc create mode 100644 ion/src/sdl/external/sdl/src/main/winrt/SDL2-WinRTResource_BlankCursor.cur create mode 100644 ion/src/sdl/external/sdl/src/main/winrt/SDL2-WinRTResources.rc create mode 100644 ion/src/sdl/external/sdl/src/main/winrt/SDL_winrt_main_NonXAML.cpp create mode 100644 ion/src/sdl/external/sdl/src/power/SDL_power.c create mode 100644 ion/src/sdl/external/sdl/src/power/SDL_syspower.h create mode 100644 ion/src/sdl/external/sdl/src/power/android/SDL_syspower.c create mode 100644 ion/src/sdl/external/sdl/src/power/emscripten/SDL_syspower.c create mode 100644 ion/src/sdl/external/sdl/src/power/haiku/SDL_syspower.c create mode 100644 ion/src/sdl/external/sdl/src/power/linux/SDL_syspower.c create mode 100644 ion/src/sdl/external/sdl/src/power/macosx/SDL_syspower.c create mode 100644 ion/src/sdl/external/sdl/src/power/psp/SDL_syspower.c create mode 100644 ion/src/sdl/external/sdl/src/power/uikit/SDL_syspower.h create mode 100644 ion/src/sdl/external/sdl/src/power/uikit/SDL_syspower.m create mode 100644 ion/src/sdl/external/sdl/src/power/windows/SDL_syspower.c create mode 100644 ion/src/sdl/external/sdl/src/power/winrt/SDL_syspower.cpp create mode 100644 ion/src/sdl/external/sdl/src/render/SDL_d3dmath.c create mode 100644 ion/src/sdl/external/sdl/src/render/SDL_d3dmath.h create mode 100644 ion/src/sdl/external/sdl/src/render/SDL_render.c create mode 100644 ion/src/sdl/external/sdl/src/render/SDL_sysrender.h create mode 100644 ion/src/sdl/external/sdl/src/render/SDL_yuv_sw.c create mode 100644 ion/src/sdl/external/sdl/src/render/SDL_yuv_sw_c.h create mode 100644 ion/src/sdl/external/sdl/src/render/direct3d/SDL_render_d3d.c create mode 100644 ion/src/sdl/external/sdl/src/render/direct3d/SDL_shaders_d3d.c create mode 100644 ion/src/sdl/external/sdl/src/render/direct3d/SDL_shaders_d3d.h create mode 100644 ion/src/sdl/external/sdl/src/render/direct3d11/SDL_render_d3d11.c create mode 100644 ion/src/sdl/external/sdl/src/render/direct3d11/SDL_render_winrt.cpp create mode 100644 ion/src/sdl/external/sdl/src/render/direct3d11/SDL_render_winrt.h create mode 100644 ion/src/sdl/external/sdl/src/render/direct3d11/SDL_shaders_d3d11.c create mode 100644 ion/src/sdl/external/sdl/src/render/direct3d11/SDL_shaders_d3d11.h create mode 100644 ion/src/sdl/external/sdl/src/render/metal/SDL_render_metal.m create mode 100644 ion/src/sdl/external/sdl/src/render/metal/SDL_shaders_metal.metal create mode 100644 ion/src/sdl/external/sdl/src/render/metal/SDL_shaders_metal_ios.h create mode 100644 ion/src/sdl/external/sdl/src/render/metal/SDL_shaders_metal_osx.h create mode 100755 ion/src/sdl/external/sdl/src/render/metal/build-metal-shaders.sh create mode 100644 ion/src/sdl/external/sdl/src/render/opengl/SDL_glfuncs.h create mode 100644 ion/src/sdl/external/sdl/src/render/opengl/SDL_render_gl.c create mode 100644 ion/src/sdl/external/sdl/src/render/opengl/SDL_shaders_gl.c create mode 100644 ion/src/sdl/external/sdl/src/render/opengl/SDL_shaders_gl.h create mode 100644 ion/src/sdl/external/sdl/src/render/opengles/SDL_glesfuncs.h create mode 100644 ion/src/sdl/external/sdl/src/render/opengles/SDL_render_gles.c create mode 100644 ion/src/sdl/external/sdl/src/render/opengles2/SDL_gles2funcs.h create mode 100644 ion/src/sdl/external/sdl/src/render/opengles2/SDL_render_gles2.c create mode 100644 ion/src/sdl/external/sdl/src/render/opengles2/SDL_shaders_gles2.c create mode 100644 ion/src/sdl/external/sdl/src/render/opengles2/SDL_shaders_gles2.h create mode 100644 ion/src/sdl/external/sdl/src/render/psp/SDL_render_psp.c create mode 100644 ion/src/sdl/external/sdl/src/render/software/SDL_blendfillrect.c create mode 100644 ion/src/sdl/external/sdl/src/render/software/SDL_blendfillrect.h create mode 100644 ion/src/sdl/external/sdl/src/render/software/SDL_blendline.c create mode 100644 ion/src/sdl/external/sdl/src/render/software/SDL_blendline.h create mode 100644 ion/src/sdl/external/sdl/src/render/software/SDL_blendpoint.c create mode 100644 ion/src/sdl/external/sdl/src/render/software/SDL_blendpoint.h create mode 100644 ion/src/sdl/external/sdl/src/render/software/SDL_draw.h create mode 100644 ion/src/sdl/external/sdl/src/render/software/SDL_drawline.c create mode 100644 ion/src/sdl/external/sdl/src/render/software/SDL_drawline.h create mode 100644 ion/src/sdl/external/sdl/src/render/software/SDL_drawpoint.c create mode 100644 ion/src/sdl/external/sdl/src/render/software/SDL_drawpoint.h create mode 100644 ion/src/sdl/external/sdl/src/render/software/SDL_render_sw.c create mode 100644 ion/src/sdl/external/sdl/src/render/software/SDL_render_sw_c.h create mode 100644 ion/src/sdl/external/sdl/src/render/software/SDL_rotate.c create mode 100644 ion/src/sdl/external/sdl/src/render/software/SDL_rotate.h create mode 100644 ion/src/sdl/external/sdl/src/sensor/SDL_sensor.c create mode 100644 ion/src/sdl/external/sdl/src/sensor/SDL_sensor_c.h create mode 100644 ion/src/sdl/external/sdl/src/sensor/SDL_syssensor.h create mode 100644 ion/src/sdl/external/sdl/src/sensor/android/SDL_androidsensor.c create mode 100644 ion/src/sdl/external/sdl/src/sensor/android/SDL_androidsensor.h create mode 100644 ion/src/sdl/external/sdl/src/sensor/coremotion/SDL_coremotionsensor.h create mode 100644 ion/src/sdl/external/sdl/src/sensor/coremotion/SDL_coremotionsensor.m create mode 100644 ion/src/sdl/external/sdl/src/sensor/dummy/SDL_dummysensor.c create mode 100644 ion/src/sdl/external/sdl/src/sensor/dummy/SDL_dummysensor.h create mode 100644 ion/src/sdl/external/sdl/src/stdlib/SDL_getenv.c create mode 100644 ion/src/sdl/external/sdl/src/stdlib/SDL_iconv.c create mode 100644 ion/src/sdl/external/sdl/src/stdlib/SDL_malloc.c create mode 100644 ion/src/sdl/external/sdl/src/stdlib/SDL_qsort.c create mode 100644 ion/src/sdl/external/sdl/src/stdlib/SDL_stdlib.c create mode 100644 ion/src/sdl/external/sdl/src/stdlib/SDL_string.c create mode 100644 ion/src/sdl/external/sdl/src/test/SDL_test_assert.c create mode 100644 ion/src/sdl/external/sdl/src/test/SDL_test_common.c create mode 100644 ion/src/sdl/external/sdl/src/test/SDL_test_compare.c create mode 100644 ion/src/sdl/external/sdl/src/test/SDL_test_crc32.c create mode 100644 ion/src/sdl/external/sdl/src/test/SDL_test_font.c create mode 100644 ion/src/sdl/external/sdl/src/test/SDL_test_fuzzer.c create mode 100644 ion/src/sdl/external/sdl/src/test/SDL_test_harness.c create mode 100644 ion/src/sdl/external/sdl/src/test/SDL_test_imageBlit.c create mode 100644 ion/src/sdl/external/sdl/src/test/SDL_test_imageBlitBlend.c create mode 100644 ion/src/sdl/external/sdl/src/test/SDL_test_imageFace.c create mode 100644 ion/src/sdl/external/sdl/src/test/SDL_test_imagePrimitives.c create mode 100644 ion/src/sdl/external/sdl/src/test/SDL_test_imagePrimitivesBlend.c create mode 100644 ion/src/sdl/external/sdl/src/test/SDL_test_log.c create mode 100644 ion/src/sdl/external/sdl/src/test/SDL_test_md5.c create mode 100644 ion/src/sdl/external/sdl/src/test/SDL_test_memory.c create mode 100644 ion/src/sdl/external/sdl/src/test/SDL_test_random.c create mode 100644 ion/src/sdl/external/sdl/src/thread/SDL_systhread.h create mode 100644 ion/src/sdl/external/sdl/src/thread/SDL_thread.c create mode 100644 ion/src/sdl/external/sdl/src/thread/SDL_thread_c.h create mode 100644 ion/src/sdl/external/sdl/src/thread/generic/SDL_syscond.c create mode 100644 ion/src/sdl/external/sdl/src/thread/generic/SDL_sysmutex.c create mode 100644 ion/src/sdl/external/sdl/src/thread/generic/SDL_sysmutex_c.h create mode 100644 ion/src/sdl/external/sdl/src/thread/generic/SDL_syssem.c create mode 100644 ion/src/sdl/external/sdl/src/thread/generic/SDL_systhread.c create mode 100644 ion/src/sdl/external/sdl/src/thread/generic/SDL_systhread_c.h create mode 100644 ion/src/sdl/external/sdl/src/thread/generic/SDL_systls.c create mode 100644 ion/src/sdl/external/sdl/src/thread/psp/SDL_syscond.c create mode 100644 ion/src/sdl/external/sdl/src/thread/psp/SDL_sysmutex.c create mode 100644 ion/src/sdl/external/sdl/src/thread/psp/SDL_sysmutex_c.h create mode 100644 ion/src/sdl/external/sdl/src/thread/psp/SDL_syssem.c create mode 100644 ion/src/sdl/external/sdl/src/thread/psp/SDL_systhread.c create mode 100644 ion/src/sdl/external/sdl/src/thread/psp/SDL_systhread_c.h create mode 100644 ion/src/sdl/external/sdl/src/thread/pthread/SDL_syscond.c create mode 100644 ion/src/sdl/external/sdl/src/thread/pthread/SDL_sysmutex.c create mode 100644 ion/src/sdl/external/sdl/src/thread/pthread/SDL_sysmutex_c.h create mode 100644 ion/src/sdl/external/sdl/src/thread/pthread/SDL_syssem.c create mode 100644 ion/src/sdl/external/sdl/src/thread/pthread/SDL_systhread.c create mode 100644 ion/src/sdl/external/sdl/src/thread/pthread/SDL_systhread_c.h create mode 100644 ion/src/sdl/external/sdl/src/thread/pthread/SDL_systls.c create mode 100644 ion/src/sdl/external/sdl/src/thread/stdcpp/SDL_syscond.cpp create mode 100644 ion/src/sdl/external/sdl/src/thread/stdcpp/SDL_sysmutex.cpp create mode 100644 ion/src/sdl/external/sdl/src/thread/stdcpp/SDL_sysmutex_c.h create mode 100644 ion/src/sdl/external/sdl/src/thread/stdcpp/SDL_systhread.cpp create mode 100644 ion/src/sdl/external/sdl/src/thread/stdcpp/SDL_systhread_c.h create mode 100644 ion/src/sdl/external/sdl/src/thread/windows/SDL_sysmutex.c create mode 100644 ion/src/sdl/external/sdl/src/thread/windows/SDL_syssem.c create mode 100644 ion/src/sdl/external/sdl/src/thread/windows/SDL_systhread.c create mode 100644 ion/src/sdl/external/sdl/src/thread/windows/SDL_systhread_c.h create mode 100644 ion/src/sdl/external/sdl/src/thread/windows/SDL_systls.c create mode 100644 ion/src/sdl/external/sdl/src/timer/SDL_timer.c create mode 100644 ion/src/sdl/external/sdl/src/timer/SDL_timer_c.h create mode 100644 ion/src/sdl/external/sdl/src/timer/dummy/SDL_systimer.c create mode 100644 ion/src/sdl/external/sdl/src/timer/haiku/SDL_systimer.c create mode 100644 ion/src/sdl/external/sdl/src/timer/psp/SDL_systimer.c create mode 100644 ion/src/sdl/external/sdl/src/timer/unix/SDL_systimer.c create mode 100644 ion/src/sdl/external/sdl/src/timer/windows/SDL_systimer.c create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_RLEaccel.c create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_RLEaccel_c.h create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_blit.c create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_blit.h create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_blit_0.c create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_blit_1.c create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_blit_A.c create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_blit_N.c create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_blit_auto.c create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_blit_auto.h create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_blit_copy.c create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_blit_copy.h create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_blit_slow.c create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_blit_slow.h create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_bmp.c create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_clipboard.c create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_egl.c create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_egl_c.h create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_fillrect.c create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_pixels.c create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_pixels_c.h create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_rect.c create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_rect_c.h create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_shape.c create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_shape_internals.h create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_stretch.c create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_surface.c create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_sysvideo.h create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_video.c create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_vulkan_internal.h create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_vulkan_utils.c create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_yuv.c create mode 100644 ion/src/sdl/external/sdl/src/video/SDL_yuv_c.h create mode 100644 ion/src/sdl/external/sdl/src/video/android/SDL_androidclipboard.c create mode 100644 ion/src/sdl/external/sdl/src/video/android/SDL_androidclipboard.h create mode 100644 ion/src/sdl/external/sdl/src/video/android/SDL_androidevents.c create mode 100644 ion/src/sdl/external/sdl/src/video/android/SDL_androidevents.h create mode 100644 ion/src/sdl/external/sdl/src/video/android/SDL_androidgl.c create mode 100644 ion/src/sdl/external/sdl/src/video/android/SDL_androidgl.h create mode 100644 ion/src/sdl/external/sdl/src/video/android/SDL_androidkeyboard.c create mode 100644 ion/src/sdl/external/sdl/src/video/android/SDL_androidkeyboard.h create mode 100644 ion/src/sdl/external/sdl/src/video/android/SDL_androidmessagebox.c create mode 100644 ion/src/sdl/external/sdl/src/video/android/SDL_androidmessagebox.h create mode 100644 ion/src/sdl/external/sdl/src/video/android/SDL_androidmouse.c create mode 100644 ion/src/sdl/external/sdl/src/video/android/SDL_androidmouse.h create mode 100644 ion/src/sdl/external/sdl/src/video/android/SDL_androidtouch.c create mode 100644 ion/src/sdl/external/sdl/src/video/android/SDL_androidtouch.h create mode 100644 ion/src/sdl/external/sdl/src/video/android/SDL_androidvideo.c create mode 100644 ion/src/sdl/external/sdl/src/video/android/SDL_androidvideo.h create mode 100644 ion/src/sdl/external/sdl/src/video/android/SDL_androidvulkan.c create mode 100644 ion/src/sdl/external/sdl/src/video/android/SDL_androidvulkan.h create mode 100644 ion/src/sdl/external/sdl/src/video/android/SDL_androidwindow.c create mode 100644 ion/src/sdl/external/sdl/src/video/android/SDL_androidwindow.h create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoaclipboard.h create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoaclipboard.m create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoaevents.h create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoaevents.m create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoakeyboard.h create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoakeyboard.m create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoamessagebox.h create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoamessagebox.m create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoametalview.h create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoametalview.m create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoamodes.h create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoamodes.m create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoamouse.h create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoamouse.m create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoamousetap.h create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoamousetap.m create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoaopengl.h create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoaopengl.m create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoaopengles.h create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoaopengles.m create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoashape.h create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoashape.m create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoavideo.h create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoavideo.m create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoavulkan.h create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoavulkan.m create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoawindow.h create mode 100644 ion/src/sdl/external/sdl/src/video/cocoa/SDL_cocoawindow.m create mode 100644 ion/src/sdl/external/sdl/src/video/directfb/SDL_DirectFB_WM.c create mode 100644 ion/src/sdl/external/sdl/src/video/directfb/SDL_DirectFB_WM.h create mode 100644 ion/src/sdl/external/sdl/src/video/directfb/SDL_DirectFB_dyn.c create mode 100644 ion/src/sdl/external/sdl/src/video/directfb/SDL_DirectFB_dyn.h create mode 100644 ion/src/sdl/external/sdl/src/video/directfb/SDL_DirectFB_events.c create mode 100644 ion/src/sdl/external/sdl/src/video/directfb/SDL_DirectFB_events.h create mode 100644 ion/src/sdl/external/sdl/src/video/directfb/SDL_DirectFB_modes.c create mode 100644 ion/src/sdl/external/sdl/src/video/directfb/SDL_DirectFB_modes.h create mode 100644 ion/src/sdl/external/sdl/src/video/directfb/SDL_DirectFB_mouse.c create mode 100644 ion/src/sdl/external/sdl/src/video/directfb/SDL_DirectFB_mouse.h create mode 100644 ion/src/sdl/external/sdl/src/video/directfb/SDL_DirectFB_opengl.c create mode 100644 ion/src/sdl/external/sdl/src/video/directfb/SDL_DirectFB_opengl.h create mode 100644 ion/src/sdl/external/sdl/src/video/directfb/SDL_DirectFB_render.c create mode 100644 ion/src/sdl/external/sdl/src/video/directfb/SDL_DirectFB_render.h create mode 100644 ion/src/sdl/external/sdl/src/video/directfb/SDL_DirectFB_shape.c create mode 100644 ion/src/sdl/external/sdl/src/video/directfb/SDL_DirectFB_shape.h create mode 100644 ion/src/sdl/external/sdl/src/video/directfb/SDL_DirectFB_video.c create mode 100644 ion/src/sdl/external/sdl/src/video/directfb/SDL_DirectFB_video.h create mode 100644 ion/src/sdl/external/sdl/src/video/directfb/SDL_DirectFB_window.c create mode 100644 ion/src/sdl/external/sdl/src/video/directfb/SDL_DirectFB_window.h create mode 100644 ion/src/sdl/external/sdl/src/video/dummy/SDL_nullevents.c create mode 100644 ion/src/sdl/external/sdl/src/video/dummy/SDL_nullevents_c.h create mode 100644 ion/src/sdl/external/sdl/src/video/dummy/SDL_nullframebuffer.c create mode 100644 ion/src/sdl/external/sdl/src/video/dummy/SDL_nullframebuffer_c.h create mode 100644 ion/src/sdl/external/sdl/src/video/dummy/SDL_nullvideo.c create mode 100644 ion/src/sdl/external/sdl/src/video/dummy/SDL_nullvideo.h create mode 100644 ion/src/sdl/external/sdl/src/video/emscripten/SDL_emscriptenevents.c create mode 100644 ion/src/sdl/external/sdl/src/video/emscripten/SDL_emscriptenevents.h create mode 100644 ion/src/sdl/external/sdl/src/video/emscripten/SDL_emscriptenframebuffer.c create mode 100644 ion/src/sdl/external/sdl/src/video/emscripten/SDL_emscriptenframebuffer.h create mode 100644 ion/src/sdl/external/sdl/src/video/emscripten/SDL_emscriptenmouse.c create mode 100644 ion/src/sdl/external/sdl/src/video/emscripten/SDL_emscriptenmouse.h create mode 100644 ion/src/sdl/external/sdl/src/video/emscripten/SDL_emscriptenopengles.c create mode 100644 ion/src/sdl/external/sdl/src/video/emscripten/SDL_emscriptenopengles.h create mode 100644 ion/src/sdl/external/sdl/src/video/emscripten/SDL_emscriptenvideo.c create mode 100644 ion/src/sdl/external/sdl/src/video/emscripten/SDL_emscriptenvideo.h create mode 100644 ion/src/sdl/external/sdl/src/video/haiku/SDL_BWin.h create mode 100644 ion/src/sdl/external/sdl/src/video/haiku/SDL_bclipboard.cc create mode 100644 ion/src/sdl/external/sdl/src/video/haiku/SDL_bclipboard.h create mode 100644 ion/src/sdl/external/sdl/src/video/haiku/SDL_bevents.cc create mode 100644 ion/src/sdl/external/sdl/src/video/haiku/SDL_bevents.h create mode 100644 ion/src/sdl/external/sdl/src/video/haiku/SDL_bframebuffer.cc create mode 100644 ion/src/sdl/external/sdl/src/video/haiku/SDL_bframebuffer.h create mode 100644 ion/src/sdl/external/sdl/src/video/haiku/SDL_bkeyboard.cc create mode 100644 ion/src/sdl/external/sdl/src/video/haiku/SDL_bkeyboard.h create mode 100644 ion/src/sdl/external/sdl/src/video/haiku/SDL_bmodes.cc create mode 100644 ion/src/sdl/external/sdl/src/video/haiku/SDL_bmodes.h create mode 100644 ion/src/sdl/external/sdl/src/video/haiku/SDL_bopengl.cc create mode 100644 ion/src/sdl/external/sdl/src/video/haiku/SDL_bopengl.h create mode 100644 ion/src/sdl/external/sdl/src/video/haiku/SDL_bvideo.cc create mode 100644 ion/src/sdl/external/sdl/src/video/haiku/SDL_bvideo.h create mode 100644 ion/src/sdl/external/sdl/src/video/haiku/SDL_bwindow.cc create mode 100644 ion/src/sdl/external/sdl/src/video/haiku/SDL_bwindow.h create mode 100644 ion/src/sdl/external/sdl/src/video/khronos/EGL/egl.h create mode 100644 ion/src/sdl/external/sdl/src/video/khronos/EGL/eglext.h create mode 100644 ion/src/sdl/external/sdl/src/video/khronos/EGL/eglplatform.h create mode 100644 ion/src/sdl/external/sdl/src/video/khronos/GLES2/gl2.h create mode 100644 ion/src/sdl/external/sdl/src/video/khronos/GLES2/gl2ext.h create mode 100644 ion/src/sdl/external/sdl/src/video/khronos/GLES2/gl2platform.h create mode 100644 ion/src/sdl/external/sdl/src/video/khronos/KHR/khrplatform.h create mode 100644 ion/src/sdl/external/sdl/src/video/khronos/vulkan/vk_icd.h create mode 100644 ion/src/sdl/external/sdl/src/video/khronos/vulkan/vk_layer.h create mode 100644 ion/src/sdl/external/sdl/src/video/khronos/vulkan/vk_platform.h create mode 100644 ion/src/sdl/external/sdl/src/video/khronos/vulkan/vk_sdk_platform.h create mode 100644 ion/src/sdl/external/sdl/src/video/khronos/vulkan/vulkan.h create mode 100644 ion/src/sdl/external/sdl/src/video/khronos/vulkan/vulkan.hpp create mode 100644 ion/src/sdl/external/sdl/src/video/khronos/vulkan/vulkan_android.h create mode 100644 ion/src/sdl/external/sdl/src/video/khronos/vulkan/vulkan_core.h create mode 100644 ion/src/sdl/external/sdl/src/video/khronos/vulkan/vulkan_fuchsia.h create mode 100644 ion/src/sdl/external/sdl/src/video/khronos/vulkan/vulkan_ios.h create mode 100644 ion/src/sdl/external/sdl/src/video/khronos/vulkan/vulkan_macos.h create mode 100644 ion/src/sdl/external/sdl/src/video/khronos/vulkan/vulkan_mir.h create mode 100644 ion/src/sdl/external/sdl/src/video/khronos/vulkan/vulkan_vi.h create mode 100644 ion/src/sdl/external/sdl/src/video/khronos/vulkan/vulkan_wayland.h create mode 100644 ion/src/sdl/external/sdl/src/video/khronos/vulkan/vulkan_win32.h create mode 100644 ion/src/sdl/external/sdl/src/video/khronos/vulkan/vulkan_xcb.h create mode 100644 ion/src/sdl/external/sdl/src/video/khronos/vulkan/vulkan_xlib.h create mode 100644 ion/src/sdl/external/sdl/src/video/khronos/vulkan/vulkan_xlib_xrandr.h create mode 100644 ion/src/sdl/external/sdl/src/video/kmsdrm/SDL_kmsdrmdyn.c create mode 100644 ion/src/sdl/external/sdl/src/video/kmsdrm/SDL_kmsdrmdyn.h create mode 100644 ion/src/sdl/external/sdl/src/video/kmsdrm/SDL_kmsdrmevents.c create mode 100644 ion/src/sdl/external/sdl/src/video/kmsdrm/SDL_kmsdrmevents.h create mode 100644 ion/src/sdl/external/sdl/src/video/kmsdrm/SDL_kmsdrmmouse.c create mode 100644 ion/src/sdl/external/sdl/src/video/kmsdrm/SDL_kmsdrmmouse.h create mode 100644 ion/src/sdl/external/sdl/src/video/kmsdrm/SDL_kmsdrmopengles.c create mode 100644 ion/src/sdl/external/sdl/src/video/kmsdrm/SDL_kmsdrmopengles.h create mode 100644 ion/src/sdl/external/sdl/src/video/kmsdrm/SDL_kmsdrmsym.h create mode 100644 ion/src/sdl/external/sdl/src/video/kmsdrm/SDL_kmsdrmvideo.c create mode 100644 ion/src/sdl/external/sdl/src/video/kmsdrm/SDL_kmsdrmvideo.h create mode 100644 ion/src/sdl/external/sdl/src/video/nacl/SDL_naclevents.c create mode 100644 ion/src/sdl/external/sdl/src/video/nacl/SDL_naclevents_c.h create mode 100644 ion/src/sdl/external/sdl/src/video/nacl/SDL_naclglue.c create mode 100644 ion/src/sdl/external/sdl/src/video/nacl/SDL_naclopengles.c create mode 100644 ion/src/sdl/external/sdl/src/video/nacl/SDL_naclopengles.h create mode 100644 ion/src/sdl/external/sdl/src/video/nacl/SDL_naclvideo.c create mode 100644 ion/src/sdl/external/sdl/src/video/nacl/SDL_naclvideo.h create mode 100644 ion/src/sdl/external/sdl/src/video/nacl/SDL_naclwindow.c create mode 100644 ion/src/sdl/external/sdl/src/video/nacl/SDL_naclwindow.h create mode 100644 ion/src/sdl/external/sdl/src/video/pandora/SDL_pandora.c create mode 100644 ion/src/sdl/external/sdl/src/video/pandora/SDL_pandora.h create mode 100644 ion/src/sdl/external/sdl/src/video/pandora/SDL_pandora_events.c create mode 100644 ion/src/sdl/external/sdl/src/video/pandora/SDL_pandora_events.h create mode 100644 ion/src/sdl/external/sdl/src/video/psp/SDL_pspevents.c create mode 100644 ion/src/sdl/external/sdl/src/video/psp/SDL_pspevents_c.h create mode 100644 ion/src/sdl/external/sdl/src/video/psp/SDL_pspgl.c create mode 100644 ion/src/sdl/external/sdl/src/video/psp/SDL_pspgl_c.h create mode 100644 ion/src/sdl/external/sdl/src/video/psp/SDL_pspmouse.c create mode 100644 ion/src/sdl/external/sdl/src/video/psp/SDL_pspmouse_c.h create mode 100644 ion/src/sdl/external/sdl/src/video/psp/SDL_pspvideo.c create mode 100644 ion/src/sdl/external/sdl/src/video/psp/SDL_pspvideo.h create mode 100644 ion/src/sdl/external/sdl/src/video/qnx/gl.c create mode 100644 ion/src/sdl/external/sdl/src/video/qnx/keyboard.c create mode 100644 ion/src/sdl/external/sdl/src/video/qnx/sdl_qnx.h create mode 100644 ion/src/sdl/external/sdl/src/video/qnx/video.c create mode 100644 ion/src/sdl/external/sdl/src/video/raspberry/SDL_rpievents.c create mode 100644 ion/src/sdl/external/sdl/src/video/raspberry/SDL_rpievents_c.h create mode 100644 ion/src/sdl/external/sdl/src/video/raspberry/SDL_rpimouse.c create mode 100644 ion/src/sdl/external/sdl/src/video/raspberry/SDL_rpimouse.h create mode 100644 ion/src/sdl/external/sdl/src/video/raspberry/SDL_rpiopengles.c create mode 100644 ion/src/sdl/external/sdl/src/video/raspberry/SDL_rpiopengles.h create mode 100644 ion/src/sdl/external/sdl/src/video/raspberry/SDL_rpivideo.c create mode 100644 ion/src/sdl/external/sdl/src/video/raspberry/SDL_rpivideo.h create mode 100755 ion/src/sdl/external/sdl/src/video/sdlgenblit.pl create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitappdelegate.h create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitappdelegate.m create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitclipboard.h create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitclipboard.m create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitevents.h create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitevents.m create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitmessagebox.h create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitmessagebox.m create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitmetalview.h create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitmetalview.m create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitmodes.h create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitmodes.m create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitopengles.h create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitopengles.m create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitopenglview.h create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitopenglview.m create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitvideo.h create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitvideo.m create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitview.h create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitview.m create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitviewcontroller.h create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitviewcontroller.m create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitvulkan.h create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitvulkan.m create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitwindow.h create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/SDL_uikitwindow.m create mode 100644 ion/src/sdl/external/sdl/src/video/uikit/keyinfotable.h create mode 100644 ion/src/sdl/external/sdl/src/video/vivante/SDL_vivanteopengles.c create mode 100644 ion/src/sdl/external/sdl/src/video/vivante/SDL_vivanteopengles.h create mode 100644 ion/src/sdl/external/sdl/src/video/vivante/SDL_vivanteplatform.c create mode 100644 ion/src/sdl/external/sdl/src/video/vivante/SDL_vivanteplatform.h create mode 100644 ion/src/sdl/external/sdl/src/video/vivante/SDL_vivantevideo.c create mode 100644 ion/src/sdl/external/sdl/src/video/vivante/SDL_vivantevideo.h create mode 100644 ion/src/sdl/external/sdl/src/video/wayland/SDL_waylandclipboard.c create mode 100644 ion/src/sdl/external/sdl/src/video/wayland/SDL_waylandclipboard.h create mode 100644 ion/src/sdl/external/sdl/src/video/wayland/SDL_waylanddatamanager.c create mode 100644 ion/src/sdl/external/sdl/src/video/wayland/SDL_waylanddatamanager.h create mode 100644 ion/src/sdl/external/sdl/src/video/wayland/SDL_waylanddyn.c create mode 100644 ion/src/sdl/external/sdl/src/video/wayland/SDL_waylanddyn.h create mode 100644 ion/src/sdl/external/sdl/src/video/wayland/SDL_waylandevents.c create mode 100644 ion/src/sdl/external/sdl/src/video/wayland/SDL_waylandevents_c.h create mode 100644 ion/src/sdl/external/sdl/src/video/wayland/SDL_waylandmouse.c create mode 100644 ion/src/sdl/external/sdl/src/video/wayland/SDL_waylandmouse.h create mode 100644 ion/src/sdl/external/sdl/src/video/wayland/SDL_waylandopengles.c create mode 100644 ion/src/sdl/external/sdl/src/video/wayland/SDL_waylandopengles.h create mode 100644 ion/src/sdl/external/sdl/src/video/wayland/SDL_waylandsym.h create mode 100644 ion/src/sdl/external/sdl/src/video/wayland/SDL_waylandtouch.c create mode 100644 ion/src/sdl/external/sdl/src/video/wayland/SDL_waylandtouch.h create mode 100644 ion/src/sdl/external/sdl/src/video/wayland/SDL_waylandvideo.c create mode 100644 ion/src/sdl/external/sdl/src/video/wayland/SDL_waylandvideo.h create mode 100644 ion/src/sdl/external/sdl/src/video/wayland/SDL_waylandvulkan.c create mode 100644 ion/src/sdl/external/sdl/src/video/wayland/SDL_waylandvulkan.h create mode 100644 ion/src/sdl/external/sdl/src/video/wayland/SDL_waylandwindow.c create mode 100644 ion/src/sdl/external/sdl/src/video/wayland/SDL_waylandwindow.h create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_msctf.h create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_vkeys.h create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowsclipboard.c create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowsclipboard.h create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowsevents.c create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowsevents.h create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowsframebuffer.c create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowsframebuffer.h create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowskeyboard.c create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowskeyboard.h create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowsmessagebox.c create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowsmessagebox.h create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowsmodes.c create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowsmodes.h create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowsmouse.c create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowsmouse.h create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowsopengl.c create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowsopengl.h create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowsopengles.c create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowsopengles.h create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowsshape.c create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowsshape.h create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowstaskdialog.h create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowsvideo.c create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowsvideo.h create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowsvulkan.c create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowsvulkan.h create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowswindow.c create mode 100644 ion/src/sdl/external/sdl/src/video/windows/SDL_windowswindow.h create mode 100644 ion/src/sdl/external/sdl/src/video/windows/wmmsg.h create mode 100644 ion/src/sdl/external/sdl/src/video/winrt/SDL_winrtevents.cpp create mode 100644 ion/src/sdl/external/sdl/src/video/winrt/SDL_winrtevents_c.h create mode 100644 ion/src/sdl/external/sdl/src/video/winrt/SDL_winrtgamebar.cpp create mode 100644 ion/src/sdl/external/sdl/src/video/winrt/SDL_winrtgamebar_cpp.h create mode 100644 ion/src/sdl/external/sdl/src/video/winrt/SDL_winrtkeyboard.cpp create mode 100644 ion/src/sdl/external/sdl/src/video/winrt/SDL_winrtmessagebox.cpp create mode 100644 ion/src/sdl/external/sdl/src/video/winrt/SDL_winrtmessagebox.h create mode 100644 ion/src/sdl/external/sdl/src/video/winrt/SDL_winrtmouse.cpp create mode 100644 ion/src/sdl/external/sdl/src/video/winrt/SDL_winrtmouse_c.h create mode 100644 ion/src/sdl/external/sdl/src/video/winrt/SDL_winrtopengles.cpp create mode 100644 ion/src/sdl/external/sdl/src/video/winrt/SDL_winrtopengles.h create mode 100644 ion/src/sdl/external/sdl/src/video/winrt/SDL_winrtpointerinput.cpp create mode 100644 ion/src/sdl/external/sdl/src/video/winrt/SDL_winrtvideo.cpp create mode 100644 ion/src/sdl/external/sdl/src/video/winrt/SDL_winrtvideo_cpp.h create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11clipboard.c create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11clipboard.h create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11dyn.c create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11dyn.h create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11events.c create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11events.h create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11framebuffer.c create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11framebuffer.h create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11keyboard.c create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11keyboard.h create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11messagebox.c create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11messagebox.h create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11modes.c create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11modes.h create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11mouse.c create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11mouse.h create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11opengl.c create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11opengl.h create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11opengles.c create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11opengles.h create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11shape.c create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11shape.h create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11sym.h create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11touch.c create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11touch.h create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11video.c create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11video.h create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11vulkan.c create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11vulkan.h create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11window.c create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11window.h create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11xinput2.c create mode 100644 ion/src/sdl/external/sdl/src/video/x11/SDL_x11xinput2.h create mode 100644 ion/src/sdl/external/sdl/src/video/x11/edid-parse.c create mode 100644 ion/src/sdl/external/sdl/src/video/x11/edid.h create mode 100644 ion/src/sdl/external/sdl/src/video/x11/imKStoUCS.c create mode 100644 ion/src/sdl/external/sdl/src/video/x11/imKStoUCS.h create mode 100644 ion/src/sdl/external/sdl/src/video/yuv2rgb/LICENSE create mode 100644 ion/src/sdl/external/sdl/src/video/yuv2rgb/README.md create mode 100644 ion/src/sdl/external/sdl/src/video/yuv2rgb/yuv_rgb.c create mode 100644 ion/src/sdl/external/sdl/src/video/yuv2rgb/yuv_rgb.h create mode 100644 ion/src/sdl/external/sdl/src/video/yuv2rgb/yuv_rgb_sse_func.h create mode 100644 ion/src/sdl/external/sdl/src/video/yuv2rgb/yuv_rgb_std_func.h diff --git a/ion/src/sdl/android/src/java/org/libsdl/app/HIDDevice.java b/ion/src/sdl/android/src/java/org/libsdl/app/HIDDevice.java new file mode 100644 index 000000000..aa358d1fc --- /dev/null +++ b/ion/src/sdl/android/src/java/org/libsdl/app/HIDDevice.java @@ -0,0 +1,19 @@ +package org.libsdl.app; + +interface HIDDevice +{ + public int getId(); + public int getVendorId(); + public int getProductId(); + public String getSerialNumber(); + public int getVersion(); + public String getManufacturerName(); + public String getProductName(); + public boolean open(); + public int sendFeatureReport(byte[] report); + public int sendOutputReport(byte[] report); + public boolean getFeatureReport(byte[] report); + public void setFrozen(boolean frozen); + public void close(); + public void shutdown(); +} diff --git a/ion/src/sdl/android/src/java/org/libsdl/app/HIDDeviceBLESteamController.java b/ion/src/sdl/android/src/java/org/libsdl/app/HIDDeviceBLESteamController.java new file mode 100644 index 000000000..51538faed --- /dev/null +++ b/ion/src/sdl/android/src/java/org/libsdl/app/HIDDeviceBLESteamController.java @@ -0,0 +1,644 @@ +package org.libsdl.app; + +import android.content.Context; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCallback; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattDescriptor; +import android.bluetooth.BluetoothManager; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothGattService; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; +import android.os.*; + +//import com.android.internal.util.HexDump; + +import java.lang.Runnable; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.UUID; + +class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDevice { + + private static final String TAG = "hidapi"; + private HIDDeviceManager mManager; + private BluetoothDevice mDevice; + private int mDeviceId; + private BluetoothGatt mGatt; + private boolean mIsRegistered = false; + private boolean mIsConnected = false; + private boolean mIsChromebook = false; + private boolean mIsReconnecting = false; + private boolean mFrozen = false; + private LinkedList mOperations; + GattOperation mCurrentOperation = null; + private Handler mHandler; + + private static final int TRANSPORT_AUTO = 0; + private static final int TRANSPORT_BREDR = 1; + private static final int TRANSPORT_LE = 2; + + private static final int CHROMEBOOK_CONNECTION_CHECK_INTERVAL = 10000; + + static public final UUID steamControllerService = UUID.fromString("100F6C32-1735-4313-B402-38567131E5F3"); + static public final UUID inputCharacteristic = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3"); + static public final UUID reportCharacteristic = UUID.fromString("100F6C34-1735-4313-B402-38567131E5F3"); + static private final byte[] enterValveMode = new byte[] { (byte)0xC0, (byte)0x87, 0x03, 0x08, 0x07, 0x00 }; + + static class GattOperation { + private enum Operation { + CHR_READ, + CHR_WRITE, + ENABLE_NOTIFICATION + } + + Operation mOp; + UUID mUuid; + byte[] mValue; + BluetoothGatt mGatt; + boolean mResult = true; + + private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid) { + mGatt = gatt; + mOp = operation; + mUuid = uuid; + } + + private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value) { + mGatt = gatt; + mOp = operation; + mUuid = uuid; + mValue = value; + } + + public void run() { + // This is executed in main thread + BluetoothGattCharacteristic chr; + + switch (mOp) { + case CHR_READ: + chr = getCharacteristic(mUuid); + //Log.v(TAG, "Reading characteristic " + chr.getUuid()); + if (!mGatt.readCharacteristic(chr)) { + Log.e(TAG, "Unable to read characteristic " + mUuid.toString()); + mResult = false; + break; + } + mResult = true; + break; + case CHR_WRITE: + chr = getCharacteristic(mUuid); + //Log.v(TAG, "Writing characteristic " + chr.getUuid() + " value=" + HexDump.toHexString(value)); + chr.setValue(mValue); + if (!mGatt.writeCharacteristic(chr)) { + Log.e(TAG, "Unable to write characteristic " + mUuid.toString()); + mResult = false; + break; + } + mResult = true; + break; + case ENABLE_NOTIFICATION: + chr = getCharacteristic(mUuid); + //Log.v(TAG, "Writing descriptor of " + chr.getUuid()); + if (chr != null) { + BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")); + if (cccd != null) { + int properties = chr.getProperties(); + byte[] value; + if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == BluetoothGattCharacteristic.PROPERTY_NOTIFY) { + value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE; + } else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) == BluetoothGattCharacteristic.PROPERTY_INDICATE) { + value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE; + } else { + Log.e(TAG, "Unable to start notifications on input characteristic"); + mResult = false; + return; + } + + mGatt.setCharacteristicNotification(chr, true); + cccd.setValue(value); + if (!mGatt.writeDescriptor(cccd)) { + Log.e(TAG, "Unable to write descriptor " + mUuid.toString()); + mResult = false; + return; + } + mResult = true; + } + } + } + } + + public boolean finish() { + return mResult; + } + + private BluetoothGattCharacteristic getCharacteristic(UUID uuid) { + BluetoothGattService valveService = mGatt.getService(steamControllerService); + if (valveService == null) + return null; + return valveService.getCharacteristic(uuid); + } + + static public GattOperation readCharacteristic(BluetoothGatt gatt, UUID uuid) { + return new GattOperation(gatt, Operation.CHR_READ, uuid); + } + + static public GattOperation writeCharacteristic(BluetoothGatt gatt, UUID uuid, byte[] value) { + return new GattOperation(gatt, Operation.CHR_WRITE, uuid, value); + } + + static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid) { + return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid); + } + } + + public HIDDeviceBLESteamController(HIDDeviceManager manager, BluetoothDevice device) { + mManager = manager; + mDevice = device; + mDeviceId = mManager.getDeviceIDForIdentifier(getIdentifier()); + mIsRegistered = false; + mIsChromebook = mManager.getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management"); + mOperations = new LinkedList(); + mHandler = new Handler(Looper.getMainLooper()); + + mGatt = connectGatt(); + final HIDDeviceBLESteamController finalThis = this; + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + finalThis.checkConnectionForChromebookIssue(); + } + }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL); + } + + public String getIdentifier() { + return String.format("SteamController.%s", mDevice.getAddress()); + } + + public BluetoothGatt getGatt() { + return mGatt; + } + + // Because on Chromebooks we show up as a dual-mode device, it will attempt to connect TRANSPORT_AUTO, which will use TRANSPORT_BREDR instead + // of TRANSPORT_LE. Let's force ourselves to connect low energy. + private BluetoothGatt connectGatt(boolean managed) { + if (Build.VERSION.SDK_INT >= 23) { + try { + return mDevice.connectGatt(mManager.getContext(), managed, this, TRANSPORT_LE); + } catch (Exception e) { + return mDevice.connectGatt(mManager.getContext(), managed, this); + } + } else { + return mDevice.connectGatt(mManager.getContext(), managed, this); + } + } + + private BluetoothGatt connectGatt() { + return connectGatt(false); + } + + protected int getConnectionState() { + + Context context = mManager.getContext(); + if (context == null) { + // We are lacking any context to get our Bluetooth information. We'll just assume disconnected. + return BluetoothProfile.STATE_DISCONNECTED; + } + + BluetoothManager btManager = (BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE); + if (btManager == null) { + // This device doesn't support Bluetooth. We should never be here, because how did + // we instantiate a device to start with? + return BluetoothProfile.STATE_DISCONNECTED; + } + + return btManager.getConnectionState(mDevice, BluetoothProfile.GATT); + } + + public void reconnect() { + + if (getConnectionState() != BluetoothProfile.STATE_CONNECTED) { + mGatt.disconnect(); + mGatt = connectGatt(); + } + + } + + protected void checkConnectionForChromebookIssue() { + if (!mIsChromebook) { + // We only do this on Chromebooks, because otherwise it's really annoying to just attempt + // over and over. + return; + } + + int connectionState = getConnectionState(); + + switch (connectionState) { + case BluetoothProfile.STATE_CONNECTED: + if (!mIsConnected) { + // We are in the Bad Chromebook Place. We can force a disconnect + // to try to recover. + Log.v(TAG, "Chromebook: We are in a very bad state; the controller shows as connected in the underlying Bluetooth layer, but we never received a callback. Forcing a reconnect."); + mIsReconnecting = true; + mGatt.disconnect(); + mGatt = connectGatt(false); + break; + } + else if (!isRegistered()) { + if (mGatt.getServices().size() > 0) { + Log.v(TAG, "Chromebook: We are connected to a controller, but never got our registration. Trying to recover."); + probeService(this); + } + else { + Log.v(TAG, "Chromebook: We are connected to a controller, but never discovered services. Trying to recover."); + mIsReconnecting = true; + mGatt.disconnect(); + mGatt = connectGatt(false); + break; + } + } + else { + Log.v(TAG, "Chromebook: We are connected, and registered. Everything's good!"); + return; + } + break; + + case BluetoothProfile.STATE_DISCONNECTED: + Log.v(TAG, "Chromebook: We have either been disconnected, or the Chromebook BtGatt.ContextMap bug has bitten us. Attempting a disconnect/reconnect, but we may not be able to recover."); + + mIsReconnecting = true; + mGatt.disconnect(); + mGatt = connectGatt(false); + break; + + case BluetoothProfile.STATE_CONNECTING: + Log.v(TAG, "Chromebook: We're still trying to connect. Waiting a bit longer."); + break; + } + + final HIDDeviceBLESteamController finalThis = this; + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + finalThis.checkConnectionForChromebookIssue(); + } + }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL); + } + + private boolean isRegistered() { + return mIsRegistered; + } + + private void setRegistered() { + mIsRegistered = true; + } + + private boolean probeService(HIDDeviceBLESteamController controller) { + + if (isRegistered()) { + return true; + } + + if (!mIsConnected) { + return false; + } + + Log.v(TAG, "probeService controller=" + controller); + + for (BluetoothGattService service : mGatt.getServices()) { + if (service.getUuid().equals(steamControllerService)) { + Log.v(TAG, "Found Valve steam controller service " + service.getUuid()); + + for (BluetoothGattCharacteristic chr : service.getCharacteristics()) { + if (chr.getUuid().equals(inputCharacteristic)) { + Log.v(TAG, "Found input characteristic"); + // Start notifications + BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")); + if (cccd != null) { + enableNotification(chr.getUuid()); + } + } + } + return true; + } + } + + if ((mGatt.getServices().size() == 0) && mIsChromebook && !mIsReconnecting) { + Log.e(TAG, "Chromebook: Discovered services were empty; this almost certainly means the BtGatt.ContextMap bug has bitten us."); + mIsConnected = false; + mIsReconnecting = true; + mGatt.disconnect(); + mGatt = connectGatt(false); + } + + return false; + } + + ////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////// + + private void finishCurrentGattOperation() { + GattOperation op = null; + synchronized (mOperations) { + if (mCurrentOperation != null) { + op = mCurrentOperation; + mCurrentOperation = null; + } + } + if (op != null) { + boolean result = op.finish(); // TODO: Maybe in main thread as well? + + // Our operation failed, let's add it back to the beginning of our queue. + if (!result) { + mOperations.addFirst(op); + } + } + executeNextGattOperation(); + } + + private void executeNextGattOperation() { + synchronized (mOperations) { + if (mCurrentOperation != null) + return; + + if (mOperations.isEmpty()) + return; + + mCurrentOperation = mOperations.removeFirst(); + } + + // Run in main thread + mHandler.post(new Runnable() { + @Override + public void run() { + synchronized (mOperations) { + if (mCurrentOperation == null) { + Log.e(TAG, "Current operation null in executor?"); + return; + } + + mCurrentOperation.run(); + // now wait for the GATT callback and when it comes, finish this operation + } + } + }); + } + + private void queueGattOperation(GattOperation op) { + synchronized (mOperations) { + mOperations.add(op); + } + executeNextGattOperation(); + } + + private void enableNotification(UUID chrUuid) { + GattOperation op = HIDDeviceBLESteamController.GattOperation.enableNotification(mGatt, chrUuid); + queueGattOperation(op); + } + + public void writeCharacteristic(UUID uuid, byte[] value) { + GattOperation op = HIDDeviceBLESteamController.GattOperation.writeCharacteristic(mGatt, uuid, value); + queueGattOperation(op); + } + + public void readCharacteristic(UUID uuid) { + GattOperation op = HIDDeviceBLESteamController.GattOperation.readCharacteristic(mGatt, uuid); + queueGattOperation(op); + } + + ////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////// BluetoothGattCallback overridden methods + ////////////////////////////////////////////////////////////////////////////////////////////////////// + + public void onConnectionStateChange(BluetoothGatt g, int status, int newState) { + //Log.v(TAG, "onConnectionStateChange status=" + status + " newState=" + newState); + mIsReconnecting = false; + if (newState == 2) { + mIsConnected = true; + // Run directly, without GattOperation + if (!isRegistered()) { + mHandler.post(new Runnable() { + @Override + public void run() { + mGatt.discoverServices(); + } + }); + } + } + else if (newState == 0) { + mIsConnected = false; + } + + // Disconnection is handled in SteamLink using the ACTION_ACL_DISCONNECTED Intent. + } + + public void onServicesDiscovered(BluetoothGatt gatt, int status) { + //Log.v(TAG, "onServicesDiscovered status=" + status); + if (status == 0) { + if (gatt.getServices().size() == 0) { + Log.v(TAG, "onServicesDiscovered returned zero services; something has gone horribly wrong down in Android's Bluetooth stack."); + mIsReconnecting = true; + mIsConnected = false; + gatt.disconnect(); + mGatt = connectGatt(false); + } + else { + probeService(this); + } + } + } + + public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + //Log.v(TAG, "onCharacteristicRead status=" + status + " uuid=" + characteristic.getUuid()); + + if (characteristic.getUuid().equals(reportCharacteristic) && !mFrozen) { + mManager.HIDDeviceFeatureReport(getId(), characteristic.getValue()); + } + + finishCurrentGattOperation(); + } + + public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + //Log.v(TAG, "onCharacteristicWrite status=" + status + " uuid=" + characteristic.getUuid()); + + if (characteristic.getUuid().equals(reportCharacteristic)) { + // Only register controller with the native side once it has been fully configured + if (!isRegistered()) { + Log.v(TAG, "Registering Steam Controller with ID: " + getId()); + mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0); + setRegistered(); + } + } + + finishCurrentGattOperation(); + } + + public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + // Enable this for verbose logging of controller input reports + //Log.v(TAG, "onCharacteristicChanged uuid=" + characteristic.getUuid() + " data=" + HexDump.dumpHexString(characteristic.getValue())); + + if (characteristic.getUuid().equals(inputCharacteristic) && !mFrozen) { + mManager.HIDDeviceInputReport(getId(), characteristic.getValue()); + } + } + + public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { + //Log.v(TAG, "onDescriptorRead status=" + status); + } + + public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { + BluetoothGattCharacteristic chr = descriptor.getCharacteristic(); + //Log.v(TAG, "onDescriptorWrite status=" + status + " uuid=" + chr.getUuid() + " descriptor=" + descriptor.getUuid()); + + if (chr.getUuid().equals(inputCharacteristic)) { + boolean hasWrittenInputDescriptor = true; + BluetoothGattCharacteristic reportChr = chr.getService().getCharacteristic(reportCharacteristic); + if (reportChr != null) { + Log.v(TAG, "Writing report characteristic to enter valve mode"); + reportChr.setValue(enterValveMode); + gatt.writeCharacteristic(reportChr); + } + } + + finishCurrentGattOperation(); + } + + public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { + //Log.v(TAG, "onReliableWriteCompleted status=" + status); + } + + public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { + //Log.v(TAG, "onReadRemoteRssi status=" + status); + } + + public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { + //Log.v(TAG, "onMtuChanged status=" + status); + } + + ////////////////////////////////////////////////////////////////////////////////////////////////////// + //////// Public API + ////////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public int getId() { + return mDeviceId; + } + + @Override + public int getVendorId() { + // Valve Corporation + final int VALVE_USB_VID = 0x28DE; + return VALVE_USB_VID; + } + + @Override + public int getProductId() { + // We don't have an easy way to query from the Bluetooth device, but we know what it is + final int D0G_BLE2_PID = 0x1106; + return D0G_BLE2_PID; + } + + @Override + public String getSerialNumber() { + // This will be read later via feature report by Steam + return "12345"; + } + + @Override + public int getVersion() { + return 0; + } + + @Override + public String getManufacturerName() { + return "Valve Corporation"; + } + + @Override + public String getProductName() { + return "Steam Controller"; + } + + @Override + public boolean open() { + return true; + } + + @Override + public int sendFeatureReport(byte[] report) { + if (!isRegistered()) { + Log.e(TAG, "Attempted sendFeatureReport before Steam Controller is registered!"); + if (mIsConnected) { + probeService(this); + } + return -1; + } + + // We need to skip the first byte, as that doesn't go over the air + byte[] actual_report = Arrays.copyOfRange(report, 1, report.length - 1); + //Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(actual_report)); + writeCharacteristic(reportCharacteristic, actual_report); + return report.length; + } + + @Override + public int sendOutputReport(byte[] report) { + if (!isRegistered()) { + Log.e(TAG, "Attempted sendOutputReport before Steam Controller is registered!"); + if (mIsConnected) { + probeService(this); + } + return -1; + } + + //Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(report)); + writeCharacteristic(reportCharacteristic, report); + return report.length; + } + + @Override + public boolean getFeatureReport(byte[] report) { + if (!isRegistered()) { + Log.e(TAG, "Attempted getFeatureReport before Steam Controller is registered!"); + if (mIsConnected) { + probeService(this); + } + return false; + } + + //Log.v(TAG, "getFeatureReport"); + readCharacteristic(reportCharacteristic); + return true; + } + + @Override + public void close() { + } + + @Override + public void setFrozen(boolean frozen) { + mFrozen = frozen; + } + + @Override + public void shutdown() { + close(); + + BluetoothGatt g = mGatt; + if (g != null) { + g.disconnect(); + g.close(); + mGatt = null; + } + mManager = null; + mIsRegistered = false; + mIsConnected = false; + mOperations.clear(); + } + +} + diff --git a/ion/src/sdl/android/src/java/org/libsdl/app/HIDDeviceManager.java b/ion/src/sdl/android/src/java/org/libsdl/app/HIDDeviceManager.java new file mode 100644 index 000000000..463f3d4ac --- /dev/null +++ b/ion/src/sdl/android/src/java/org/libsdl/app/HIDDeviceManager.java @@ -0,0 +1,682 @@ +package org.libsdl.app; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.PendingIntent; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothManager; +import android.bluetooth.BluetoothProfile; +import android.util.Log; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.hardware.usb.*; +import android.os.Handler; +import android.os.Looper; + +import java.util.HashMap; +import java.util.ArrayList; +import java.util.List; + +public class HIDDeviceManager { + private static final String TAG = "hidapi"; + private static final String ACTION_USB_PERMISSION = "org.libsdl.app.USB_PERMISSION"; + + private static HIDDeviceManager sManager; + private static int sManagerRefCount = 0; + + public static HIDDeviceManager acquire(Context context) { + if (sManagerRefCount == 0) { + sManager = new HIDDeviceManager(context); + } + ++sManagerRefCount; + return sManager; + } + + public static void release(HIDDeviceManager manager) { + if (manager == sManager) { + --sManagerRefCount; + if (sManagerRefCount == 0) { + sManager.close(); + sManager = null; + } + } + } + + private Context mContext; + private HashMap mDevicesById = new HashMap(); + private HashMap mUSBDevices = new HashMap(); + private HashMap mBluetoothDevices = new HashMap(); + private int mNextDeviceId = 0; + private SharedPreferences mSharedPreferences = null; + private boolean mIsChromebook = false; + private UsbManager mUsbManager; + private Handler mHandler; + private BluetoothManager mBluetoothManager; + private List mLastBluetoothDevices; + + private final BroadcastReceiver mUsbBroadcast = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) { + UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + handleUsbDeviceAttached(usbDevice); + } else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) { + UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + handleUsbDeviceDetached(usbDevice); + } else if (action.equals(HIDDeviceManager.ACTION_USB_PERMISSION)) { + UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + handleUsbDevicePermission(usbDevice, intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)); + } + } + }; + + private final BroadcastReceiver mBluetoothBroadcast = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + // Bluetooth device was connected. If it was a Steam Controller, handle it + if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) { + BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + Log.d(TAG, "Bluetooth device connected: " + device); + + if (isSteamController(device)) { + connectBluetoothDevice(device); + } + } + + // Bluetooth device was disconnected, remove from controller manager (if any) + if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) { + BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + Log.d(TAG, "Bluetooth device disconnected: " + device); + + disconnectBluetoothDevice(device); + } + } + }; + + private HIDDeviceManager(final Context context) { + mContext = context; + + // Make sure we have the HIDAPI library loaded with the native functions + try { + // SDL.loadLibrary("epsilon"); // TODO: we're not building two shared objects but just one + } catch (Throwable e) { + Log.w(TAG, "Couldn't load hidapi: " + e.toString()); + + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setCancelable(false); + builder.setTitle("SDL HIDAPI Error"); + builder.setMessage("Please report the following error to the SDL maintainers: " + e.getMessage()); + builder.setNegativeButton("Quit", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + try { + // If our context is an activity, exit rather than crashing when we can't + // call our native functions. + Activity activity = (Activity)context; + + activity.finish(); + } + catch (ClassCastException cce) { + // Context wasn't an activity, there's nothing we can do. Give up and return. + } + } + }); + builder.show(); + + return; + } + + HIDDeviceRegisterCallback(); + + mSharedPreferences = mContext.getSharedPreferences("hidapi", Context.MODE_PRIVATE); + mIsChromebook = mContext.getPackageManager().hasSystemFeature("org.chromium.arc.device_management"); + +// if (shouldClear) { +// SharedPreferences.Editor spedit = mSharedPreferences.edit(); +// spedit.clear(); +// spedit.commit(); +// } +// else + { + mNextDeviceId = mSharedPreferences.getInt("next_device_id", 0); + } + + initializeUSB(); + initializeBluetooth(); + } + + public Context getContext() { + return mContext; + } + + public int getDeviceIDForIdentifier(String identifier) { + SharedPreferences.Editor spedit = mSharedPreferences.edit(); + + int result = mSharedPreferences.getInt(identifier, 0); + if (result == 0) { + result = mNextDeviceId++; + spedit.putInt("next_device_id", mNextDeviceId); + } + + spedit.putInt(identifier, result); + spedit.commit(); + return result; + } + + private void initializeUSB() { + mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE); + + /* + // Logging + for (UsbDevice device : mUsbManager.getDeviceList().values()) { + Log.i(TAG,"Path: " + device.getDeviceName()); + Log.i(TAG,"Manufacturer: " + device.getManufacturerName()); + Log.i(TAG,"Product: " + device.getProductName()); + Log.i(TAG,"ID: " + device.getDeviceId()); + Log.i(TAG,"Class: " + device.getDeviceClass()); + Log.i(TAG,"Protocol: " + device.getDeviceProtocol()); + Log.i(TAG,"Vendor ID " + device.getVendorId()); + Log.i(TAG,"Product ID: " + device.getProductId()); + Log.i(TAG,"Interface count: " + device.getInterfaceCount()); + Log.i(TAG,"---------------------------------------"); + + // Get interface details + for (int index = 0; index < device.getInterfaceCount(); index++) { + UsbInterface mUsbInterface = device.getInterface(index); + Log.i(TAG," ***** *****"); + Log.i(TAG," Interface index: " + index); + Log.i(TAG," Interface ID: " + mUsbInterface.getId()); + Log.i(TAG," Interface class: " + mUsbInterface.getInterfaceClass()); + Log.i(TAG," Interface subclass: " + mUsbInterface.getInterfaceSubclass()); + Log.i(TAG," Interface protocol: " + mUsbInterface.getInterfaceProtocol()); + Log.i(TAG," Endpoint count: " + mUsbInterface.getEndpointCount()); + + // Get endpoint details + for (int epi = 0; epi < mUsbInterface.getEndpointCount(); epi++) + { + UsbEndpoint mEndpoint = mUsbInterface.getEndpoint(epi); + Log.i(TAG," ++++ ++++ ++++"); + Log.i(TAG," Endpoint index: " + epi); + Log.i(TAG," Attributes: " + mEndpoint.getAttributes()); + Log.i(TAG," Direction: " + mEndpoint.getDirection()); + Log.i(TAG," Number: " + mEndpoint.getEndpointNumber()); + Log.i(TAG," Interval: " + mEndpoint.getInterval()); + Log.i(TAG," Packet size: " + mEndpoint.getMaxPacketSize()); + Log.i(TAG," Type: " + mEndpoint.getType()); + } + } + } + Log.i(TAG," No more devices connected."); + */ + + // Register for USB broadcasts and permission completions + IntentFilter filter = new IntentFilter(); + filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); + filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); + filter.addAction(HIDDeviceManager.ACTION_USB_PERMISSION); + mContext.registerReceiver(mUsbBroadcast, filter); + + for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) { + handleUsbDeviceAttached(usbDevice); + } + } + + UsbManager getUSBManager() { + return mUsbManager; + } + + private void shutdownUSB() { + try { + mContext.unregisterReceiver(mUsbBroadcast); + } catch (Exception e) { + // We may not have registered, that's okay + } + } + + private boolean isHIDDeviceUSB(UsbDevice usbDevice) { + for (int interface_number = 0; interface_number < usbDevice.getInterfaceCount(); ++interface_number) { + if (isHIDDeviceInterface(usbDevice, interface_number)) { + return true; + } + } + return false; + } + + private boolean isHIDDeviceInterface(UsbDevice usbDevice, int interface_number) { + UsbInterface usbInterface = usbDevice.getInterface(interface_number); + if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) { + return true; + } + if (interface_number == 0) { + if (isXbox360Controller(usbDevice, usbInterface) || isXboxOneController(usbDevice, usbInterface)) { + return true; + } + } + return false; + } + + private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterface) { + final int XB360_IFACE_SUBCLASS = 93; + final int XB360_IFACE_PROTOCOL = 1; // Wired only + final int[] SUPPORTED_VENDORS = { + 0x0079, // GPD Win 2 + 0x044f, // Thrustmaster + 0x045e, // Microsoft + 0x046d, // Logitech + 0x056e, // Elecom + 0x06a3, // Saitek + 0x0738, // Mad Catz + 0x07ff, // Mad Catz + 0x0e6f, // Unknown + 0x0f0d, // Hori + 0x11c9, // Nacon + 0x12ab, // Unknown + 0x1430, // RedOctane + 0x146b, // BigBen + 0x1532, // Razer Sabertooth + 0x15e4, // Numark + 0x162e, // Joytech + 0x1689, // Razer Onza + 0x1bad, // Harmonix + 0x24c6, // PowerA + }; + + if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC && + usbInterface.getInterfaceSubclass() == XB360_IFACE_SUBCLASS && + usbInterface.getInterfaceProtocol() == XB360_IFACE_PROTOCOL) { + int vendor_id = usbDevice.getVendorId(); + for (int supportedVid : SUPPORTED_VENDORS) { + if (vendor_id == supportedVid) { + return true; + } + } + } + return false; + } + + private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterface) { + final int XB1_IFACE_SUBCLASS = 71; + final int XB1_IFACE_PROTOCOL = 208; + final int[] SUPPORTED_VENDORS = { + 0x045e, // Microsoft + 0x0738, // Mad Catz + 0x0e6f, // Unknown + 0x0f0d, // Hori + 0x1532, // Razer Wildcat + 0x24c6, // PowerA + }; + + if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC && + usbInterface.getInterfaceSubclass() == XB1_IFACE_SUBCLASS && + usbInterface.getInterfaceProtocol() == XB1_IFACE_PROTOCOL) { + int vendor_id = usbDevice.getVendorId(); + for (int supportedVid : SUPPORTED_VENDORS) { + if (vendor_id == supportedVid) { + return true; + } + } + } + return false; + } + + private void handleUsbDeviceAttached(UsbDevice usbDevice) { + if (isHIDDeviceUSB(usbDevice)) { + connectHIDDeviceUSB(usbDevice); + } + } + + private void handleUsbDeviceDetached(UsbDevice usbDevice) { + HIDDeviceUSB device = mUSBDevices.get(usbDevice); + if (device == null) + return; + + int id = device.getId(); + mUSBDevices.remove(usbDevice); + mDevicesById.remove(id); + device.shutdown(); + HIDDeviceDisconnected(id); + } + + private void handleUsbDevicePermission(UsbDevice usbDevice, boolean permission_granted) { + HIDDeviceUSB device = mUSBDevices.get(usbDevice); + if (device == null) + return; + + boolean opened = false; + if (permission_granted) { + opened = device.open(); + } + HIDDeviceOpenResult(device.getId(), opened); + } + + private void connectHIDDeviceUSB(UsbDevice usbDevice) { + synchronized (this) { + for (int interface_number = 0; interface_number < usbDevice.getInterfaceCount(); interface_number++) { + if (isHIDDeviceInterface(usbDevice, interface_number)) { + HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_number); + int id = device.getId(); + mUSBDevices.put(usbDevice, device); + mDevicesById.put(id, device); + HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), interface_number); + break; + } + } + } + } + + private void initializeBluetooth() { + Log.d(TAG, "Initializing Bluetooth"); + + if (mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) { + Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH"); + return; + } + + // Find bonded bluetooth controllers and create SteamControllers for them + mBluetoothManager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE); + if (mBluetoothManager == null) { + // This device doesn't support Bluetooth. + return; + } + + BluetoothAdapter btAdapter = mBluetoothManager.getAdapter(); + if (btAdapter == null) { + // This device has Bluetooth support in the codebase, but has no available adapters. + return; + } + + // Get our bonded devices. + for (BluetoothDevice device : btAdapter.getBondedDevices()) { + + Log.d(TAG, "Bluetooth device available: " + device); + if (isSteamController(device)) { + connectBluetoothDevice(device); + } + + } + + // NOTE: These don't work on Chromebooks, to my undying dismay. + IntentFilter filter = new IntentFilter(); + filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); + filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); + mContext.registerReceiver(mBluetoothBroadcast, filter); + + if (mIsChromebook) { + mHandler = new Handler(Looper.getMainLooper()); + mLastBluetoothDevices = new ArrayList<>(); + + // final HIDDeviceManager finalThis = this; + // mHandler.postDelayed(new Runnable() { + // @Override + // public void run() { + // finalThis.chromebookConnectionHandler(); + // } + // }, 5000); + } + } + + private void shutdownBluetooth() { + try { + mContext.unregisterReceiver(mBluetoothBroadcast); + } catch (Exception e) { + // We may not have registered, that's okay + } + } + + // Chromebooks do not pass along ACTION_ACL_CONNECTED / ACTION_ACL_DISCONNECTED properly. + // This function provides a sort of dummy version of that, watching for changes in the + // connected devices and attempting to add controllers as things change. + public void chromebookConnectionHandler() { + if (!mIsChromebook) { + return; + } + + ArrayList disconnected = new ArrayList<>(); + ArrayList connected = new ArrayList<>(); + + List currentConnected = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT); + + for (BluetoothDevice bluetoothDevice : currentConnected) { + if (!mLastBluetoothDevices.contains(bluetoothDevice)) { + connected.add(bluetoothDevice); + } + } + for (BluetoothDevice bluetoothDevice : mLastBluetoothDevices) { + if (!currentConnected.contains(bluetoothDevice)) { + disconnected.add(bluetoothDevice); + } + } + + mLastBluetoothDevices = currentConnected; + + for (BluetoothDevice bluetoothDevice : disconnected) { + disconnectBluetoothDevice(bluetoothDevice); + } + for (BluetoothDevice bluetoothDevice : connected) { + connectBluetoothDevice(bluetoothDevice); + } + + final HIDDeviceManager finalThis = this; + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + finalThis.chromebookConnectionHandler(); + } + }, 10000); + } + + public boolean connectBluetoothDevice(BluetoothDevice bluetoothDevice) { + Log.v(TAG, "connectBluetoothDevice device=" + bluetoothDevice); + synchronized (this) { + if (mBluetoothDevices.containsKey(bluetoothDevice)) { + Log.v(TAG, "Steam controller with address " + bluetoothDevice + " already exists, attempting reconnect"); + + HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice); + device.reconnect(); + + return false; + } + HIDDeviceBLESteamController device = new HIDDeviceBLESteamController(this, bluetoothDevice); + int id = device.getId(); + mBluetoothDevices.put(bluetoothDevice, device); + mDevicesById.put(id, device); + + // The Steam Controller will mark itself connected once initialization is complete + } + return true; + } + + public void disconnectBluetoothDevice(BluetoothDevice bluetoothDevice) { + synchronized (this) { + HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice); + if (device == null) + return; + + int id = device.getId(); + mBluetoothDevices.remove(bluetoothDevice); + mDevicesById.remove(id); + device.shutdown(); + HIDDeviceDisconnected(id); + } + } + + public boolean isSteamController(BluetoothDevice bluetoothDevice) { + // Sanity check. If you pass in a null device, by definition it is never a Steam Controller. + if (bluetoothDevice == null) { + return false; + } + + // If the device has no local name, we really don't want to try an equality check against it. + if (bluetoothDevice.getName() == null) { + return false; + } + + return bluetoothDevice.getName().equals("SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0); + } + + private void close() { + shutdownUSB(); + shutdownBluetooth(); + synchronized (this) { + for (HIDDevice device : mDevicesById.values()) { + device.shutdown(); + } + mDevicesById.clear(); + mBluetoothDevices.clear(); + HIDDeviceReleaseCallback(); + } + } + + public void setFrozen(boolean frozen) { + synchronized (this) { + for (HIDDevice device : mDevicesById.values()) { + device.setFrozen(frozen); + } + } + } + + ////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////// + + private HIDDevice getDevice(int id) { + synchronized (this) { + HIDDevice result = mDevicesById.get(id); + if (result == null) { + Log.v(TAG, "No device for id: " + id); + Log.v(TAG, "Available devices: " + mDevicesById.keySet()); + } + return result; + } + } + + ////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////// JNI interface functions + ////////////////////////////////////////////////////////////////////////////////////////////////////// + + public boolean openDevice(int deviceID) { + // Look to see if this is a USB device and we have permission to access it + for (HIDDeviceUSB device : mUSBDevices.values()) { + if (deviceID == device.getId()) { + UsbDevice usbDevice = device.getDevice(); + if (!mUsbManager.hasPermission(usbDevice)) { + HIDDeviceOpenPending(deviceID); + try { + mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, new Intent(HIDDeviceManager.ACTION_USB_PERMISSION), 0)); + } catch (Exception e) { + Log.v(TAG, "Couldn't request permission for USB device " + usbDevice); + HIDDeviceOpenResult(deviceID, false); + } + return false; + } + break; + } + } + + try { + Log.v(TAG, "openDevice deviceID=" + deviceID); + HIDDevice device; + device = getDevice(deviceID); + if (device == null) { + HIDDeviceDisconnected(deviceID); + return false; + } + + return device.open(); + } catch (Exception e) { + Log.e(TAG, "Got exception: " + Log.getStackTraceString(e)); + } + return false; + } + + public int sendOutputReport(int deviceID, byte[] report) { + try { + Log.v(TAG, "sendOutputReport deviceID=" + deviceID + " length=" + report.length); + HIDDevice device; + device = getDevice(deviceID); + if (device == null) { + HIDDeviceDisconnected(deviceID); + return -1; + } + + return device.sendOutputReport(report); + } catch (Exception e) { + Log.e(TAG, "Got exception: " + Log.getStackTraceString(e)); + } + return -1; + } + + public int sendFeatureReport(int deviceID, byte[] report) { + try { + Log.v(TAG, "sendFeatureReport deviceID=" + deviceID + " length=" + report.length); + HIDDevice device; + device = getDevice(deviceID); + if (device == null) { + HIDDeviceDisconnected(deviceID); + return -1; + } + + return device.sendFeatureReport(report); + } catch (Exception e) { + Log.e(TAG, "Got exception: " + Log.getStackTraceString(e)); + } + return -1; + } + + public boolean getFeatureReport(int deviceID, byte[] report) { + try { + Log.v(TAG, "getFeatureReport deviceID=" + deviceID); + HIDDevice device; + device = getDevice(deviceID); + if (device == null) { + HIDDeviceDisconnected(deviceID); + return false; + } + + return device.getFeatureReport(report); + } catch (Exception e) { + Log.e(TAG, "Got exception: " + Log.getStackTraceString(e)); + } + return false; + } + + public void closeDevice(int deviceID) { + try { + Log.v(TAG, "closeDevice deviceID=" + deviceID); + HIDDevice device; + device = getDevice(deviceID); + if (device == null) { + HIDDeviceDisconnected(deviceID); + return; + } + + device.close(); + } catch (Exception e) { + Log.e(TAG, "Got exception: " + Log.getStackTraceString(e)); + } + } + + + ////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////// Native methods + ////////////////////////////////////////////////////////////////////////////////////////////////////// + + private native void HIDDeviceRegisterCallback(); + private native void HIDDeviceReleaseCallback(); + + native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number); + native void HIDDeviceOpenPending(int deviceID); + native void HIDDeviceOpenResult(int deviceID, boolean opened); + native void HIDDeviceDisconnected(int deviceID); + + native void HIDDeviceInputReport(int deviceID, byte[] report); + native void HIDDeviceFeatureReport(int deviceID, byte[] report); +} diff --git a/ion/src/sdl/android/src/java/org/libsdl/app/HIDDeviceUSB.java b/ion/src/sdl/android/src/java/org/libsdl/app/HIDDeviceUSB.java new file mode 100644 index 000000000..c9fc58ece --- /dev/null +++ b/ion/src/sdl/android/src/java/org/libsdl/app/HIDDeviceUSB.java @@ -0,0 +1,307 @@ +package org.libsdl.app; + +import android.hardware.usb.*; +import android.os.Build; +import android.util.Log; +import java.util.Arrays; + +class HIDDeviceUSB implements HIDDevice { + + private static final String TAG = "hidapi"; + + protected HIDDeviceManager mManager; + protected UsbDevice mDevice; + protected int mInterface; + protected int mDeviceId; + protected UsbDeviceConnection mConnection; + protected UsbEndpoint mInputEndpoint; + protected UsbEndpoint mOutputEndpoint; + protected InputThread mInputThread; + protected boolean mRunning; + protected boolean mFrozen; + + public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_number) { + mManager = manager; + mDevice = usbDevice; + mInterface = interface_number; + mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier()); + mRunning = false; + } + + public String getIdentifier() { + return String.format("%s/%x/%x", mDevice.getDeviceName(), mDevice.getVendorId(), mDevice.getProductId()); + } + + @Override + public int getId() { + return mDeviceId; + } + + @Override + public int getVendorId() { + return mDevice.getVendorId(); + } + + @Override + public int getProductId() { + return mDevice.getProductId(); + } + + @Override + public String getSerialNumber() { + String result = null; + if (Build.VERSION.SDK_INT >= 21) { + result = mDevice.getSerialNumber(); + } + if (result == null) { + result = ""; + } + return result; + } + + @Override + public int getVersion() { + return 0; + } + + @Override + public String getManufacturerName() { + String result = null; + if (Build.VERSION.SDK_INT >= 21) { + result = mDevice.getManufacturerName(); + } + if (result == null) { + result = String.format("%x", getVendorId()); + } + return result; + } + + @Override + public String getProductName() { + String result = null; + if (Build.VERSION.SDK_INT >= 21) { + result = mDevice.getProductName(); + } + if (result == null) { + result = String.format("%x", getProductId()); + } + return result; + } + + public UsbDevice getDevice() { + return mDevice; + } + + public String getDeviceName() { + return getManufacturerName() + " " + getProductName() + "(0x" + String.format("%x", getVendorId()) + "/0x" + String.format("%x", getProductId()) + ")"; + } + + @Override + public boolean open() { + mConnection = mManager.getUSBManager().openDevice(mDevice); + if (mConnection == null) { + Log.w(TAG, "Unable to open USB device " + getDeviceName()); + return false; + } + + // Force claim all interfaces + for (int i = 0; i < mDevice.getInterfaceCount(); i++) { + UsbInterface iface = mDevice.getInterface(i); + + if (!mConnection.claimInterface(iface, true)) { + Log.w(TAG, "Failed to claim interfaces on USB device " + getDeviceName()); + close(); + return false; + } + } + + // Find the endpoints + UsbInterface iface = mDevice.getInterface(mInterface); + for (int j = 0; j < iface.getEndpointCount(); j++) { + UsbEndpoint endpt = iface.getEndpoint(j); + switch (endpt.getDirection()) { + case UsbConstants.USB_DIR_IN: + if (mInputEndpoint == null) { + mInputEndpoint = endpt; + } + break; + case UsbConstants.USB_DIR_OUT: + if (mOutputEndpoint == null) { + mOutputEndpoint = endpt; + } + break; + } + } + + // Make sure the required endpoints were present + if (mInputEndpoint == null || mOutputEndpoint == null) { + Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName()); + close(); + return false; + } + + // Start listening for input + mRunning = true; + mInputThread = new InputThread(); + mInputThread.start(); + + return true; + } + + @Override + public int sendFeatureReport(byte[] report) { + int res = -1; + int offset = 0; + int length = report.length; + boolean skipped_report_id = false; + byte report_number = report[0]; + + if (report_number == 0x0) { + ++offset; + --length; + skipped_report_id = true; + } + + res = mConnection.controlTransfer( + UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_OUT, + 0x09/*HID set_report*/, + (3/*HID feature*/ << 8) | report_number, + 0, + report, offset, length, + 1000/*timeout millis*/); + + if (res < 0) { + Log.w(TAG, "sendFeatureReport() returned " + res + " on device " + getDeviceName()); + return -1; + } + + if (skipped_report_id) { + ++length; + } + return length; + } + + @Override + public int sendOutputReport(byte[] report) { + int r = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000); + if (r != report.length) { + Log.w(TAG, "sendOutputReport() returned " + r + " on device " + getDeviceName()); + } + return r; + } + + @Override + public boolean getFeatureReport(byte[] report) { + int res = -1; + int offset = 0; + int length = report.length; + boolean skipped_report_id = false; + byte report_number = report[0]; + + if (report_number == 0x0) { + /* Offset the return buffer by 1, so that the report ID + will remain in byte 0. */ + ++offset; + --length; + skipped_report_id = true; + } + + res = mConnection.controlTransfer( + UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_IN, + 0x01/*HID get_report*/, + (3/*HID feature*/ << 8) | report_number, + 0, + report, offset, length, + 1000/*timeout millis*/); + + if (res < 0) { + Log.w(TAG, "getFeatureReport() returned " + res + " on device " + getDeviceName()); + return false; + } + + if (skipped_report_id) { + ++res; + ++length; + } + + byte[] data; + if (res == length) { + data = report; + } else { + data = Arrays.copyOfRange(report, 0, res); + } + mManager.HIDDeviceFeatureReport(mDeviceId, data); + + return true; + } + + @Override + public void close() { + mRunning = false; + if (mInputThread != null) { + while (mInputThread.isAlive()) { + mInputThread.interrupt(); + try { + mInputThread.join(); + } catch (InterruptedException e) { + // Keep trying until we're done + } + } + mInputThread = null; + } + if (mConnection != null) { + for (int i = 0; i < mDevice.getInterfaceCount(); i++) { + UsbInterface iface = mDevice.getInterface(i); + mConnection.releaseInterface(iface); + } + mConnection.close(); + mConnection = null; + } + } + + @Override + public void shutdown() { + close(); + mManager = null; + } + + @Override + public void setFrozen(boolean frozen) { + mFrozen = frozen; + } + + protected class InputThread extends Thread { + @Override + public void run() { + int packetSize = mInputEndpoint.getMaxPacketSize(); + byte[] packet = new byte[packetSize]; + while (mRunning) { + int r; + try + { + r = mConnection.bulkTransfer(mInputEndpoint, packet, packetSize, 1000); + } + catch (Exception e) + { + Log.v(TAG, "Exception in UsbDeviceConnection bulktransfer: " + e); + break; + } + if (r < 0) { + // Could be a timeout or an I/O error + } + if (r > 0) { + byte[] data; + if (r == packetSize) { + data = packet; + } else { + data = Arrays.copyOfRange(packet, 0, r); + } + + if (!mFrozen) { + mManager.HIDDeviceInputReport(mDeviceId, data); + } + } + } + } + } +} diff --git a/ion/src/sdl/android/src/java/org/libsdl/app/SDL.java b/ion/src/sdl/android/src/java/org/libsdl/app/SDL.java new file mode 100644 index 000000000..fb7f7319a --- /dev/null +++ b/ion/src/sdl/android/src/java/org/libsdl/app/SDL.java @@ -0,0 +1,84 @@ +package org.libsdl.app; + +import android.content.Context; + +import java.lang.reflect.*; + +/** + SDL library initialization +*/ +public class SDL { + + // This function should be called first and sets up the native code + // so it can call into the Java classes + public static void setupJNI() { + SDLActivity.nativeSetupJNI(); + SDLAudioManager.nativeSetupJNI(); + SDLControllerManager.nativeSetupJNI(); + } + + // This function should be called each time the activity is started + public static void initialize() { + setContext(null); + + SDLActivity.initialize(); + SDLAudioManager.initialize(); + SDLControllerManager.initialize(); + } + + // This function stores the current activity (SDL or not) + public static void setContext(Context context) { + mContext = context; + } + + public static Context getContext() { + return mContext; + } + + public static void loadLibrary(String libraryName) throws UnsatisfiedLinkError, SecurityException, NullPointerException { + + if (libraryName == null) { + throw new NullPointerException("No library name provided."); + } + + try { + // Let's see if we have ReLinker available in the project. This is necessary for + // some projects that have huge numbers of local libraries bundled, and thus may + // trip a bug in Android's native library loader which ReLinker works around. (If + // loadLibrary works properly, ReLinker will simply use the normal Android method + // internally.) + // + // To use ReLinker, just add it as a dependency. For more information, see + // https://github.com/KeepSafe/ReLinker for ReLinker's repository. + // + Class relinkClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker"); + Class relinkListenerClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker$LoadListener"); + Class contextClass = mContext.getClassLoader().loadClass("android.content.Context"); + Class stringClass = mContext.getClassLoader().loadClass("java.lang.String"); + + // Get a 'force' instance of the ReLinker, so we can ensure libraries are reinstalled if + // they've changed during updates. + Method forceMethod = relinkClass.getDeclaredMethod("force"); + Object relinkInstance = forceMethod.invoke(null); + Class relinkInstanceClass = relinkInstance.getClass(); + + // Actually load the library! + Method loadMethod = relinkInstanceClass.getDeclaredMethod("loadLibrary", contextClass, stringClass, stringClass, relinkListenerClass); + loadMethod.invoke(relinkInstance, mContext, libraryName, null, null); + } + catch (final Throwable e) { + // Fall back + try { + System.loadLibrary(libraryName); + } + catch (final UnsatisfiedLinkError ule) { + throw ule; + } + catch (final SecurityException se) { + throw se; + } + } + } + + protected static Context mContext; +} diff --git a/ion/src/sdl/android/src/java/org/libsdl/app/SDLActivity.java b/ion/src/sdl/android/src/java/org/libsdl/app/SDLActivity.java new file mode 100644 index 000000000..53d3ffee0 --- /dev/null +++ b/ion/src/sdl/android/src/java/org/libsdl/app/SDLActivity.java @@ -0,0 +1,2157 @@ +package org.libsdl.app; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Hashtable; +import java.lang.reflect.Method; +import java.lang.Math; + +import android.app.*; +import android.content.*; +import android.content.res.Configuration; +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.RelativeLayout; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.os.*; +import android.util.DisplayMetrics; +import android.util.Log; +import android.util.SparseArray; +import android.graphics.*; +import android.graphics.drawable.Drawable; +import android.hardware.*; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.ApplicationInfo; + +/** + SDL Activity +*/ +public class SDLActivity extends Activity implements View.OnSystemUiVisibilityChangeListener { + private static final String TAG = "SDL"; + + public static boolean mIsResumedCalled, mHasFocus; + + // Cursor types + private static final int SDL_SYSTEM_CURSOR_NONE = -1; + private static final int SDL_SYSTEM_CURSOR_ARROW = 0; + private static final int SDL_SYSTEM_CURSOR_IBEAM = 1; + private static final int SDL_SYSTEM_CURSOR_WAIT = 2; + private static final int SDL_SYSTEM_CURSOR_CROSSHAIR = 3; + private static final int SDL_SYSTEM_CURSOR_WAITARROW = 4; + private static final int SDL_SYSTEM_CURSOR_SIZENWSE = 5; + private static final int SDL_SYSTEM_CURSOR_SIZENESW = 6; + private static final int SDL_SYSTEM_CURSOR_SIZEWE = 7; + private static final int SDL_SYSTEM_CURSOR_SIZENS = 8; + private static final int SDL_SYSTEM_CURSOR_SIZEALL = 9; + private static final int SDL_SYSTEM_CURSOR_NO = 10; + private static final int SDL_SYSTEM_CURSOR_HAND = 11; + + protected static final int SDL_ORIENTATION_UNKNOWN = 0; + protected static final int SDL_ORIENTATION_LANDSCAPE = 1; + protected static final int SDL_ORIENTATION_LANDSCAPE_FLIPPED = 2; + protected static final int SDL_ORIENTATION_PORTRAIT = 3; + protected static final int SDL_ORIENTATION_PORTRAIT_FLIPPED = 4; + + protected static int mCurrentOrientation; + + // Handle the state of the native layer + public enum NativeState { + INIT, RESUMED, PAUSED + } + + public static NativeState mNextNativeState; + public static NativeState mCurrentNativeState; + + /** 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 boolean mScreenKeyboardShown; + protected static ViewGroup mLayout; + protected static SDLClipboardHandler mClipboardHandler; + protected static Hashtable mCursors; + protected static int mLastCursorID; + protected static SDLGenericMotionListener_API12 mMotionListener; + protected static HIDDeviceManager mHIDDeviceManager; + + // This is what SDL runs in. It invokes SDL_main(), eventually + protected static Thread mSDLThread; + + protected static SDLGenericMotionListener_API12 getMotionListener() { + if (mMotionListener == null) { + if (Build.VERSION.SDK_INT >= 26) { + mMotionListener = new SDLGenericMotionListener_API26(); + } else + if (Build.VERSION.SDK_INT >= 24) { + mMotionListener = new SDLGenericMotionListener_API24(); + } else { + mMotionListener = new SDLGenericMotionListener_API12(); + } + } + + return mMotionListener; + } + + /** + * This method returns the name of the shared object with the application entry point + * It can be overridden by derived classes. + */ + protected String getMainSharedObject() { + String library; + String[] libraries = SDLActivity.mSingleton.getLibraries(); + if (libraries.length > 0) { + library = "lib" + libraries[libraries.length - 1] + ".so"; + } else { + library = "libmain.so"; + } + return getContext().getApplicationInfo().nativeLibraryDir + "/" + library; + } + + /** + * This method returns the name of the application entry point + * It can be overridden by derived classes. + */ + protected String getMainFunction() { + return "SDL_main"; + } + + /** + * 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()) { + SDL.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; + mClipboardHandler = null; + mCursors = new Hashtable(); + mLastCursorID = 0; + mSDLThread = null; + mBrokenLibraries = false; + mIsResumedCalled = false; + mHasFocus = true; + mNextNativeState = NativeState.INIT; + mCurrentNativeState = NativeState.INIT; + } + + // Setup + @Override + protected void onCreate(Bundle savedInstanceState) { + Log.v(TAG, "Device: " + Build.DEVICE); + Log.v(TAG, "Model: " + Build.MODEL); + Log.v(TAG, "onCreate()"); + super.onCreate(savedInstanceState); + + try { + Thread.currentThread().setName("SDLActivity"); + } catch (Exception e) { + Log.v(TAG, "modify thread properties failed " + e.toString()); + } + + // 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) + { + mSingleton = this; + 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 JNI + SDL.setupJNI(); + + // Initialize state + SDL.initialize(); + + // So we can call stuff from static callbacks + mSingleton = this; + SDL.setContext(this); + + mClipboardHandler = new SDLClipboardHandler_API11(); + + mHIDDeviceManager = HIDDeviceManager.acquire(this); + + // Set up the surface + mSurface = new SDLSurface(getApplication()); + + mLayout = new RelativeLayout(this); + mLayout.addView(mSurface); + + // Get our current screen orientation and pass it down. + mCurrentOrientation = SDLActivity.getCurrentOrientation(); + // FIXME: with only one activity, SDL Thread is not yet started and this onNativeOrientationChanged() is ignored + SDLActivity.onNativeOrientationChanged(mCurrentOrientation); + + setContentView(mLayout); + + setWindowStyle(false); + + getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(this); + + // 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(); + mNextNativeState = NativeState.PAUSED; + mIsResumedCalled = false; + + if (SDLActivity.mBrokenLibraries) { + return; + } + + if (mHIDDeviceManager != null) { + mHIDDeviceManager.setFrozen(true); + } + + SDLActivity.handleNativeState(); + } + + @Override + protected void onResume() { + Log.v(TAG, "onResume()"); + super.onResume(); + mNextNativeState = NativeState.RESUMED; + mIsResumedCalled = true; + + if (SDLActivity.mBrokenLibraries) { + return; + } + + if (mHIDDeviceManager != null) { + mHIDDeviceManager.setFrozen(false); + } + + SDLActivity.handleNativeState(); + } + + public static int getCurrentOrientation() { + final Context context = SDLActivity.getContext(); + final Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); + + int result = SDL_ORIENTATION_UNKNOWN; + + switch (display.getRotation()) { + case Surface.ROTATION_0: + result = SDL_ORIENTATION_PORTRAIT; + break; + + case Surface.ROTATION_90: + result = SDL_ORIENTATION_LANDSCAPE; + break; + + case Surface.ROTATION_180: + result = SDL_ORIENTATION_PORTRAIT_FLIPPED; + break; + + case Surface.ROTATION_270: + result = SDL_ORIENTATION_LANDSCAPE_FLIPPED; + break; + } + + return result; + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + Log.v(TAG, "onWindowFocusChanged(): " + hasFocus); + + if (SDLActivity.mBrokenLibraries) { + return; + } + + SDLActivity.mHasFocus = hasFocus; + if (hasFocus) { + mNextNativeState = NativeState.RESUMED; + SDLActivity.getMotionListener().reclaimRelativeMouseModeIfNeeded(); + } else { + mNextNativeState = NativeState.PAUSED; + } + + SDLActivity.handleNativeState(); + } + + @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 (mHIDDeviceManager != null) { + HIDDeviceManager.release(mHIDDeviceManager); + mHIDDeviceManager = null; + } + + if (SDLActivity.mBrokenLibraries) { + super.onDestroy(); + return; + } + + if (SDLActivity.mSDLThread != null) { + + // Send Quit event to "SDLThread" thread + SDLActivity.nativeSendQuit(); + + // Wait for "SDLThread" thread to end + try { + SDLActivity.mSDLThread.join(); + } catch(Exception e) { + Log.v(TAG, "Problem stopping SDLThread: " + e); + } + } + + SDLActivity.nativeQuit(); + + super.onDestroy(); + } + + @Override + public void onBackPressed() { + // Check if we want to block the back button in case of mouse right click. + // + // If we do, the normal hardware back button will no longer work and people have to use home, + // but the mouse right click will work. + // + String trapBack = SDLActivity.nativeGetHint("SDL_ANDROID_TRAP_BACK_BUTTON"); + if ((trapBack != null) && trapBack.equals("1")) { + // Exit and let the mouse handler handle this button (if appropriate) + return; + } + + // Default system back button behavior. + super.onBackPressed(); + } + + // Called by JNI from SDL. + public static void manualBackButton() { + mSingleton.pressBackButton(); + } + + // Used to get us onto the activity's main thread + public void pressBackButton() { + runOnUiThread(new Runnable() { + @Override + public void run() { + SDLActivity.this.superOnBackPressed(); + } + }); + } + + // Used to access the system back behavior. + public void superOnBackPressed() { + super.onBackPressed(); + } + + @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 == KeyEvent.KEYCODE_ZOOM_IN || /* API 11 */ + keyCode == KeyEvent.KEYCODE_ZOOM_OUT /* API 11 */ + ) { + return false; + } + return super.dispatchKeyEvent(event); + } + + /* Transition to next state */ + public static void handleNativeState() { + + if (mNextNativeState == mCurrentNativeState) { + // Already in same state, discard. + return; + } + + // Try a transition to init state + if (mNextNativeState == NativeState.INIT) { + + mCurrentNativeState = mNextNativeState; + return; + } + + // Try a transition to paused state + if (mNextNativeState == NativeState.PAUSED) { + if (mSDLThread != null) { + nativePause(); + } + if (mSurface != null) { + mSurface.handlePause(); + } + mCurrentNativeState = mNextNativeState; + return; + } + + // Try a transition to resumed state + if (mNextNativeState == NativeState.RESUMED) { + if (mSurface.mIsSurfaceReady && mHasFocus && mIsResumedCalled) { + if (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 + // FIXME: Why aren't we enabling sensor input at start? + + mSDLThread = new Thread(new SDLMain(), "SDLThread"); + mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, true); + mSDLThread.start(); + + // No nativeResume(), don't signal Android_ResumeSem + mSurface.handleResume(); + } else { + nativeResume(); + mSurface.handleResume(); + } + + mCurrentNativeState = mNextNativeState; + } + } + } + + // Messages from the SDLMain thread + static final int COMMAND_CHANGE_TITLE = 1; + static final int COMMAND_CHANGE_WINDOW_STYLE = 2; + static final int COMMAND_TEXTEDIT_HIDE = 3; + static final int COMMAND_CHANGE_SURFACEVIEW_FORMAT = 4; + static final int COMMAND_SET_KEEP_SCREEN_ON = 5; + + protected static final int COMMAND_USER = 0x8000; + + protected static boolean mFullscreenModeActive; + + /** + * 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 = SDL.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_CHANGE_WINDOW_STYLE: + if (Build.VERSION.SDK_INT < 19) { + // This version of Android doesn't support the immersive fullscreen mode + break; + } + if (context instanceof Activity) { + Window window = ((Activity) context).getWindow(); + if (window != null) { + if ((msg.obj instanceof Integer) && (((Integer) msg.obj).intValue() != 0)) { + int flags = View.SYSTEM_UI_FLAG_FULLSCREEN | + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | + View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.INVISIBLE; + window.getDecorView().setSystemUiVisibility(flags); + window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + window.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); + SDLActivity.mFullscreenModeActive = true; + } else { + int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_VISIBLE; + window.getDecorView().setSystemUiVisibility(flags); + window.addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); + window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + SDLActivity.mFullscreenModeActive = false; + } + } + } else { + Log.e(TAG, "error handling message, getContext() returned no Activity"); + } + break; + case COMMAND_TEXTEDIT_HIDE: + if (mTextEdit != null) { + // Note: On some devices setting view to GONE creates a flicker in landscape. + // Setting the View's sizes to 0 is similar to GONE but without the flicker. + // The sizes will be set to useful values when the keyboard is shown again. + mTextEdit.setLayoutParams(new RelativeLayout.LayoutParams(0, 0)); + + InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0); + + mScreenKeyboardShown = false; + } + break; + case COMMAND_SET_KEEP_SCREEN_ON: + { + if (context instanceof Activity) { + 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; + } + case COMMAND_CHANGE_SURFACEVIEW_FORMAT: + { + int format = ((int)msg.obj); + int pf; + + if (SDLActivity.mSurface == null) { + return; + } + + SurfaceHolder holder = SDLActivity.mSurface.getHolder(); + if (holder == null) { + return; + } + + if (format == 1) { + pf = PixelFormat.RGBA_8888; + } else if (format == 2) { + pf = PixelFormat.RGBX_8888; + } else { + pf = PixelFormat.RGB_565; + } + + holder.setFormat(pf); + + 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; + boolean result = commandHandler.sendMessage(msg); + + if ((Build.VERSION.SDK_INT >= 19) && (command == COMMAND_CHANGE_WINDOW_STYLE)) { + // Ensure we don't return until the resize has actually happened, + // or 500ms have passed. + + boolean bShouldWait = false; + + if (data instanceof Integer) { + // Let's figure out if we're already laid out fullscreen or not. + Display display = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); + android.util.DisplayMetrics realMetrics = new android.util.DisplayMetrics(); + display.getRealMetrics( realMetrics ); + + boolean bFullscreenLayout = ((realMetrics.widthPixels == mSurface.getWidth()) && + (realMetrics.heightPixels == mSurface.getHeight())); + + if (((Integer)data).intValue() == 1) { + // If we aren't laid out fullscreen or actively in fullscreen mode already, we're going + // to change size and should wait for surfaceChanged() before we return, so the size + // is right back in native code. If we're already laid out fullscreen, though, we're + // not going to change size even if we change decor modes, so we shouldn't wait for + // surfaceChanged() -- which may not even happen -- and should return immediately. + bShouldWait = !bFullscreenLayout; + } + else { + // If we're laid out fullscreen (even if the status bar and nav bar are present), + // or are actively in fullscreen, we're going to change size and should wait for + // surfaceChanged before we return, so the size is right back in native code. + bShouldWait = bFullscreenLayout; + } + } + + if (bShouldWait) { + // We'll wait for the surfaceChanged() method, which will notify us + // when called. That way, we know our current size is really the + // size we need, instead of grabbing a size that's still got + // the navigation and/or status bars before they're hidden. + // + // We'll wait for up to half a second, because some devices + // take a surprisingly long time for the surface resize, but + // then we'll just give up and return. + // + synchronized(SDLActivity.getContext()) { + try { + SDLActivity.getContext().wait(500); + } + catch (InterruptedException ie) { + ie.printStackTrace(); + } + } + } + } + + return result; + } + + // C functions we call + public static native int nativeSetupJNI(); + public static native int nativeRunMain(String library, String function, Object arguments); + public static native void nativeLowMemory(); + public static native void nativeSendQuit(); + 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 nativeSetScreenResolution(int surfaceWidth, int surfaceHeight, int deviceWidth, int deviceHeight, int format, float rate); + public static native void onNativeResize(); + 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, boolean relative); + 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 onNativeClipboardChanged(); + public static native void onNativeSurfaceCreated(); + public static native void onNativeSurfaceChanged(); + public static native void onNativeSurfaceDestroyed(); + public static native String nativeGetHint(String name); + public static native void nativeSetenv(String name, String value); + public static native void onNativeOrientationChanged(int orientation); + public static native void nativeAddTouch(int touchId, 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 void setWindowStyle(boolean fullscreen) { + // Called from SDLMain() thread and can't directly affect the view + mSingleton.sendCommand(COMMAND_CHANGE_WINDOW_STYLE, fullscreen ? 1 : 0); + } + + /** + * This method is called by SDL using JNI. + * This is a static method for JNI convenience, it calls a non-static method + * so that is can be overridden + */ + public static void setOrientation(int w, int h, boolean resizable, String hint) + { + if (mSingleton != null) { + mSingleton.setOrientationBis(w, h, resizable, hint); + } + } + + /** + * This can be overridden + */ + public void setOrientationBis(int w, int h, boolean resizable, String hint) + { + int orientation = -1; + + if (hint.contains("LandscapeRight") && hint.contains("LandscapeLeft")) { + orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; + } else if (hint.contains("LandscapeRight")) { + orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; + } else if (hint.contains("LandscapeLeft")) { + orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; + } else if (hint.contains("Portrait") && hint.contains("PortraitUpsideDown")) { + orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT; + } else if (hint.contains("Portrait")) { + orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; + } else if (hint.contains("PortraitUpsideDown")) { + orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; + } + + /* no valid hint */ + if (orientation == -1) { + if (resizable) { + /* no fixed orientation */ + } else { + if (w > h) { + orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; + } else { + orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT; + } + } + } + + Log.v("SDL", "setOrientation() orientation=" + orientation + " width=" + w +" height="+ h +" resizable=" + resizable + " hint=" + hint); + if (orientation != -1) { + mSingleton.setRequestedOrientation(orientation); + } + } + + /** + * This method is called by SDL using JNI. + */ + public static boolean isScreenKeyboardShown() + { + if (mTextEdit == null) { + return false; + } + + if (!mScreenKeyboardShown) { + return false; + } + + InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + return imm.isAcceptingText(); + + } + + /** + * This method is called by SDL using JNI. + */ + public static boolean supportsRelativeMouse() + { + // ChromeOS doesn't provide relative mouse motion via the Android 7 APIs + if (isChromebook()) { + return false; + } + + // DeX mode in Samsung Experience 9.0 and earlier doesn't support relative mice properly under + // Android 7 APIs, and simply returns no data under Android 8 APIs. + // + // This is fixed in Samsung Experience 9.5, which corresponds to Android 8.1.0, and + // thus SDK version 27. If we are in DeX mode and not API 27 or higher, as a result, + // we should stick to relative mode. + // + if ((Build.VERSION.SDK_INT < 27) && isDeXMode()) { + return false; + } + + return SDLActivity.getMotionListener().supportsRelativeMouse(); + } + + /** + * This method is called by SDL using JNI. + */ + public static boolean setRelativeMouseEnabled(boolean enabled) + { + if (enabled && !supportsRelativeMouse()) { + return false; + } + + return SDLActivity.getMotionListener().setRelativeMouseEnabled(enabled); + } + + /** + * This method is called by SDL using JNI. + */ + public static boolean sendMessage(int command, int param) { + if (mSingleton == null) { + return false; + } + return mSingleton.sendCommand(command, Integer.valueOf(param)); + } + + /** + * This method is called by SDL using JNI. + */ + public static Context getContext() { + return SDL.getContext(); + } + + /** + * This method is called by SDL using JNI. + */ + public static boolean isAndroidTV() { + UiModeManager uiModeManager = (UiModeManager) getContext().getSystemService(UI_MODE_SERVICE); + if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) { + return true; + } + if (Build.MANUFACTURER.equals("MINIX") && Build.MODEL.equals("NEO-U1")) { + return true; + } + if (Build.MANUFACTURER.equals("Amlogic") && Build.MODEL.equals("X96-W")) { + return true; + } + return false; + } + + /** + * This method is called by SDL using JNI. + */ + public static boolean isTablet() { + DisplayMetrics metrics = new DisplayMetrics(); + Activity activity = (Activity)getContext(); + activity.getWindowManager().getDefaultDisplay().getMetrics(metrics); + + double dWidthInches = metrics.widthPixels / (double)metrics.xdpi; + double dHeightInches = metrics.heightPixels / (double)metrics.ydpi; + + double dDiagonal = Math.sqrt((dWidthInches * dWidthInches) + (dHeightInches * dHeightInches)); + + // If our diagonal size is seven inches or greater, we consider ourselves a tablet. + return (dDiagonal >= 7.0); + } + + /** + * This method is called by SDL using JNI. + */ + public static boolean isChromebook() { + return getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management"); + } + + /** + * This method is called by SDL using JNI. + */ + public static boolean isDeXMode() { + if (Build.VERSION.SDK_INT < 24) { + return false; + } + try { + final Configuration config = getContext().getResources().getConfiguration(); + final Class configClass = config.getClass(); + return configClass.getField("SEM_DESKTOP_MODE_ENABLED").getInt(configClass) + == configClass.getField("semDesktopModeEnabled").getInt(config); + } catch(Exception ignored) { + return false; + } + } + + /** + * This method is called by SDL using JNI. + */ + public static DisplayMetrics getDisplayDPI() { + return getContext().getResources().getDisplayMetrics(); + } + + /** + * This method is called by SDL using JNI. + */ + public static boolean getManifestEnvironmentVariables() { + try { + ApplicationInfo applicationInfo = getContext().getPackageManager().getApplicationInfo(getContext().getPackageName(), PackageManager.GET_META_DATA); + Bundle bundle = applicationInfo.metaData; + if (bundle == null) { + return false; + } + String prefix = "SDL_ENV."; + final int trimLength = prefix.length(); + for (String key : bundle.keySet()) { + if (key.startsWith(prefix)) { + String name = key.substring(trimLength); + String value = bundle.get(key).toString(); + nativeSetenv(name, value); + } + } + /* environment variables set! */ + return true; + } catch (Exception e) { + Log.v("SDL", "exception " + e.toString()); + } + return false; + } + + // This method is called by SDLControllerManager's API 26 Generic Motion Handler. + public static View getContentView() + { + return mSingleton.mLayout; + } + + 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() { + RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(w, h + HEIGHT_PADDING); + params.leftMargin = x; + params.topMargin = y; + + if (mTextEdit == null) { + mTextEdit = new DummyEdit(SDL.getContext()); + + mLayout.addView(mTextEdit, params); + } else { + mTextEdit.setLayoutParams(params); + } + + mTextEdit.setVisibility(View.VISIBLE); + mTextEdit.requestFocus(); + + InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(mTextEdit, 0); + + mScreenKeyboardShown = true; + } + } + + /** + * 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)); + } + + public static boolean isTextInputEvent(KeyEvent event) { + + // Key pressed with Ctrl should be sent as SDL_KEYDOWN/SDL_KEYUP and not SDL_TEXTINPUT + if (event.isCtrlPressed()) { + return false; + } + + return event.isPrintingKey() || event.getKeyCode() == KeyEvent.KEYCODE_SPACE; + } + + /** + * This method is called by SDL using JNI. + */ + public static Surface getNativeSurface() { + if (SDLActivity.mSurface == null) { + return null; + } + return SDLActivity.mSurface.getNativeSurface(); + } + + /** + * This method is called by SDL using JNI. + */ + public static void setSurfaceViewFormat(int format) { + mSingleton.sendCommand(COMMAND_CHANGE_SURFACEVIEW_FORMAT, format); + return; + } + + // Input + + /** + * This method is called by SDL using JNI. + */ + public static void initTouch() { + int[] ids = InputDevice.getDeviceIds(); + + for (int i = 0; i < ids.length; ++i) { + InputDevice device = InputDevice.getDevice(ids[i]); + if (device != null && (device.getSources() & InputDevice.SOURCE_TOUCHSCREEN) != 0) { + nativeAddTouch(device.getId(), device.getName()); + } + } + } + + // APK expansion files support + + /** com.android.vending.expansion.zipfile.ZipResourceFile object or null. */ + private static Object expansionFile; + + /** com.android.vending.expansion.zipfile.ZipResourceFile's getInputStream() or null. */ + private static Method expansionFileMethod; + + /** + * 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 static 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, SDL.getContext(), 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