2013-04-15 23:10:32 -07:00
/*
2015-01-01 15:04:46 -08:00
* Copyright 2011 - 2015 Branimir Karadzic . All rights reserved .
2013-04-15 23:10:32 -07:00
* License : http : //www.opensource.org/licenses/BSD-2-Clause
*/
2013-08-14 21:08:46 -07:00
# include "entry_p.h"
2013-04-15 23:10:32 -07:00
2013-08-14 21:08:46 -07:00
# if ENTRY_CONFIG_USE_NATIVE && BX_PLATFORM_ANDROID
2013-04-15 23:10:32 -07:00
2013-08-07 23:11:20 -07:00
# include <bgfxplatform.h>
2013-04-18 21:16:09 -07:00
2013-04-15 23:10:32 -07:00
# include <stdio.h>
2013-04-18 21:16:09 -07:00
# include <bx/thread.h>
# include <android/input.h>
# include <android/log.h>
# include <android/looper.h>
# include <android/window.h>
# include <android_native_app_glue.h>
2013-04-20 23:13:44 -07:00
2013-04-18 21:16:09 -07:00
extern " C "
{
2014-08-22 19:29:54 -07:00
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wunused-parameter"
2013-04-18 21:16:09 -07:00
# include <android_native_app_glue.c>
2014-08-22 19:29:54 -07:00
# pragma GCC diagnostic pop
2013-04-18 21:16:09 -07:00
} // extern "C"
2013-04-15 23:10:32 -07:00
namespace entry
{
2014-12-29 13:53:44 -08:00
struct GamepadRemap
{
uint16_t m_keyCode ;
Key : : Enum m_key ;
} ;
static GamepadRemap s_gamepadRemap [ ] =
{
{ AKEYCODE_DPAD_UP , Key : : GamepadUp } ,
{ AKEYCODE_DPAD_DOWN , Key : : GamepadDown } ,
{ AKEYCODE_DPAD_LEFT , Key : : GamepadLeft } ,
{ AKEYCODE_DPAD_RIGHT , Key : : GamepadRight } ,
{ AKEYCODE_BUTTON_START , Key : : GamepadStart } ,
{ AKEYCODE_BACK , Key : : GamepadBack } ,
{ AKEYCODE_BUTTON_THUMBL , Key : : GamepadThumbL } ,
{ AKEYCODE_BUTTON_THUMBR , Key : : GamepadThumbR } ,
{ AKEYCODE_BUTTON_L1 , Key : : GamepadShoulderL } ,
{ AKEYCODE_BUTTON_R1 , Key : : GamepadShoulderR } ,
{ AKEYCODE_GUIDE , Key : : GamepadGuide } ,
{ AKEYCODE_BUTTON_A , Key : : GamepadA } ,
{ AKEYCODE_BUTTON_B , Key : : GamepadB } ,
{ AKEYCODE_BUTTON_X , Key : : GamepadX } ,
{ AKEYCODE_BUTTON_Y , Key : : GamepadY } ,
} ;
struct GamepadAxisRemap
{
int32_t m_event ;
GamepadAxis : : Enum m_axis ;
bool m_convert ;
} ;
static GamepadAxisRemap s_translateAxis [ ] =
{
{ AMOTION_EVENT_AXIS_X , GamepadAxis : : LeftX , false } ,
{ AMOTION_EVENT_AXIS_Y , GamepadAxis : : LeftY , false } ,
{ AMOTION_EVENT_AXIS_LTRIGGER , GamepadAxis : : LeftZ , false } ,
{ AMOTION_EVENT_AXIS_Z , GamepadAxis : : RightX , true } ,
{ AMOTION_EVENT_AXIS_RZ , GamepadAxis : : RightY , false } ,
{ AMOTION_EVENT_AXIS_RTRIGGER , GamepadAxis : : RightZ , false } ,
} ;
2013-04-18 21:16:09 -07:00
struct MainThreadEntry
{
int m_argc ;
char * * m_argv ;
static int32_t threadFunc ( void * _userData ) ;
} ;
struct Context
{
Context ( )
2013-04-20 23:13:44 -07:00
: m_window ( NULL )
2014-12-25 18:27:27 -08:00
, m_count ( 0 )
2013-04-18 21:16:09 -07:00
{
2014-12-29 13:53:44 -08:00
memset ( m_value , 0 , sizeof ( m_value ) ) ;
// Deadzone values from xinput.h
m_deadzone [ GamepadAxis : : LeftX ] =
m_deadzone [ GamepadAxis : : LeftY ] = 7849 ;
m_deadzone [ GamepadAxis : : RightX ] =
m_deadzone [ GamepadAxis : : RightY ] = 8689 ;
m_deadzone [ GamepadAxis : : LeftZ ] =
m_deadzone [ GamepadAxis : : RightZ ] = 30 ;
2013-04-18 21:16:09 -07:00
}
void run ( android_app * _app )
{
m_app = _app ;
m_app - > userData = ( void * ) this ;
m_app - > onAppCmd = onAppCmdCB ;
m_app - > onInputEvent = onInputEventCB ;
2014-10-24 20:33:56 -07:00
ANativeActivity_setWindowFlags ( m_app - > activity , 0
| AWINDOW_FLAG_FULLSCREEN
| AWINDOW_FLAG_KEEP_SCREEN_ON
, 0
) ;
2013-04-18 21:16:09 -07:00
const char * argv [ 1 ] = { " android.so " } ;
2013-04-20 23:13:44 -07:00
m_mte . m_argc = 1 ;
m_mte . m_argv = const_cast < char * * > ( argv ) ;
2013-04-18 21:16:09 -07:00
while ( 0 = = m_app - > destroyRequested )
{
int32_t num ;
android_poll_source * source ;
/*int32_t id =*/ ALooper_pollAll ( - 1 , NULL , & num , ( void * * ) & source ) ;
if ( NULL ! = source )
{
source - > process ( m_app , source ) ;
}
}
2013-04-20 23:13:44 -07:00
m_thread . shutdown ( ) ;
2013-04-18 21:16:09 -07:00
}
void onAppCmd ( int32_t _cmd )
{
switch ( _cmd )
{
case APP_CMD_INPUT_CHANGED :
// Command from main thread: the AInputQueue has changed. Upon processing
// this command, android_app->inputQueue will be updated to the new queue
// (or NULL).
break ;
case APP_CMD_INIT_WINDOW :
// Command from main thread: a new ANativeWindow is ready for use. Upon
// receiving this command, android_app->window will contain the new window
// surface.
2013-04-20 23:13:44 -07:00
if ( m_window = = NULL )
{
m_window = m_app - > window ;
2014-12-23 23:32:36 -08:00
bgfx : : androidSetWindow ( m_window ) ;
int32_t width = ANativeWindow_getWidth ( m_window ) ;
int32_t height = ANativeWindow_getHeight ( m_window ) ;
DBG ( " ANativeWindow width %d, height %d " , width , height ) ;
WindowHandle defaultWindow = { 0 } ;
m_eventQueue . postSizeEvent ( defaultWindow , width , height ) ;
2013-04-20 23:13:44 -07:00
m_thread . init ( MainThreadEntry : : threadFunc , & m_mte ) ;
}
2013-04-18 21:16:09 -07:00
break ;
case APP_CMD_TERM_WINDOW :
// Command from main thread: the existing ANativeWindow needs to be
// terminated. Upon receiving this command, android_app->window still
// contains the existing window; after calling android_app_exec_cmd
// it will be set to NULL.
break ;
case APP_CMD_WINDOW_RESIZED :
// Command from main thread: the current ANativeWindow has been resized.
// Please redraw with its new size.
break ;
case APP_CMD_WINDOW_REDRAW_NEEDED :
// Command from main thread: the system needs that the current ANativeWindow
// be redrawn. You should redraw the window before handing this to
// android_app_exec_cmd() in order to avoid transient drawing glitches.
break ;
case APP_CMD_CONTENT_RECT_CHANGED :
// Command from main thread: the content area of the window has changed,
// such as from the soft input window being shown or hidden. You can
// find the new content rect in android_app::contentRect.
break ;
case APP_CMD_GAINED_FOCUS :
// Command from main thread: the app's activity window has gained
// input focus.
break ;
case APP_CMD_LOST_FOCUS :
// Command from main thread: the app's activity window has lost
// input focus.
break ;
case APP_CMD_CONFIG_CHANGED :
// Command from main thread: the current device configuration has changed.
break ;
case APP_CMD_LOW_MEMORY :
// Command from main thread: the system is running low on memory.
// Try to reduce your memory use.
break ;
case APP_CMD_START :
// Command from main thread: the app's activity has been started.
break ;
case APP_CMD_RESUME :
// Command from main thread: the app's activity has been resumed.
break ;
case APP_CMD_SAVE_STATE :
// Command from main thread: the app should generate a new saved state
// for itself, to restore from later if needed. If you have saved state,
// allocate it with malloc and place it in android_app.savedState with
// the size in android_app.savedStateSize. The will be freed for you
// later.
break ;
case APP_CMD_PAUSE :
// Command from main thread: the app's activity has been paused.
break ;
case APP_CMD_STOP :
// Command from main thread: the app's activity has been stopped.
break ;
case APP_CMD_DESTROY :
// Command from main thread: the app's activity is being destroyed,
// and waiting for the app thread to clean up and exit before proceeding.
m_eventQueue . postExitEvent ( ) ;
break ;
}
}
2014-12-29 13:53:44 -08:00
bool filter ( GamepadAxis : : Enum _axis , int32_t * _value )
{
const int32_t old = m_value [ _axis ] ;
const int32_t deadzone = m_deadzone [ _axis ] ;
int32_t value = * _value ;
value = value > deadzone | | value < - deadzone ? value : 0 ;
m_value [ _axis ] = value ;
* _value = value ;
return old ! = value ;
}
2013-04-18 21:16:09 -07:00
int32_t onInputEvent ( AInputEvent * _event )
{
2014-12-29 13:53:44 -08:00
WindowHandle defaultWindow = { 0 } ;
GamepadHandle handle = { 0 } ;
const int32_t type = AInputEvent_getType ( _event ) ;
const int32_t source = AInputEvent_getSource ( _event ) ;
const int32_t actionBits = AMotionEvent_getAction ( _event ) ;
2014-12-25 18:27:27 -08:00
switch ( type )
{
case AINPUT_EVENT_TYPE_MOTION :
{
2014-12-29 13:53:44 -08:00
if ( 0 ! = ( source & ( AINPUT_SOURCE_GAMEPAD | AINPUT_SOURCE_JOYSTICK ) ) )
{
for ( uint32_t ii = 0 ; ii < BX_COUNTOF ( s_translateAxis ) ; + + ii )
{
const float fval = AMotionEvent_getAxisValue ( _event , s_translateAxis [ ii ] . m_event , 0 ) ;
int32_t value = int32_t ( ( s_translateAxis [ ii ] . m_convert ? fval * 2.0f + 1.0f : fval ) * INT16_MAX ) ;
GamepadAxis : : Enum axis = s_translateAxis [ ii ] . m_axis ;
if ( filter ( axis , & value ) )
{
m_eventQueue . postAxisEvent ( defaultWindow , handle , axis , value ) ;
}
}
2014-12-25 18:27:27 -08:00
2014-12-29 13:53:44 -08:00
return 1 ;
}
else
{
float mx = AMotionEvent_getX ( _event , 0 ) ;
float my = AMotionEvent_getY ( _event , 0 ) ;
int32_t count = AMotionEvent_getPointerCount ( _event ) ;
2014-12-25 18:27:27 -08:00
2014-12-29 13:53:44 -08:00
int32_t action = ( actionBits & AMOTION_EVENT_ACTION_MASK ) ;
int32_t index = ( actionBits & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK ) > > AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT ;
2014-12-25 18:27:27 -08:00
2014-12-29 13:53:44 -08:00
count = m_count ;
2014-12-25 18:27:27 -08:00
2014-12-29 13:53:44 -08:00
switch ( action )
{
case AMOTION_EVENT_ACTION_DOWN :
case AMOTION_EVENT_ACTION_POINTER_DOWN :
m_count + + ;
break ;
case AMOTION_EVENT_ACTION_UP :
case AMOTION_EVENT_ACTION_POINTER_UP :
m_count - - ;
break ;
default :
break ;
}
2014-12-25 18:27:27 -08:00
2014-12-29 13:53:44 -08:00
if ( count ! = m_count )
2014-12-25 18:27:27 -08:00
{
m_eventQueue . postMouseEvent ( defaultWindow
, ( int32_t ) mx
, ( int32_t ) my
, 0
2014-12-29 13:53:44 -08:00
, 1 = = count ? MouseButton : : Left : MouseButton : : Right
, false
2014-12-25 18:27:27 -08:00
) ;
2014-12-29 13:53:44 -08:00
if ( 0 ! = m_count )
{
m_eventQueue . postMouseEvent ( defaultWindow
, ( int32_t ) mx
, ( int32_t ) my
, 0
, 1 = = m_count ? MouseButton : : Left : MouseButton : : Right
, true
) ;
}
}
switch ( action )
{
case AMOTION_EVENT_ACTION_MOVE :
if ( 0 = = index )
{
m_eventQueue . postMouseEvent ( defaultWindow
, ( int32_t ) mx
, ( int32_t ) my
, 0
) ;
}
break ;
default :
break ;
2014-12-25 18:27:27 -08:00
}
}
2014-12-29 13:53:44 -08:00
}
break ;
2014-12-25 18:27:27 -08:00
2014-12-29 13:53:44 -08:00
case AINPUT_EVENT_TYPE_KEY :
{
int32_t keyCode = AKeyEvent_getKeyCode ( _event ) ;
if ( 0 ! = ( source & ( AINPUT_SOURCE_GAMEPAD | AINPUT_SOURCE_JOYSTICK ) ) )
2014-12-25 18:27:27 -08:00
{
2014-12-29 13:53:44 -08:00
for ( uint32_t jj = 0 ; jj < BX_COUNTOF ( s_gamepadRemap ) ; + + jj )
2014-12-25 18:27:27 -08:00
{
2014-12-29 13:53:44 -08:00
if ( keyCode = = s_gamepadRemap [ jj ] . m_keyCode )
{
m_eventQueue . postKeyEvent ( defaultWindow , s_gamepadRemap [ jj ] . m_key , 0 , actionBits = = AKEY_EVENT_ACTION_DOWN ) ;
break ;
}
2014-12-25 18:27:27 -08:00
}
}
2014-12-29 13:53:44 -08:00
return 1 ;
2014-12-25 18:27:27 -08:00
}
break ;
2014-12-29 13:53:44 -08:00
default :
DBG ( " type %d " , type ) ;
2014-12-25 18:27:27 -08:00
break ;
}
2014-12-29 13:53:44 -08:00
2013-04-18 21:16:09 -07:00
return 0 ;
}
static void onAppCmdCB ( struct android_app * _app , int32_t _cmd )
{
Context * self = ( Context * ) _app - > userData ;
self - > onAppCmd ( _cmd ) ;
}
static int32_t onInputEventCB ( struct android_app * _app , AInputEvent * _event )
{
Context * self = ( Context * ) _app - > userData ;
return self - > onInputEvent ( _event ) ;
}
2013-04-20 23:13:44 -07:00
MainThreadEntry m_mte ;
bx : : Thread m_thread ;
2013-04-18 21:16:09 -07:00
EventQueue m_eventQueue ;
2013-04-20 23:13:44 -07:00
ANativeWindow * m_window ;
2013-04-18 21:16:09 -07:00
android_app * m_app ;
2014-12-25 18:27:27 -08:00
int32_t m_count ;
2014-12-29 13:53:44 -08:00
int32_t m_value [ GamepadAxis : : Count ] ;
int32_t m_deadzone [ GamepadAxis : : Count ] ;
2013-04-18 21:16:09 -07:00
} ;
static Context s_ctx ;
2013-04-15 23:10:32 -07:00
const Event * poll ( )
{
2013-04-18 21:16:09 -07:00
return s_ctx . m_eventQueue . poll ( ) ;
2013-04-15 23:10:32 -07:00
}
2014-09-22 19:34:10 -07:00
const Event * poll ( WindowHandle _handle )
{
return s_ctx . m_eventQueue . poll ( _handle ) ;
}
2013-04-15 23:10:32 -07:00
void release ( const Event * _event )
{
2013-04-18 21:16:09 -07:00
s_ctx . m_eventQueue . release ( _event ) ;
2013-04-15 23:10:32 -07:00
}
2014-09-22 19:34:10 -07:00
WindowHandle createWindow ( int32_t _x , int32_t _y , uint32_t _width , uint32_t _height , uint32_t _flags , const char * _title )
{
BX_UNUSED ( _x , _y , _width , _height , _flags , _title ) ;
WindowHandle handle = { UINT16_MAX } ;
return handle ;
}
void destroyWindow ( WindowHandle _handle )
{
BX_UNUSED ( _handle ) ;
}
void setWindowPos ( WindowHandle _handle , int32_t _x , int32_t _y )
{
BX_UNUSED ( _handle , _x , _y ) ;
}
2014-09-18 22:32:33 -07:00
void setWindowSize ( WindowHandle _handle , uint32_t _width , uint32_t _height )
2013-04-15 23:10:32 -07:00
{
2014-09-18 22:32:33 -07:00
BX_UNUSED ( _handle , _width , _height ) ;
2013-04-15 23:10:32 -07:00
}
2014-09-18 22:32:33 -07:00
void setWindowTitle ( WindowHandle _handle , const char * _title )
2014-08-05 20:57:52 +01:00
{
2014-09-18 22:32:33 -07:00
BX_UNUSED ( _handle , _title ) ;
2014-08-05 20:57:52 +01:00
}
2014-09-18 22:32:33 -07:00
void toggleWindowFrame ( WindowHandle _handle )
2013-04-15 23:10:32 -07:00
{
2014-09-18 22:32:33 -07:00
BX_UNUSED ( _handle ) ;
2013-04-15 23:10:32 -07:00
}
2015-03-07 23:38:48 +01:00
void toggleFullscreen ( WindowHandle _handle )
{
BX_UNUSED ( _handle ) ;
}
2014-09-18 22:32:33 -07:00
void setMouseLock ( WindowHandle _handle , bool _lock )
2013-04-15 23:10:32 -07:00
{
2014-09-18 22:32:33 -07:00
BX_UNUSED ( _handle , _lock ) ;
2013-04-15 23:10:32 -07:00
}
2013-04-18 21:16:09 -07:00
int32_t MainThreadEntry : : threadFunc ( void * _userData )
{
2013-12-09 22:36:13 -08:00
int32_t result = chdir ( " /sdcard/bgfx/examples/runtime " ) ;
BX_CHECK ( 0 = = result , " Failed to chdir to dir. android.permission.WRITE_EXTERNAL_STORAGE? " , errno ) ;
2013-04-18 21:16:09 -07:00
MainThreadEntry * self = ( MainThreadEntry * ) _userData ;
2013-12-09 22:36:13 -08:00
result = main ( self - > m_argc , self - > m_argv ) ;
2013-04-18 21:16:09 -07:00
// PostMessage(s_ctx.m_hwnd, WM_QUIT, 0, 0);
return result ;
}
2013-04-15 23:10:32 -07:00
} // namespace entry
2013-04-18 21:16:09 -07:00
extern " C " void android_main ( android_app * _app )
2013-04-15 23:10:32 -07:00
{
2013-04-18 21:16:09 -07:00
using namespace entry ;
s_ctx . run ( _app ) ;
2013-04-15 23:10:32 -07:00
}
2013-08-14 21:08:46 -07:00
# endif // ENTRY_CONFIG_USE_NATIVE && BX_PLATFORM_ANDROID