]> git.tdb.fi Git - libs/gui.git/commitdiff
Android support
authorMikko Rasa <tdb@tdb.fi>
Sun, 12 Oct 2014 15:30:32 +0000 (18:30 +0300)
committerMikko Rasa <tdb@tdb.fi>
Sun, 12 Oct 2014 16:12:37 +0000 (19:12 +0300)
Everything should work to some degree at least, though some details are
still missing.

Build
source/graphics/android/display.cpp [new file with mode: 0644]
source/graphics/android/display_platform.h [new file with mode: 0644]
source/graphics/android/window.cpp [new file with mode: 0644]
source/graphics/android/window_platform.h [new file with mode: 0644]
source/graphics/egl_android/glcontext.cpp [new file with mode: 0644]
source/input/android/keyboard.cpp [new file with mode: 0644]
source/input/android/keys.cpp [new file with mode: 0644]
source/input/android/mouse.cpp [new file with mode: 0644]

diff --git a/Build b/Build
index 9580ba607984f90dec3893321ce70db3b066e050..9d42217e270a02c90e6ec4f2c56292346952bcea 100644 (file)
--- a/Build
+++ b/Build
@@ -5,7 +5,7 @@ package "mspgui"
 
        require "mspcore";
        require "sigc++-2.0";
-       if_arch "!windows & !darwin"
+       if_arch "!windows & !darwin & !android"
        {
                require "xlib";
        };
@@ -73,10 +73,17 @@ package "mspgui"
        };
        if_feature "opengl"
        {
-               require "opengl";
+               if_arch "android"
+               {
+                       require "opengles";
+               };
+               if_arch "!android"
+               {
+                       require "opengl";
+               };
        };
 
-       if_arch "!windows & !darwin"
+       if_arch "!windows & !darwin & !android"
        {
                feature "xrandr" "Include support for video mode switching with XRandR";
                if_feature "xrandr"
@@ -129,7 +136,15 @@ package "mspgui"
                                standard CC "c99";
                        };
                };
