2010-06-06 09:34:36 +00:00
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers , whose names
* are too numerous to list here . Please refer to the COPYRIGHT
* file distributed with this source distribution .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
2014-02-18 02:34:17 +01:00
*
2010-06-06 09:34:36 +00:00
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
2014-02-18 02:34:17 +01:00
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
2010-06-06 09:34:36 +00:00
* GNU General Public License for more details .
2014-02-18 02:34:17 +01:00
*
2010-06-06 09:34:36 +00:00
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
*/
2011-02-06 16:03:52 +01:00
# if defined(__ANDROID__)
2019-09-15 00:46:02 +03:00
# define FORBIDDEN_SYMBOL_EXCEPTION_getenv(a)
2011-05-03 11:27:44 +02:00
// Allow use of stuff in <time.h>
# define FORBIDDEN_SYMBOL_EXCEPTION_time_h
2011-05-03 14:29:01 +02:00
// Disable printf override in common/forbidden.h to avoid
// clashes with log.h from the Android SDK.
// That header file uses
// __attribute__ ((format(printf, 3, 4)))
// which gets messed up by our override mechanism; this could
// be avoided by either changing the Android SDK to use the equally
// legal and valid
// __attribute__ ((format(printf, 3, 4)))
// or by refining our printf override to use a varadic macro
// (which then wouldn't be portable, though).
// Anyway, for now we just disable the printf override globally
// for the Android port
# define FORBIDDEN_SYMBOL_EXCEPTION_printf
2010-06-06 09:34:36 +00:00
# include <sys/time.h>
2011-02-26 14:53:02 +01:00
# include <sys/resource.h>
2011-03-19 12:17:43 +01:00
# include <sys/system_properties.h>
2010-06-06 09:34:36 +00:00
# include <time.h>
2011-02-20 17:01:46 +01:00
# include <unistd.h>
2010-06-06 09:34:36 +00:00
# include "common/util.h"
2011-04-28 20:02:01 +02:00
# include "common/textconsole.h"
2010-06-06 09:34:36 +00:00
# include "common/rect.h"
# include "common/queue.h"
# include "common/mutex.h"
# include "common/events.h"
# include "common/config-manager.h"
2019-10-18 16:48:18 +01:00
# include "backends/audiocd/default/default-audiocd.h"
2020-08-27 23:05:45 +01:00
# include "backends/events/default/default-events.h"
2019-04-10 23:16:49 +01:00
# include "backends/mutex/pthread/pthread-mutex.h"
2010-06-06 09:34:36 +00:00
# include "backends/saves/default/default-saves.h"
# include "backends/timer/default/default-timer.h"
2020-02-26 08:23:47 +00:00
# include "backends/keymapper/keymapper.h"
# include "backends/keymapper/keymapper-defaults.h"
# include "backends/keymapper/standard-actions.h"
2019-11-12 20:01:43 +02:00
# include "backends/platform/android/jni-android.h"
2011-02-20 17:01:46 +01:00
# include "backends/platform/android/android.h"
2019-09-08 19:37:48 +01:00
# include "backends/platform/android/graphics.h"
2010-06-06 09:34:36 +00:00
2011-02-19 17:30:30 +01:00
const char * android_log_tag = " ScummVM " ;
2010-06-06 09:34:36 +00:00
2011-02-14 17:51:58 +01:00
// This replaces the bionic libc assert functions with something that
2010-06-08 13:25:00 +00:00
// actually prints the assertion failure before aborting.
2011-02-14 17:51:58 +01:00
extern " C " {
void __assert ( const char * file , int line , const char * expr ) {
2011-02-19 17:30:30 +01:00
__android_log_assert ( expr , android_log_tag ,
2011-02-19 16:18:15 +01:00
" Assertion failure: '%s' in %s:%d " ,
expr , file , line ) ;
2011-02-14 17:51:58 +01:00
}
2011-03-03 11:20:37 +01:00
void __assert2 ( const char * file , int line , const char * func ,
const char * expr ) {
2011-02-19 17:30:30 +01:00
__android_log_assert ( expr , android_log_tag ,
2011-02-19 16:18:15 +01:00
" Assertion failure: '%s' in %s:%d (%s) " ,
expr , file , line , func ) ;
2011-02-14 17:51:58 +01:00
}
2010-06-08 13:25:00 +00:00
}
2011-02-26 14:53:02 +01:00
OSystem_Android : : OSystem_Android ( int audio_sample_rate , int audio_buffer_size ) :
_audio_sample_rate ( audio_sample_rate ) ,
_audio_buffer_size ( audio_buffer_size ) ,
2011-02-19 16:18:15 +01:00
_screen_changeid ( 0 ) ,
_mixer ( 0 ) ,
2012-02-03 12:36:03 +01:00
_queuedEventTime ( 0 ) ,
2019-04-10 23:16:49 +01:00
_event_queue_lock ( 0 ) ,
2011-03-15 23:30:17 +01:00
_touch_pt_down ( ) ,
2011-03-17 20:48:44 +01:00
_touch_pt_scroll ( ) ,
2011-03-15 23:30:17 +01:00
_touch_pt_dt ( ) ,
_eventScaleX ( 100 ) ,
_eventScaleY ( 100 ) ,
// TODO put these values in some option dlg?
_touchpad_mode ( true ) ,
2011-03-17 21:00:15 +01:00
_touchpad_scale ( 66 ) ,
2011-03-15 23:30:17 +01:00
_dpad_scale ( 4 ) ,
2020-12-30 15:10:44 +02:00
// _fingersDown(0),
_firstPointerId ( - 1 ) ,
_secondPointerId ( - 1 ) ,
_thirdPointerId ( - 1 ) ,
2013-08-08 14:53:36 +02:00
_trackball_scale ( 2 ) ,
2020-02-26 08:23:47 +00:00
_joystick_scale ( 10 ) {
2011-06-07 10:50:57 +02:00
_fsFactory = new POSIXFilesystemFactory ( ) ;
2011-04-05 19:01:51 +02:00
LOGI ( " Running on: [%s] [%s] [%s] [%s] [%s] SDK:%s ABI:%s " ,
2019-09-08 19:37:48 +01:00
getSystemProperty ( " ro.product.manufacturer " ) . c_str ( ) ,
2011-04-05 19:01:51 +02:00
getSystemProperty ( " ro.product.model " ) . c_str ( ) ,
getSystemProperty ( " ro.product.brand " ) . c_str ( ) ,
2011-04-05 19:10:35 +02:00
getSystemProperty ( " ro.build.fingerprint " ) . c_str ( ) ,
2011-03-19 12:17:43 +01:00
getSystemProperty ( " ro.build.display.id " ) . c_str ( ) ,
getSystemProperty ( " ro.build.version.sdk " ) . c_str ( ) ,
getSystemProperty ( " ro.product.cpu.abi " ) . c_str ( ) ) ;
2010-06-06 09:34:36 +00:00
}
OSystem_Android : : ~ OSystem_Android ( ) {
2011-02-19 17:30:30 +01:00
ENTER ( ) ;
2019-10-11 21:48:45 +03:00
// _audiocdManager should be deleted before _mixer!
// It is normally deleted in proper order in the OSystem destructor.
// However, currently _mixer is deleted here (OSystem_Android)
// and in the ModularBackend destructor,
// hence unless _audiocdManager is deleted here first,
// it will cause a crash for the Android app (arm64 v8a) upon exit
// -- when the audio cd manager was actually used eg. audio cd test of the testbed
// FIXME: A more proper fix would probably be to:
// - delete _mixer in the base class (OSystem) after _audiocdManager (this is already the current behavior)
// - remove its deletion from OSystem_Android and ModularBackend (this is what needs to be fixed).
delete _audiocdManager ;
_audiocdManager = 0 ;
2011-02-27 20:13:48 +01:00
delete _mixer ;
2011-06-07 10:50:57 +02:00
_mixer = 0 ;
2010-06-06 09:34:36 +00:00
delete _fsFactory ;
2011-06-07 10:50:57 +02:00
_fsFactory = 0 ;
2011-10-21 14:25:53 +02:00
delete _timerManager ;
_timerManager = 0 ;
2011-02-19 16:18:15 +01:00
2020-08-20 14:56:19 +01:00
delete _event_queue_lock ;
2019-09-23 16:02:30 +03:00
delete _savefileManager ;
_savefileManager = 0 ;
2010-06-06 09:34:36 +00:00
}
2011-02-19 16:18:15 +01:00
void * OSystem_Android : : timerThreadFunc ( void * arg ) {
OSystem_Android * system = ( OSystem_Android * ) arg ;
2011-06-07 10:50:57 +02:00
DefaultTimerManager * timer = ( DefaultTimerManager * ) ( system - > _timerManager ) ;
2010-06-06 09:34:36 +00:00
2011-02-26 14:53:02 +01:00
// renice this thread to boost the audio thread
if ( setpriority ( PRIO_PROCESS , 0 , 19 ) < 0 )
2011-02-27 20:16:52 +01:00
LOGW ( " couldn't renice the timer thread " ) ;
2011-02-26 14:53:02 +01:00
2011-02-23 22:44:33 +01:00
JNI : : attachThread ( ) ;
2011-02-14 18:48:33 +01:00
2010-06-06 09:34:36 +00:00
struct timespec tv ;
tv . tv_sec = 0 ;
2011-03-22 20:29:08 +01:00
tv . tv_nsec = 10 * 1000 * 1000 ; // 10ms
2010-06-06 09:34:36 +00:00
while ( ! system - > _timer_thread_exit ) {
2011-03-02 22:26:15 +01:00
if ( JNI : : pause ) {
LOGD ( " timer thread going to sleep " ) ;
sem_wait ( & JNI : : pause_sem ) ;
LOGD ( " timer thread woke up " ) ;
}
2011-03-03 11:20:37 +01:00
2010-06-06 09:34:36 +00:00
timer - > handler ( ) ;
2011-02-19 16:18:15 +01:00
nanosleep ( & tv , 0 ) ;
2010-06-06 09:34:36 +00:00
}
2011-02-23 22:44:33 +01:00
JNI : : detachThread ( ) ;
2011-02-14 18:48:33 +01:00
2011-02-19 16:18:15 +01:00
return 0 ;
2010-06-06 09:34:36 +00:00
}
2011-02-26 14:53:02 +01:00
void * OSystem_Android : : audioThreadFunc ( void * arg ) {
JNI : : attachThread ( ) ;
OSystem_Android * system = ( OSystem_Android * ) arg ;
Audio : : MixerImpl * mixer = system - > _mixer ;
uint buf_size = system - > _audio_buffer_size ;
JNIEnv * env = JNI : : getEnv ( ) ;
jbyteArray bufa = env - > NewByteArray ( buf_size ) ;
2011-02-26 18:44:17 +01:00
bool paused = true ;
2019-07-23 17:21:09 +01:00
int offset , left , written , i ;
2011-02-26 14:53:02 +01:00
2011-02-26 18:44:17 +01:00
struct timespec tv_delay ;
tv_delay . tv_sec = 0 ;
tv_delay . tv_nsec = 20 * 1000 * 1000 ;
uint msecs_full = buf_size * 1000 / ( mixer - > getOutputRate ( ) * 2 * 2 ) ;
struct timespec tv_full ;
tv_full . tv_sec = 0 ;
tv_full . tv_nsec = msecs_full * 1000 * 1000 ;
2011-02-27 11:25:03 +01:00
uint silence_count = 33 ;
2011-02-26 14:53:02 +01:00
while ( ! system - > _audio_thread_exit ) {
2011-03-02 22:26:15 +01:00
if ( JNI : : pause ) {
2011-03-03 11:35:21 +01:00
JNI : : setAudioStop ( ) ;
paused = true ;
silence_count = 33 ;
2011-03-02 22:26:15 +01:00
LOGD ( " audio thread going to sleep " ) ;
sem_wait ( & JNI : : pause_sem ) ;
LOGD ( " audio thread woke up " ) ;
}
2011-03-03 11:20:37 +01:00
2019-07-23 17:21:09 +01:00
byte * buf = ( byte * ) env - > GetPrimitiveArrayCritical ( bufa , 0 ) ;
2011-02-26 14:53:02 +01:00
assert ( buf ) ;
2019-07-23 17:21:09 +01:00
int samples = mixer - > mixCallback ( buf , buf_size ) ;
2011-02-26 14:53:02 +01:00
2019-07-23 17:21:09 +01:00
bool silence = samples < 1 ;
2011-02-26 22:23:17 +01:00
// looks stupid, and it is, but currently there's no way to detect
// silence-only buffers from the mixer
if ( ! silence ) {
silence = true ;
for ( i = 0 ; i < samples ; i + = 2 )
// SID streams constant crap
if ( READ_UINT16 ( buf + i ) > 32 ) {
silence = false ;
break ;
}
}
2011-02-26 14:53:02 +01:00
env - > ReleasePrimitiveArrayCritical ( bufa , buf , 0 ) ;
2011-02-26 22:23:17 +01:00
if ( silence ) {
2011-02-26 18:44:17 +01:00
if ( ! paused )
silence_count + + ;
// only pause after a while to prevent toggle mania
if ( silence_count > 32 ) {
if ( ! paused ) {
LOGD ( " AudioTrack pause " ) ;
JNI : : setAudioPause ( ) ;
paused = true ;
}
nanosleep ( & tv_full , 0 ) ;
continue ;
}
}
if ( paused ) {
LOGD ( " AudioTrack play " ) ;
JNI : : setAudioPlay ( ) ;
paused = false ;
silence_count = 0 ;
}
2011-02-26 14:53:02 +01:00
offset = 0 ;
left = buf_size ;
written = 0 ;
while ( left > 0 ) {
written = JNI : : writeAudio ( env , bufa , offset , left ) ;
if ( written < 0 ) {
2011-02-27 20:16:52 +01:00
LOGE ( " AudioTrack error: %d " , written ) ;
2011-02-26 14:53:02 +01:00
break ;
}
// buffer full
if ( written < left )
2011-02-26 18:44:17 +01:00
nanosleep ( & tv_delay , 0 ) ;
2011-02-26 14:53:02 +01:00
offset + = written ;
left - = written ;
}
if ( written < 0 )
break ;
2011-03-14 20:26:35 +01:00
// prepare the next buffer, and run into the blocking AudioTrack.write
2011-02-26 14:53:02 +01:00
}
JNI : : setAudioStop ( ) ;
env - > DeleteLocalRef ( bufa ) ;
JNI : : detachThread ( ) ;
return 0 ;
}
2020-10-07 15:23:53 +03:00
//
// When launching ScummVM (from ScummVMActivity) order of business is as follows:
// 1. scummvm_main() (base/main.cpp)
// 1.1. call system.initBackend() (from scummvm_main() (base/main.cpp))
// According to comments in main.cpp:
// "Init the backend. Must take place after all config data (including the command line params) was read."
// 1.2. call setupGraphics(system); (from scummvm_main() (base/main.cpp))
// 1.3. call launcherDialog() (from scummvm_main() (base/main.cpp))
// Upon calling launcherDialog() the transient domain configuration options are cleared!
// According to comments in main.cpp:
// "Those that affect the graphics mode and the others (like bootparam etc.) should not blindly be passed to the first game launched from the launcher."
2010-06-06 09:34:36 +00:00
void OSystem_Android : : initBackend ( ) {
2011-02-19 17:30:30 +01:00
ENTER ( ) ;
2011-02-19 16:18:15 +01:00
2011-02-24 18:24:27 +01:00
_main_thread = pthread_self ( ) ;
2021-04-12 15:04:12 +03:00
// TODO Setting debug level to 3, temporarily
// only for catching the level 3 messages from the new (Apr 2021)
// gui-scale (hidpi) code
gDebugLevel = 3 ;
2020-10-07 15:23:53 +03:00
// Warning: ConfMan.registerDefault() can be used for a Session of ScummVM
// but:
// 1. The values will NOT persist to storage
// ie. they won't get saved to scummvm.ini
// 2. The values will NOT be reflected on the GUI
// and they cannot be recovered after exiting scummvm and re-launching
// Also, if after a ConfMan.registerDefault(), we subsequently use ConfMan.hasKey()
// here or anywhere else in ScummVM, it WILL NOT return true.
// As noted in ConfigManager::hasKey() implementation: (common/config_manager.cpp)
// // Search the domains in the following order:
// // 1) the transient domain,
// // 2) the active game domain (if any),
// // 3) the application domain.
// --> // The defaults domain is explicitly *not* checked. <--
//
// So for at least some of these keys,
// we need to additionally check with hasKey() if they are persisted
// and set them explicitly that way.
// TODO Maybe the registerDefault only has meaning for "savepath"
// and similar key/values retrieved from "Command Line"
// so that they won't get "nuked"
// and maintained for the duration ScummVM app session (until we exit the app)
2011-03-13 22:31:28 +01:00
ConfMan . registerDefault ( " fullscreen " , true ) ;
ConfMan . registerDefault ( " aspect_ratio " , true ) ;
2020-10-07 15:23:53 +03:00
ConfMan . registerDefault ( " filtering " , false ) ;
ConfMan . registerDefault ( " autosave_period " , 0 ) ;
// explicitly set this, since fullscreen cannot be changed from GUI
// and for Android it should be persisted (and ConfMan.hasKey("fullscreen") check should return true for it)
// Also in Options::dialogBuild() (gui/options.cpp), since Android does not have kFeatureFullscreenMode (see hasFeature() below)
// the state of the checkbox in the GUI is set to true (and disabled)
ConfMan . setBool ( " fullscreen " , true ) ;
// Aspect ratio can be changed from the GUI.
// However we set it explicitly here (in addition to the registerDefault command above)
// if it's not already set in the persistent config file
if ( ! ConfMan . hasKey ( " aspect_ratio " ) ) {
ConfMan . setBool ( " aspect_ratio " , true ) ;
}
if ( ! ConfMan . hasKey ( " filtering " ) ) {
ConfMan . setBool ( " filtering " , false ) ;
}
// Note: About the stretch mode setting
// If not explicitly set in the config file
// the default used by OSystem::setStretchMode() (common/system.h)
// is the one returned by getDefaultStretchMode() (backends/graphics/opengl-graphics.cpp)
// which currently is STRETCH_FIT
if ( ! ConfMan . hasKey ( " autosave_period " ) ) {
ConfMan . setInt ( " autosave_period " , 0 ) ;
}
2020-09-16 20:34:49 +03:00
// The swap_menu_and_back is a deprecated configuration key
2020-02-27 08:18:54 +02:00
// It is no longer relevant, after introducing the keymapper functionality
// since the behaviour of the menu and back buttons is now handled by the keymapper.
2020-09-16 20:34:49 +03:00
// We now ignore it completely
2011-03-13 22:31:28 +01:00
2011-02-27 11:23:11 +01:00
ConfMan . setBool ( " FM_high_quality " , false ) ;
ConfMan . setBool ( " FM_medium_quality " , true ) ;
2010-06-06 09:34:36 +00:00
2020-10-07 15:23:53 +03:00
// We need a relaxed delay for the remapping timeout since handling touch interface and virtual keyboard can be slow
2020-09-26 16:07:38 +03:00
// and especially in some occasions when we need to pull down (hide) the keyboard and map a system key (like the AC_Back) button.
// 8 seconds should be enough
ConfMan . registerDefault ( " remap_timeout_delay_ms " , 8000 ) ;
if ( ! ConfMan . hasKey ( " remap_timeout_delay_ms " ) ) {
ConfMan . setInt ( " remap_timeout_delay_ms " , 8000 ) ;
}
2020-01-31 17:22:28 +02:00
if ( ! ConfMan . hasKey ( " browser_lastpath " ) ) {
ConfMan . set ( " browser_lastpath " , " / " ) ;
}
2019-09-15 00:20:11 +03:00
2010-06-06 09:34:36 +00:00
// BUG: "transient" ConfMan settings get nuked by the options
2011-02-19 16:18:15 +01:00
// screen. Passing the savepath in this way makes it stick
2020-10-07 15:23:53 +03:00
// (via ConfMan.registerDefault() which is called from DefaultSaveFileManager constructor (backends/saves/default/default-saves.cpp))
2020-10-02 12:42:06 +03:00
// Note: The aforementioned bug is probably the one reported here:
// https://bugs.scummvm.org/ticket/3712
// and maybe here:
// https://bugs.scummvm.org/ticket/7389
2020-10-07 15:23:53 +03:00
// However, we do NOT set the savepath key explicitly for ConfMan
// and thus the savepath will only be persisted as "default" config
// for the rest of the app session (until exit).
// It will NOT be reflected on the GUI, if it's not set explicitly by the user there
// TODO Why do we need it not shown on the GUI though?
// Btw, this is a ScummVM thing, the "defaults" do not show they values on our GUI)
2011-06-07 10:50:57 +02:00
_savefileManager = new DefaultSaveFileManager ( ConfMan . get ( " savepath " ) ) ;
2020-01-31 17:22:28 +02:00
// TODO remove the debug message eventually
LOGD ( " Setting DefaultSaveFileManager path to: %s " , ConfMan . get ( " savepath " ) . c_str ( ) ) ;
2019-04-10 23:16:49 +01:00
_mutexManager = new PthreadMutexManager ( ) ;
2011-06-07 10:50:57 +02:00
_timerManager = new DefaultTimerManager ( ) ;
2010-06-06 09:34:36 +00:00
2020-08-20 14:56:19 +01:00
_event_queue_lock = new Common : : Mutex ( ) ;
2019-04-10 23:16:49 +01:00
2011-02-19 16:18:15 +01:00
gettimeofday ( & _startTime , 0 ) ;
2010-06-06 09:34:36 +00:00
2019-11-30 13:38:06 +00:00
_mixer = new Audio : : MixerImpl ( _audio_sample_rate ) ;
2010-06-06 09:34:36 +00:00
_mixer - > setReady ( true ) ;
_timer_thread_exit = false ;
2011-02-19 16:18:15 +01:00
pthread_create ( & _timer_thread , 0 , timerThreadFunc , this ) ;
2010-06-06 09:34:36 +00:00
2011-02-26 14:53:02 +01:00
_audio_thread_exit = false ;
pthread_create ( & _audio_thread , 0 , audioThreadFunc , this ) ;
2019-09-08 19:37:48 +01:00
_graphicsManager = new AndroidGraphicsManager ( ) ;
2011-03-07 21:49:51 +01:00
2011-02-26 14:53:02 +01:00
// renice this thread to boost the audio thread
if ( setpriority ( PRIO_PROCESS , 0 , 19 ) < 0 )
warning ( " couldn't renice the main thread " ) ;
2011-02-27 20:13:48 +01:00
JNI : : setReadyForEvents ( true ) ;
2011-06-06 15:02:33 +02:00
2020-08-27 23:05:45 +01:00
_eventManager = new DefaultEventManager ( this ) ;
_audiocdManager = new DefaultAudioCDManager ( ) ;
2020-08-02 00:05:25 +01:00
BaseBackend : : initBackend ( ) ;
2010-06-06 09:34:36 +00:00
}
bool OSystem_Android : : hasFeature ( Feature f ) {
2020-10-07 15:23:53 +03:00
if ( f = = kFeatureFullscreenMode )
return false ;
2019-09-08 19:37:48 +01:00
if ( f = = kFeatureVirtualKeyboard | |
2018-07-27 19:01:06 +01:00
f = = kFeatureOpenUrl | |
2021-04-12 15:04:12 +03:00
f = = kFeatureClipboardSupport | |
f = = OSystem : : kFeatureHiDPI ) {
2019-09-08 19:37:48 +01:00
return true ;
}
2020-08-02 00:05:25 +01:00
return ModularGraphicsBackend : : hasFeature ( f ) ;
2010-06-06 09:34:36 +00:00
}
void OSystem_Android : : setFeatureState ( Feature f , bool enable ) {
2011-02-19 17:30:30 +01:00
ENTER ( " %d, %d " , f , enable ) ;
2011-02-19 16:18:15 +01:00
2010-06-06 09:34:36 +00:00
switch ( f ) {
case kFeatureVirtualKeyboard :
_virtkeybd_on = enable ;
2020-10-14 17:05:56 +01:00
JNI : : showVirtualKeyboard ( enable ) ;
2010-06-06 09:34:36 +00:00
break ;
default :
2020-08-02 00:05:25 +01:00
ModularGraphicsBackend : : setFeatureState ( f , enable ) ;
2010-06-06 09:34:36 +00:00
break ;
}
}
bool OSystem_Android : : getFeatureState ( Feature f ) {
switch ( f ) {
case kFeatureVirtualKeyboard :
return _virtkeybd_on ;
2021-04-12 15:04:12 +03:00
case OSystem : : kFeatureHiDPI :
return true ;
2010-06-06 09:34:36 +00:00
default :
2020-08-02 00:05:25 +01:00
return ModularGraphicsBackend : : getFeatureState ( f ) ;
2010-06-06 09:34:36 +00:00
}
}
2020-10-25 02:29:39 +03:00
// TODO Re-eval if we need this here
Common : : HardwareInputSet * OSystem_Android : : getHardwareInputSet ( ) {
using namespace Common ;
CompositeHardwareInputSet * inputSet = new CompositeHardwareInputSet ( ) ;
inputSet - > addHardwareInputSet ( new MouseHardwareInputSet ( defaultMouseButtons ) ) ;
inputSet - > addHardwareInputSet ( new KeyboardHardwareInputSet ( defaultKeys , defaultModifiers ) ) ;
inputSet - > addHardwareInputSet ( new JoystickHardwareInputSet ( defaultJoystickButtons , defaultJoystickAxes ) ) ;
return inputSet ;
}
// TODO Re-eval if we need this here
Common : : KeymapArray OSystem_Android : : getGlobalKeymaps ( ) {
Common : : KeymapArray globalMaps = BaseBackend : : getGlobalKeymaps ( ) ;
return globalMaps ;
}
2020-02-26 08:23:47 +00:00
Common : : KeymapperDefaultBindings * OSystem_Android : : getKeymapperDefaultBindings ( ) {
Common : : KeymapperDefaultBindings * keymapperDefaultBindings = new Common : : KeymapperDefaultBindings ( ) ;
2020-02-27 08:18:54 +02:00
// The swap_menu_and_back is a legacy configuration key
2020-09-16 20:34:49 +03:00
// We now ignore it entirely (it as always false -- ie. back short press is AC_BACK)
2020-10-03 12:26:38 +03:00
//
// Note: setDefaultBinding maps a hw input to a keymapId_actionId combo.
//
// Clarifications/Quote by developer bgK (via Discord, Oct 3, 2020)
// bgK: [With the introduction of the ScummVM keymapper we have] "standard actions" defined in "standard-actions.h".
// The engines use those as much as possible when defining keymaps.
// Then, the backends can override the default bindings to make use of the platform specific keys.
//
//
2020-09-16 20:34:49 +03:00
keymapperDefaultBindings - > setDefaultBinding ( Common : : kGlobalKeymapName , " MENU " , " MENU " ) ;
2020-10-03 12:26:38 +03:00
//
// We want the AC_BACK key to be the default (until overridden explicitly by the user or a game engine)
// mapped key for the standard SKIP action.
//
// bgK: "engine-default" is for the default keymap used by games that don't define their own keymap.
// [We] want Common::kStandardActionsKeymapName to override the action for all the keymaps
// Common::kStandardActionsKeymapName is used as a fallback if there are no keymap specific bindings defined.
// So it should be enough on its own.
// [ie. we don't have to set default binding for "engine-default", as well]
// ["engine-default" is used for to create a Keymap sequence of type kKeymapTypeGame in engines/metaengine.cpp initKeymaps() for an engine]
// [In initKeymaps() is where the default key Esc is mapped to Skip action for game engines]
//
// [kStandardActionsKeymapName is defined as (constant char*) in ./backends/keymapper/keymap, and utilised in getActionDefaultMappings()]
// ["If no keymap-specific default mapping was found, look for a standard action binding"]
keymapperDefaultBindings - > setDefaultBinding ( Common : : kStandardActionsKeymapName , Common : : kStandardActionSkip , " AC_BACK " ) ;
// The "CLOS" action ID is not a typo.
// See: backends/keymapper/remap-widget.cpp: kCloseCmd = 'CLOS'
2020-09-16 20:34:49 +03:00
keymapperDefaultBindings - > setDefaultBinding ( Common : : kGuiKeymapName , " CLOS " , " AC_BACK " ) ;
2020-02-26 08:23:47 +00:00
return keymapperDefaultBindings ;
}
2013-05-17 00:18:09 +03:00
uint32 OSystem_Android : : getMillis ( bool skipRecord ) {
2010-06-06 09:34:36 +00:00
timeval curTime ;
2011-02-19 16:18:15 +01:00
gettimeofday ( & curTime , 0 ) ;
2011-03-14 23:44:43 +01:00
return ( uint32 ) ( ( ( curTime . tv_sec - _startTime . tv_sec ) * 1000 ) +
2010-06-06 09:34:36 +00:00
( ( curTime . tv_usec - _startTime . tv_usec ) / 1000 ) ) ;
}
void OSystem_Android : : delayMillis ( uint msecs ) {
usleep ( msecs * 1000 ) ;
}
void OSystem_Android : : quit ( ) {
2011-02-19 17:30:30 +01:00
ENTER ( ) ;
2010-06-06 09:34:36 +00:00
2011-02-27 20:13:48 +01:00
JNI : : setReadyForEvents ( false ) ;
2011-02-26 14:53:02 +01:00
_audio_thread_exit = true ;
pthread_join ( _audio_thread , 0 ) ;
2010-06-06 09:34:36 +00:00
_timer_thread_exit = true ;
2011-02-19 16:18:15 +01:00
pthread_join ( _timer_thread , 0 ) ;
2010-06-06 09:34:36 +00:00
}
2020-11-01 21:15:34 +00:00
void OSystem_Android : : setWindowCaption ( const Common : : U32String & caption ) {
2011-02-24 00:45:41 +01:00
JNI : : setWindowCaption ( caption ) ;
2010-06-06 09:34:36 +00:00
}
Audio : : Mixer * OSystem_Android : : getMixer ( ) {
assert ( _mixer ) ;
return _mixer ;
}
2021-07-08 19:31:13 +02:00
void OSystem_Android : : getTimeAndDate ( TimeDate & td , bool skipRecord ) const {
2010-06-06 09:34:36 +00:00
struct tm tm ;
2011-02-19 16:18:15 +01:00
const time_t curTime = time ( 0 ) ;
2010-06-06 09:34:36 +00:00
localtime_r ( & curTime , & tm ) ;
td . tm_sec = tm . tm_sec ;
td . tm_min = tm . tm_min ;
td . tm_hour = tm . tm_hour ;
td . tm_mday = tm . tm_mday ;
td . tm_mon = tm . tm_mon ;
td . tm_year = tm . tm_year ;
2012-06-09 20:20:19 -04:00
td . tm_wday = tm . tm_wday ;
2010-06-06 09:34:36 +00:00
}
2019-09-08 19:37:48 +01:00
void OSystem_Android : : addSysArchivesToSearchSet ( Common : : SearchSet & s , int priority ) {
2011-02-24 00:45:41 +01:00
ENTER ( " " ) ;
2011-02-19 16:18:15 +01:00
2011-02-24 00:45:41 +01:00
JNI : : addSysArchivesToSearchSet ( s , priority ) ;
2010-06-06 09:34:36 +00:00
}
2019-09-08 19:37:48 +01:00
void OSystem_Android : : logMessage ( LogMessageType : : Type type , const char * message ) {
2010-11-18 19:12:14 +00:00
switch ( type ) {
2011-06-06 12:26:01 +02:00
case LogMessageType : : kInfo :
__android_log_write ( ANDROID_LOG_INFO , android_log_tag , message ) ;
break ;
2010-11-18 19:12:14 +00:00
case LogMessageType : : kDebug :
2011-02-19 17:30:30 +01:00
__android_log_write ( ANDROID_LOG_DEBUG , android_log_tag , message ) ;
2010-11-18 19:12:14 +00:00
break ;
case LogMessageType : : kWarning :
2011-02-19 17:30:30 +01:00
__android_log_write ( ANDROID_LOG_WARN , android_log_tag , message ) ;
2010-11-18 19:12:14 +00:00
break ;
case LogMessageType : : kError :
2011-02-19 17:30:30 +01:00
__android_log_write ( ANDROID_LOG_ERROR , android_log_tag , message ) ;
2010-11-18 19:12:14 +00:00
break ;
}
}
2010-06-06 09:34:36 +00:00
2011-03-19 12:36:27 +01:00
Common : : String OSystem_Android : : getSystemLanguage ( ) const {
return Common : : String : : format ( " %s_%s " ,
getSystemProperty ( " persist.sys.language " ) . c_str ( ) ,
getSystemProperty ( " persist.sys.country " ) . c_str ( ) ) ;
}
2016-09-09 23:51:40 +01:00
bool OSystem_Android : : openUrl ( const Common : : String & url ) {
2020-06-25 23:00:49 +01:00
return JNI : : openUrl ( url ) ;
2016-09-09 23:51:40 +01:00
}
2018-10-02 14:57:48 +01:00
bool OSystem_Android : : hasTextInClipboard ( ) {
return JNI : : hasTextInClipboard ( ) ;
}
2020-08-19 10:07:36 +05:30
Common : : U32String OSystem_Android : : getTextFromClipboard ( ) {
2018-10-02 14:57:48 +01:00
return JNI : : getTextFromClipboard ( ) ;
}
2020-08-19 10:07:36 +05:30
bool OSystem_Android : : setTextInClipboard ( const Common : : U32String & text ) {
2018-10-02 14:57:48 +01:00
return JNI : : setTextInClipboard ( text ) ;
}
2019-06-20 21:14:04 +01:00
bool OSystem_Android : : isConnectionLimited ( ) {
return JNI : : isConnectionLimited ( ) ;
}
2011-03-19 12:17:43 +01:00
Common : : String OSystem_Android : : getSystemProperty ( const char * name ) const {
char value [ PROP_VALUE_MAX ] ;
int len = __system_property_get ( name , value ) ;
return Common : : String ( value , len ) ;
}
2010-06-06 09:34:36 +00:00
# endif