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