From a0584001d28ae224e0065c3c11b09d7965e78963 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sun, 12 Oct 2014 18:30:32 +0300 Subject: [PATCH] Android support Everything should work to some degree at least, though some details are still missing. --- Build | 23 +++- source/graphics/android/display.cpp | 118 ++++++++++++++++++ source/graphics/android/display_platform.h | 33 +++++ source/graphics/android/window.cpp | 76 ++++++++++++ source/graphics/android/window_platform.h | 35 ++++++ source/graphics/egl_android/glcontext.cpp | 136 +++++++++++++++++++++ source/input/android/keyboard.cpp | 35 ++++++ source/input/android/keys.cpp | 51 ++++++++ source/input/android/mouse.cpp | 49 ++++++++ 9 files changed, 552 insertions(+), 4 deletions(-) create mode 100644 source/graphics/android/display.cpp create mode 100644 source/graphics/android/display_platform.h create mode 100644 source/graphics/android/window.cpp create mode 100644 source/graphics/android/window_platform.h create mode 100644 source/graphics/egl_android/glcontext.cpp create mode 100644 source/input/android/keyboard.cpp create mode 100644 source/input/android/keys.cpp create mode 100644 source/input/android/mouse.cpp diff --git a/Build b/Build index 9580ba6..9d42217 100644 --- 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 index 0000000..f0afc72 --- /dev/null +++ b/source/graphics/android/display.cpp @@ -0,0 +1,118 @@ +#include +#include +#include "display.h" +#include "display_private.h" + +using namespace std; + +namespace Msp { +namespace Graphics { + +Display::Display(const string &) +{ + Android::MainThread *thread = reinterpret_cast(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 index 0000000..198fd46 --- /dev/null +++ b/source/graphics/android/display_platform.h @@ -0,0 +1,33 @@ +#ifndef MSP_GRAPHICS_DISPLAY_PLATFORM_H_ +#define MSP_GRAPHICS_DISPLAY_PLATFORM_H_ + +#include +#include +#include +#include "window_private.h" + +namespace Msp { +namespace Graphics { + +typedef void *DisplayHandle; + +struct PlatformDisplayPrivate +{ + AInputQueue *input_queue; + std::list 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 index 0000000..465d325 --- /dev/null +++ b/source/graphics/android/window.cpp @@ -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 index 0000000..3ed8f84 --- /dev/null +++ b/source/graphics/android/window_platform.h @@ -0,0 +1,35 @@ +#ifndef MSP_GRAPHICS_WINDOW_PLATFORM_H_ +#define MSP_GRAPHICS_WINDOW_PLATFORM_H_ + +#include +#include + +namespace Msp { +namespace Graphics { + +typedef ANativeWindow *WindowHandle; + +enum AndroidEventType +{ + INPUT_EVENT, + WINDOW_CREATED, + WINDOW_RESIZED, + WINDOW_DESTROYED +}; + +struct PlatformWindowPrivate +{ + mutable sigc::signal signal_window_acquired; + mutable sigc::signal 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 index 0000000..ecf9a63 --- /dev/null +++ b/source/graphics/egl_android/glcontext.cpp @@ -0,0 +1,136 @@ +#include +#include +#include +#include +#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 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 index 0000000..631b048 --- /dev/null +++ b/source/input/android/keyboard.cpp @@ -0,0 +1,35 @@ +#include +#include +#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 index 0000000..a96df8b --- /dev/null +++ b/source/input/android/keys.cpp @@ -0,0 +1,51 @@ +#include +#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 index 0000000..b16b6be --- /dev/null +++ b/source/input/android/mouse.cpp @@ -0,0 +1,49 @@ +#include +#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=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 -- 2.43.0