-               if_arch "!windows & !darwin"
+               if_arch "android"
+               {
+                       overlay "android";
+                       if_feature "opengl"
+                       {
+                               overlay "egl_android";
+                       };
+               };
+               if_arch "!windows & !darwin & !android"
                {
                        overlay "x11";
                        if_feature "opengl"
diff --git a/source/graphics/android/display.cpp b/source/graphics/android/display.cpp
new file mode 100644 (file)
index 0000000..f0afc72
--- /dev/null
@@ -0,0 +1,118 @@
+#include <msp/core/application.h>
+#include <msp/core/mainthread.h>
+#include "display.h"
+#include "display_private.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Graphics {
+
+Display::Display(const string &)
+{
+       Android::MainThread *thread = reinterpret_cast<Android::MainThread *>(Application::get_data());
+       if(!thread->is_starting_up())
+               throw logic_error("Display must be created during startup");
+
+       priv = new Private;
+       priv->input_queue = 0;
+       priv->native_window = 0;
+
+       thread->signal_native_window_created.connect(sigc::mem_fun(priv, &PlatformDisplayPrivate::native_window_created));
+       thread->signal_native_window_resized.connect(sigc::mem_fun(priv, &PlatformDisplayPrivate::native_window_resized));
+       thread->signal_native_window_destroyed.connect(sigc::mem_fun(priv, &PlatformDisplayPrivate::native_window_destroyed));
+       thread->signal_input_queue_created.connect(sigc::mem_fun(priv, &PlatformDisplayPrivate::input_queue_created));
+       thread->signal_input_queue_destroyed.connect(sigc::mem_fun(priv, &PlatformDisplayPrivate::input_queue_destroyed));
+       thread->resume_startup();
+}
+
+Display::~Display()
+{
+       delete priv;
+}
+
+void Display::set_mode(const VideoMode &, bool)
+{
+       throw runtime_error("video mode switching not supported");
+}
+
+bool Display::process_events()
+{
+       MutexLock lock(priv->event_mutex);
+       if(!priv->input_queue)
+               return false;
+
+       Window::Event event;
+       if(!priv->events.empty())
+       {
+               event = priv->events.front();
+               priv->events.pop_front();
+       }
+       else if(AInputQueue_getEvent(priv->input_queue, &event.aevent)>=0)
+       {
+               if(AInputQueue_preDispatchEvent(priv->input_queue, event.aevent))
+                       return true;
+
+               event.type = INPUT_EVENT;
+       }
+       else
+               return false;
+
+       bool handled = false;
+       if(!priv->windows.empty())
+               handled = priv->windows.begin()->second->event(event);
+
+       if(event.type==INPUT_EVENT)
+               AInputQueue_finishEvent(priv->input_queue, event.aevent, handled);
+       else if(event.type==WINDOW_CREATED)
+               priv->window_mutex.lock();
+       else if(event.type==WINDOW_DESTROYED)
+               priv->window_mutex.unlock();
+
+       return true;
+}
+
+void Display::check_error()
+{
+}
+
+
+void PlatformDisplayPrivate::push_event(Msp::Graphics::AndroidEventType type)
+{
+       MutexLock lock(event_mutex);
+       Window::Event event;
+       event.type = type;
+       events.push_back(event);
+}
+
+void PlatformDisplayPrivate::native_window_created(ANativeWindow *window)
+{
+       native_window = window;
+       push_event(WINDOW_CREATED);
+}
+
+void PlatformDisplayPrivate::native_window_resized(ANativeWindow *)
+{
+       push_event(WINDOW_RESIZED);
+}
+
+void PlatformDisplayPrivate::native_window_destroyed(ANativeWindow *)
+{
+       push_event(WINDOW_DESTROYED);
+       MutexLock lock(window_mutex);
+       native_window = 0;
+}
+
+void PlatformDisplayPrivate::input_queue_created(AInputQueue *queue)
+{
+       MutexLock lock(event_mutex);
+       input_queue = queue;
+}
+
+void PlatformDisplayPrivate::input_queue_destroyed(AInputQueue *)
+{
+       MutexLock lock(event_mutex);
+       input_queue = 0;
+}
+} // namespace Graphics
+} // namespace Msp
diff --git a/source/graphics/android/display_platform.h b/source/graphics/android/display_platform.h
new file mode 100644 (file)
index 0000000..198fd46
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef MSP_GRAPHICS_DISPLAY_PLATFORM_H_
+#define MSP_GRAPHICS_DISPLAY_PLATFORM_H_
+
+#include <android/input.h>
+#include <android/native_window.h>
+#include <msp/core/mutex.h>
+#include "window_private.h"
+
+namespace Msp {
+namespace Graphics {
+
+typedef void *DisplayHandle;
+
+struct PlatformDisplayPrivate
+{
+       AInputQueue *input_queue;
+       std::list<Window::Event> events;
+       Mutex event_mutex;
+       WindowHandle native_window;
+       Mutex window_mutex;
+
+       void push_event(AndroidEventType);
+       void native_window_created(ANativeWindow *);
+       void native_window_resized(ANativeWindow *);
+       void native_window_destroyed(ANativeWindow *);
+       void input_queue_created(AInputQueue *);
+       void input_queue_destroyed(AInputQueue *);
+};
+
+} // namespace Graphics
+} // namespace Msp
+
+#endif
diff --git a/source/graphics/android/window.cpp b/source/graphics/android/window.cpp
new file mode 100644 (file)
index 0000000..465d325
--- /dev/null
@@ -0,0 +1,76 @@
+#include "display.h"
+#include "display_private.h"
+#include "window.h"
+#include "window_private.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Graphics {
+
+void Window::platform_init()
+{
+       priv->window = 0;
+}
+
+void Window::platform_cleanup()
+{
+}
+
+void Window::set_title(const string &)
+{
+}
+
+void Window::platform_reconfigure(bool)
+{
+}
+
+void Window::show_cursor(bool)
+{
+}
+
+void Window::warp_pointer(int, int)
+{
+}
+
+void Window::platform_show()
+{
+}
+
+void Window::platform_hide()
+{
+}
+
+bool Window::event(const Event &evnt)
+{
+       switch(evnt.type)
+       {
+       case INPUT_EVENT:
+               signal_input_event.emit(evnt);
+               break;
+       case WINDOW_CREATED:
+               priv->window = display.get_private().native_window;
+               display.remove_window(*this);
+               display.add_window(*this);
+               priv->signal_window_acquired.emit(priv->window);
+               break;
+       case WINDOW_RESIZED:
+               options.width = ANativeWindow_getWidth(priv->window);
+               options.height = ANativeWindow_getHeight(priv->window);
+               signal_resize.emit(options.width, options.height);
+               break;
+       case WINDOW_DESTROYED:
+               priv->signal_window_lost.emit();
+               priv->window = 0;
+               display.remove_window(*this);
+               display.add_window(*this);
+               break;
+       default:
+               return false;
+       }
+
+       return true;
+}
+
+} // namespace Graphics
+} // namespace Msp
diff --git a/source/graphics/android/window_platform.h b/source/graphics/android/window_platform.h
new file mode 100644 (file)
index 0000000..3ed8f84
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef MSP_GRAPHICS_WINDOW_PLATFORM_H_
+#define MSP_GRAPHICS_WINDOW_PLATFORM_H_
+
+#include <android/input.h>
+#include <android/native_window.h>
+
+namespace Msp {
+namespace Graphics {
+
+typedef ANativeWindow *WindowHandle;
+
+enum AndroidEventType
+{
+       INPUT_EVENT,
+       WINDOW_CREATED,
+       WINDOW_RESIZED,
+       WINDOW_DESTROYED
+};
+
+struct PlatformWindowPrivate
+{
+       mutable sigc::signal<void, WindowHandle> signal_window_acquired;
+       mutable sigc::signal<void> signal_window_lost;
+};
+
+struct PlatformEvent
+{
+       AndroidEventType type;
+       AInputEvent *aevent;
+};
+
+} // namespace Graphics
+} // namespace Msp
+
+#endif
diff --git a/source/graphics/egl_android/glcontext.cpp b/source/graphics/egl_android/glcontext.cpp
new file mode 100644 (file)
index 0000000..ecf9a63
--- /dev/null
@@ -0,0 +1,136 @@
+#include <vector>
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <android/native_window.h>
+#include "display.h"
+#include "glcontext.h"
+#include "window_private.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Graphics {
+
+struct GLContext::Private
+{
+       EGLDisplay display;
+       EGLConfig config;
+       EGLSurface surface;
+       EGLContext context;
+
+       void attach(WindowHandle);
+       void detach();
+};
+
+
+void GLContext::platform_init(const GLOptions &opts)
+{
+       EGLDisplay egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+       if(egl_display==EGL_NO_DISPLAY)
+               throw runtime_error("no egl display");
+
+       if(!eglInitialize(egl_display, 0, 0))
+               throw runtime_error("could not initialize egl");
+
+       vector<int> attribs;
+
+       attribs.push_back(EGL_COLOR_BUFFER_TYPE);
+       attribs.push_back(EGL_RGB_BUFFER);
+       attribs.push_back(EGL_RENDERABLE_TYPE);
+       attribs.push_back(EGL_OPENGL_ES2_BIT);
+       attribs.push_back(EGL_SURFACE_TYPE);
+       attribs.push_back(EGL_WINDOW_BIT);
+       attribs.push_back(EGL_DEPTH_SIZE);
+       attribs.push_back(1);
+
+       if(opts.alpha)
+       {
+               attribs.push_back(EGL_ALPHA_SIZE);
+               attribs.push_back(1);
+       }
+
+       if(opts.stencil)
+       {
+               attribs.push_back(EGL_STENCIL_SIZE);
+               attribs.push_back(1);
+       }
+
+       if(opts.multisample>0)
+       {
+               attribs.push_back(EGL_SAMPLE_BUFFERS);
+               attribs.push_back(1);
+               attribs.push_back(EGL_SAMPLES);
+               attribs.push_back(opts.multisample);
+       }
+
+       attribs.push_back(EGL_NONE);
+
+       EGLConfig config;
+       int num_configs;
+       if(!eglChooseConfig(egl_display, &attribs.front(), &config, 1, &num_configs))
+       {
+               eglTerminate(egl_display);
+               throw unsupported_gl_mode(opts);
+       }
+
+       priv = new Private;
+       priv->display = egl_display;
+       priv->config = config;
+
+       /* Must wait for the window to be available, because the calling code
+       expects GL to be usable after creation of the context. */
+       WindowHandle native_window;
+       const Window::Private &window_priv = window.get_private();
+       while(!(native_window = window_priv.window))
+               display.tick();
+
+       int format;
+       eglGetConfigAttrib(priv->display, config, EGL_NATIVE_VISUAL_ID, &format);
+       ANativeWindow_setBuffersGeometry(native_window, 0, 0, format);
+
+       priv->surface = eglCreateWindowSurface(priv->display, config, native_window, 0);
+
+       eglBindAPI(EGL_OPENGL_ES_API);
+       int context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
+       priv->context = eglCreateContext(priv->display, config, 0, context_attribs);
+
+       eglMakeCurrent(priv->display, priv->surface, priv->surface, priv->context);
+
+       window_priv.signal_window_acquired.connect(sigc::mem_fun(priv, &Private::attach));
+       window_priv.signal_window_lost.connect(sigc::mem_fun(priv, &Private::detach));
+}
+
+GLContext::~GLContext()
+{
+       eglDestroyContext(priv->display, priv->context);
+       if(priv->surface!=EGL_NO_SURFACE)
+               eglDestroySurface(priv->display, priv->surface);
+       eglTerminate(priv->display);
+       delete priv;
+}
+
+void GLContext::swap_buffers()
+{
+       eglSwapBuffers(priv->display, priv->surface);
+}
+
+void GLContext::window_resized(unsigned w, unsigned h)
+{
+       glViewport(0, 0, w, h);
+}
+
+
+void GLContext::Private::attach(WindowHandle native_window)
+{
+       surface = eglCreateWindowSurface(display, config, native_window, 0);
+       eglMakeCurrent(display, surface, surface, context);
+}
+
+void GLContext::Private::detach()
+{
+       eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+       eglDestroySurface(display, surface);
+}
+
+} // namespace Graphics
+} // namespace Msp
diff --git a/source/input/android/keyboard.cpp b/source/input/android/keyboard.cpp
new file mode 100644 (file)
index 0000000..631b048
--- /dev/null
@@ -0,0 +1,35 @@
+#include <android/input.h>
+#include <msp/graphics/window_private.h>
+#include "keyboard.h"
+#include "keys_private.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Input {
+
+string Keyboard::get_button_name(unsigned btn) const
+{
+       return Device::get_button_name(btn);
+}
+
+void Keyboard::input_event(const Graphics::Window::Event &event)
+{
+       int type = AInputEvent_getType(event.aevent);
+       if(type!=AINPUT_EVENT_TYPE_KEY)
+               return;
+
+       int action = AKeyEvent_getAction(event.aevent);
+       switch(action)
+       {
+       case AKEY_EVENT_ACTION_DOWN:
+       case AKEY_EVENT_ACTION_UP:
+               if(unsigned key = key_from_sys(AKeyEvent_getKeyCode(event.aevent)))
+                       set_button_state(key, action==AKEY_EVENT_ACTION_DOWN, true);
+               break;
+       default:;
+       }
+}
+
+} // namespace Input
+} // namespace Msp
diff --git a/source/input/android/keys.cpp b/source/input/android/keys.cpp
new file mode 100644 (file)
index 0000000..a96df8b
--- /dev/null
@@ -0,0 +1,51 @@
+#include <android/keycodes.h>
+#include "keys.h"
+
+namespace Msp {
+namespace Input {
+
+unsigned sys_keymap[N_KEYS_] =
+{
+       0, 0, 0, 0, 0, 0, 0, 0,
+       AKEYCODE_DEL, AKEYCODE_TAB, AKEYCODE_ENTER, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, AKEYCODE_ESCAPE, 0, 0, 0, 0,
+
+       AKEYCODE_SPACE, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0,
+       AKEYCODE_0, AKEYCODE_1, AKEYCODE_2, AKEYCODE_3, AKEYCODE_4, AKEYCODE_5, AKEYCODE_6, AKEYCODE_7,
+       AKEYCODE_8, AKEYCODE_9, 0, 0, 0, 0, 0, 0,
+
+       0, AKEYCODE_A, AKEYCODE_B, AKEYCODE_C, AKEYCODE_D, AKEYCODE_E, AKEYCODE_F, AKEYCODE_G,
+       AKEYCODE_H, AKEYCODE_I, AKEYCODE_J, AKEYCODE_K, AKEYCODE_L, AKEYCODE_M, AKEYCODE_N, AKEYCODE_O,
+       AKEYCODE_P, AKEYCODE_Q, AKEYCODE_R, AKEYCODE_S, AKEYCODE_T, AKEYCODE_U, AKEYCODE_V, AKEYCODE_W,
+       AKEYCODE_X, AKEYCODE_Y, AKEYCODE_Z, 0, 0, 0, 0, 0,
+
+       0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0,
+
+       0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0,
+
+       AKEYCODE_SOFT_LEFT, AKEYCODE_SOFT_RIGHT, 0, 0, 0, 0, 0, 0,
+       AKEYCODE_MOVE_HOME, AKEYCODE_MOVE_END, AKEYCODE_PAGE_UP, AKEYCODE_PAGE_DOWN, AKEYCODE_INSERT, AKEYCODE_FORWARD_DEL, 0, 0,
+       0, AKEYCODE_F1, AKEYCODE_F2, AKEYCODE_F3, AKEYCODE_F4, AKEYCODE_F5, AKEYCODE_F6, AKEYCODE_F7,
+       AKEYCODE_F8, AKEYCODE_F9, AKEYCODE_F10, AKEYCODE_F11, AKEYCODE_F12, 0, 0, 0,
+
+       AKEYCODE_SHIFT_LEFT, AKEYCODE_SHIFT_RIGHT, AKEYCODE_CTRL_LEFT, AKEYCODE_CTRL_RIGHT, AKEYCODE_ALT_LEFT, AKEYCODE_ALT_RIGHT, 0, 0,
+       AKEYCODE_CAPS_LOCK, AKEYCODE_SCROLL_LOCK, AKEYCODE_NUM_LOCK, 0, 0, 0, 0, 0,
+       AKEYCODE_NUMPAD_0, AKEYCODE_NUMPAD_1, AKEYCODE_NUMPAD_2, AKEYCODE_NUMPAD_3, AKEYCODE_NUMPAD_4, AKEYCODE_NUMPAD_5, AKEYCODE_NUMPAD_6, AKEYCODE_NUMPAD_7,
+       AKEYCODE_NUMPAD_8, AKEYCODE_NUMPAD_9, AKEYCODE_NUMPAD_ADD, AKEYCODE_NUMPAD_SUBTRACT, AKEYCODE_NUMPAD_MULTIPLY, AKEYCODE_NUMPAD_DIVIDE, AKEYCODE_NUMPAD_DOT, 0,
+
+       0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+} // namespace Input
+} // namespace Msp
diff --git a/source/input/android/mouse.cpp b/source/input/android/mouse.cpp
new file mode 100644 (file)
index 0000000..b16b6be
--- /dev/null
@@ -0,0 +1,49 @@
+#include <msp/graphics/window_private.h>
+#include "mouse.h"
+
+namespace Msp {
+namespace Input {
+
+void Mouse::input_event(const Graphics::Window::Event &event)
+{
+       int type = AInputEvent_getType(event.aevent);
+       if(type!=AINPUT_EVENT_TYPE_MOTION)
+               return;
+
+       /* Emulate a mouse with the touch events of a single finger.  If more
+       fingers appear while the first one is held down, they are ignored, even if
+       the first finger is released. */
+
+       int action = AMotionEvent_getAction(event.aevent);
+       int action_pointer = (action&AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)>>AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+       action &= AMOTION_EVENT_ACTION_MASK;
+
+       int pointer_count = AMotionEvent_getPointerCount(event.aevent);
+       int pointer_zero = -1;
+       for(int i=0; (i<pointer_count && pointer_zero<0); ++i)
+               if(AMotionEvent_getPointerId(event.aevent, i)==0)
+                       pointer_zero = i;
+
+       if(pointer_zero>=0)
+       {
+               float x = AMotionEvent_getX(event.aevent, pointer_zero);
+               float y = AMotionEvent_getY(event.aevent, pointer_zero);
+               set_axis_value(0, x*2/window.get_width()-1, true);
+               set_axis_value(1, 1-y*2/window.get_height(), true);
+       }
+
+       switch(action)
+       {
+       case AMOTION_EVENT_ACTION_DOWN:
+       case AMOTION_EVENT_ACTION_UP:
+       case AMOTION_EVENT_ACTION_POINTER_DOWN:
+       case AMOTION_EVENT_ACTION_POINTER_UP:
+               if(action_pointer==0)
+                       set_button_state(1, action==AMOTION_EVENT_ACTION_DOWN, true);
+               break;
+       default:;
+       }
+}
+
+} // namespace Input
+} // namespace Msp