Content browser (WIP)
Get rid of the renderer thread. It is now the main/UI thread on all platforms. The emulator runs in a separate thread. Content browser displayed at startup.
This commit is contained in:
parent
56c0358eaf
commit
492e771272
|
@ -21,7 +21,7 @@ void savecfgf()
|
|||
FILE* cfgfile = fopen(cfgPath.c_str(),"wt");
|
||||
if (!cfgfile)
|
||||
{
|
||||
printf("Error : Unable to open file for saving \n");
|
||||
printf("Error: Unable to open file '%s' for saving\n", cfgPath.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -76,6 +76,10 @@ void cfgSaveStr(const wchar * Section, const wchar * Key, const wchar * String)
|
|||
|
||||
bool cfgOpen()
|
||||
{
|
||||
if (get_writable_config_path("").empty())
|
||||
// Config dir not set (android onboarding)
|
||||
return false;
|
||||
|
||||
const char* filename = "/emu.cfg";
|
||||
string config_path_read = get_readonly_config_path(filename);
|
||||
cfgPath = get_writable_config_path(filename);
|
||||
|
|
|
@ -461,6 +461,9 @@ bool naomi_cart_LoadRom(char* file)
|
|||
|
||||
if (RomCacheMap)
|
||||
{
|
||||
for (int i = 0; i < RomCacheMapCount; i++)
|
||||
if (RomCacheMap[i] != INVALID_FD)
|
||||
close(RomCacheMap[i]);
|
||||
RomCacheMapCount = 0;
|
||||
delete[] RomCacheMap;
|
||||
}
|
||||
|
|
|
@ -270,6 +270,8 @@ bool rend_single_frame()
|
|||
//wait render start only if no frame pending
|
||||
do
|
||||
{
|
||||
// FIXME not here
|
||||
os_DoEvents();
|
||||
#if !defined(TARGET_NO_THREADS)
|
||||
if (gui_is_open())
|
||||
{
|
||||
|
@ -285,12 +287,8 @@ bool rend_single_frame()
|
|||
if (renderer != NULL)
|
||||
renderer->RenderLastFrame();
|
||||
|
||||
#if defined(_ANDROID)
|
||||
if (!rs.Wait(100))
|
||||
return false;
|
||||
#else
|
||||
rs.Wait();
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
if (gui_is_open())
|
||||
|
@ -378,6 +376,7 @@ void rend_term_renderer()
|
|||
delete fallback_renderer;
|
||||
fallback_renderer = NULL;
|
||||
}
|
||||
tactx_Term();
|
||||
}
|
||||
|
||||
void* rend_thread(void* p)
|
||||
|
@ -386,33 +385,6 @@ void* rend_thread(void* p)
|
|||
install_prof_handler(1);
|
||||
#endif
|
||||
|
||||
#if SET_AFNT
|
||||
cpu_set_t mask;
|
||||
|
||||
/* CPU_ZERO initializes all the bits in the mask to zero. */
|
||||
|
||||
CPU_ZERO( &mask );
|
||||
|
||||
|
||||
|
||||
/* CPU_SET sets only the bit corresponding to cpu. */
|
||||
|
||||
CPU_SET( 1, &mask );
|
||||
|
||||
|
||||
|
||||
/* sched_setaffinity returns 0 in success */
|
||||
|
||||
if( sched_setaffinity( 0, sizeof(mask), &mask ) == -1 )
|
||||
|
||||
{
|
||||
|
||||
printf("WARNING: Could not set CPU Affinity, continuing...\n");
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
rend_init_renderer();
|
||||
|
||||
//we don't know if this is true, so let's not speculate here
|
||||
|
@ -436,10 +408,6 @@ void* rend_thread(void* p)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
#if !defined(TARGET_NO_THREADS)
|
||||
cThread rthd(rend_thread,0);
|
||||
#endif
|
||||
|
||||
bool pend_rend = false;
|
||||
|
||||
void rend_resize(int width, int height) {
|
||||
|
@ -570,79 +538,9 @@ void rend_end_render()
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
void rend_end_wait()
|
||||
{
|
||||
#if HOST_OS!=OS_WINDOWS && !defined(_ANDROID)
|
||||
// if (!re.state) printf("Render End: Waiting ...\n");
|
||||
#endif
|
||||
re.Wait();
|
||||
pvrrc.InUse=false;
|
||||
}
|
||||
*/
|
||||
|
||||
bool rend_init()
|
||||
{
|
||||
rend_create_renderer();
|
||||
|
||||
#if !defined(_ANDROID) && HOST_OS != OS_DARWIN
|
||||
#if !defined(TARGET_NO_THREADS)
|
||||
rthd.Start();
|
||||
#else
|
||||
rend_init_renderer();
|
||||
|
||||
renderer->Resize(640, 480);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if SET_AFNT
|
||||
cpu_set_t mask;
|
||||
|
||||
|
||||
|
||||
/* CPU_ZERO initializes all the bits in the mask to zero. */
|
||||
|
||||
CPU_ZERO( &mask );
|
||||
|
||||
|
||||
|
||||
/* CPU_SET sets only the bit corresponding to cpu. */
|
||||
|
||||
CPU_SET( 0, &mask );
|
||||
|
||||
|
||||
|
||||
/* sched_setaffinity returns 0 in success */
|
||||
|
||||
if( sched_setaffinity( 0, sizeof(mask), &mask ) == -1 )
|
||||
|
||||
{
|
||||
|
||||
printf("WARNING: Could not set CPU Affinity, continuing...\n");
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void rend_term()
|
||||
void rend_stop_renderer()
|
||||
{
|
||||
renderer_enabled = false;
|
||||
#if !defined(TARGET_NO_THREADS)
|
||||
rs.Set();
|
||||
#endif
|
||||
|
||||
if (fCheckFrames)
|
||||
fclose(fCheckFrames);
|
||||
|
||||
if (fLogFrames)
|
||||
fclose(fLogFrames);
|
||||
|
||||
#if !defined(TARGET_NO_THREADS)
|
||||
rthd.WaitToEnd();
|
||||
#endif
|
||||
tactx_Term();
|
||||
}
|
||||
|
||||
void rend_vblank()
|
||||
|
@ -656,8 +554,6 @@ void rend_vblank()
|
|||
}
|
||||
render_called = false;
|
||||
check_framebuffer_write();
|
||||
|
||||
os_DoEvents();
|
||||
}
|
||||
|
||||
void check_framebuffer_write()
|
||||
|
|
|
@ -5,14 +5,14 @@
|
|||
extern u32 VertexCount;
|
||||
extern u32 FrameCount;
|
||||
|
||||
bool rend_init();
|
||||
void rend_term();
|
||||
|
||||
void rend_init_renderer();
|
||||
void rend_term_renderer();
|
||||
void rend_stop_renderer();
|
||||
void rend_vblank();
|
||||
void rend_start_render();
|
||||
void rend_end_render();
|
||||
void rend_end_wait();
|
||||
void rend_cancel_emu_wait();
|
||||
bool rend_single_frame();
|
||||
|
||||
void rend_set_fb_scale(float x,float y);
|
||||
void rend_resize(int width, int height);
|
||||
|
|
|
@ -28,7 +28,6 @@ void libPvr_Reset(bool Manual)
|
|||
{
|
||||
Regs_Reset(Manual);
|
||||
spg_Reset(Manual);
|
||||
//rend_reset(); //*TODO* wtf ?
|
||||
}
|
||||
|
||||
s32 libPvr_Init()
|
||||
|
@ -38,11 +37,6 @@ s32 libPvr_Init()
|
|||
//failed
|
||||
return rv_error;
|
||||
}
|
||||
if (!rend_init())
|
||||
{
|
||||
//failed
|
||||
return rv_error;
|
||||
}
|
||||
|
||||
return rv_ok;
|
||||
}
|
||||
|
@ -50,6 +44,5 @@ s32 libPvr_Init()
|
|||
//called when exiting from sh4 thread , from the new thread context (for any thread specific de init) :P
|
||||
void libPvr_Term()
|
||||
{
|
||||
rend_term();
|
||||
spg_Term();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -300,6 +300,7 @@ void tactx_Term()
|
|||
ctx_list[i]->Free();
|
||||
delete ctx_list[i];
|
||||
}
|
||||
ctx_list.clear();
|
||||
mtx_pool.Lock();
|
||||
{
|
||||
for (size_t i = 0; i < ctx_pool.size(); i++)
|
||||
|
@ -308,5 +309,6 @@ void tactx_Term()
|
|||
delete ctx_pool[i];
|
||||
}
|
||||
}
|
||||
ctx_pool.clear();
|
||||
mtx_pool.Unlock();
|
||||
}
|
||||
|
|
|
@ -195,8 +195,8 @@ void ExecuteDelayslot_RTE()
|
|||
#define AICA_SAMPLE_GCM 441
|
||||
#define AICA_SAMPLE_CYCLES (SH4_MAIN_CLOCK/(44100/AICA_SAMPLE_GCM)*32)
|
||||
|
||||
int aica_schid;
|
||||
int rtc_schid;
|
||||
int aica_schid = -1;
|
||||
int rtc_schid = -1;
|
||||
|
||||
//14336 Cycles
|
||||
|
||||
|
@ -281,11 +281,14 @@ void Sh4_int_Init()
|
|||
{
|
||||
verify(sizeof(Sh4cntx)==448);
|
||||
|
||||
aica_schid=sh4_sched_register(0,&AicaUpdate);
|
||||
sh4_sched_request(aica_schid,AICA_TICK);
|
||||
if (aica_schid == -1)
|
||||
{
|
||||
aica_schid=sh4_sched_register(0,&AicaUpdate);
|
||||
sh4_sched_request(aica_schid,AICA_TICK);
|
||||
|
||||
rtc_schid=sh4_sched_register(0,&DreamcastSecond);
|
||||
sh4_sched_request(rtc_schid,SH4_MAIN_CLOCK);
|
||||
rtc_schid=sh4_sched_register(0,&DreamcastSecond);
|
||||
sh4_sched_request(rtc_schid,SH4_MAIN_CLOCK);
|
||||
}
|
||||
memset(&p_sh4rcb->cntx, 0, sizeof(p_sh4rcb->cntx));
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include "gamepad_device.h"
|
||||
#include "rend/gui.h"
|
||||
|
||||
extern void dc_stop();
|
||||
extern void dc_exit();
|
||||
|
||||
extern u16 kcode[4];
|
||||
extern u8 rt[4], lt[4];
|
||||
|
@ -56,7 +56,7 @@ bool GamepadDevice::gamepad_btn_input(u32 code, bool pressed)
|
|||
{
|
||||
case EMU_BTN_ESCAPE:
|
||||
if (pressed)
|
||||
dc_stop();
|
||||
dc_exit();
|
||||
break;
|
||||
case EMU_BTN_MENU:
|
||||
if (pressed)
|
||||
|
|
|
@ -179,9 +179,9 @@ void os_CreateWindow()
|
|||
}
|
||||
|
||||
void common_linux_setup();
|
||||
int dc_init(int argc,wchar* argv[]);
|
||||
void dc_run();
|
||||
int reicast_init(int argc, char* argv[]);
|
||||
void dc_term();
|
||||
void* rend_thread(void* p);
|
||||
|
||||
#ifdef TARGET_PANDORA
|
||||
void gl_term();
|
||||
|
@ -391,14 +391,14 @@ int main(int argc, wchar* argv[])
|
|||
|
||||
settings.profile.run_counts=0;
|
||||
|
||||
if (dc_init(argc,argv))
|
||||
if (reicast_init(argc, argv))
|
||||
die("Reicast initialization failed\n");
|
||||
|
||||
#if !defined(TARGET_EMSCRIPTEN)
|
||||
#if FEAT_HAS_NIXPROF
|
||||
install_prof_handler(0);
|
||||
#endif
|
||||
dc_run();
|
||||
rend_thread(NULL);
|
||||
#else
|
||||
emscripten_set_main_loop(&dc_run, 100, false);
|
||||
#endif
|
||||
|
|
|
@ -86,7 +86,7 @@ extern bool dump_frame_switch;
|
|||
extern bool naomi_test_button;
|
||||
extern bool coin_chute;
|
||||
|
||||
void dc_stop(void);
|
||||
void dc_exit(void);
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -122,7 +122,7 @@ void event_x11_handle()
|
|||
|
||||
if (event.type == ClientMessage &&
|
||||
event.xclient.data.l[0] == wmDeleteMessage)
|
||||
dc_stop();
|
||||
dc_exit();
|
||||
else if (event.type == ConfigureNotify)
|
||||
{
|
||||
x11_width = event.xconfigure.width;
|
||||
|
|
351
core/nullDC.cpp
351
core/nullDC.cpp
|
@ -13,7 +13,6 @@
|
|||
#include "hw/maple/maple_cfg.h"
|
||||
#include "hw/sh4/sh4_mem.h"
|
||||
|
||||
#include "webui/server.h"
|
||||
#include "hw/naomi/naomi_cart.h"
|
||||
#include "reios/reios.h"
|
||||
#include "hw/sh4/sh4_sched.h"
|
||||
|
@ -21,10 +20,12 @@
|
|||
#include "hw/pvr/spg.h"
|
||||
#include "hw/aica/dsp.h"
|
||||
#include "imgread/common.h"
|
||||
#include "rend/gui.h"
|
||||
|
||||
void FlushCache();
|
||||
void LoadCustom();
|
||||
void dc_resume_emu(bool continue_running);
|
||||
void* dc_run(void*);
|
||||
void dc_resume();
|
||||
|
||||
settings_t settings;
|
||||
// Set if game has corresponding option by default, so that it's not saved in the config
|
||||
|
@ -33,26 +34,7 @@ static bool safemode_game;
|
|||
static bool tr_poly_depth_mask_game;
|
||||
static bool extra_depth_game;
|
||||
|
||||
static bool continue_running = false;
|
||||
static cMutex mtx_mainloop ;
|
||||
static cResetEvent resume_mainloop(false, true);
|
||||
|
||||
/*
|
||||
libndc
|
||||
|
||||
//initialise (and parse the command line)
|
||||
ndc_init(argc,argv);
|
||||
|
||||
...
|
||||
//run a dreamcast slice
|
||||
//either a frame, or up to 25 ms of emulation
|
||||
//returns 1 if the frame is ready (fb needs to be flipped -- i'm looking at you android)
|
||||
ndc_step();
|
||||
|
||||
...
|
||||
//terminate (and free everything)
|
||||
ndc_term()
|
||||
*/
|
||||
cThread emu_thread(&dc_run, NULL);
|
||||
|
||||
#if HOST_OS==OS_WINDOWS
|
||||
#include <windows.h>
|
||||
|
@ -133,14 +115,14 @@ s32 plugins_Init()
|
|||
if (s32 rv = libPvr_Init())
|
||||
return rv;
|
||||
|
||||
#ifndef TARGET_DISPFRAME
|
||||
#ifndef TARGET_DISPFRAME
|
||||
if (s32 rv = libGDR_Init())
|
||||
return rv;
|
||||
#endif
|
||||
#if DC_PLATFORM == DC_PLATFORM_NAOMI || DC_PLATFORM == DC_PLATFORM_ATOMISWAVE
|
||||
#endif
|
||||
#if DC_PLATFORM == DC_PLATFORM_NAOMI || DC_PLATFORM == DC_PLATFORM_ATOMISWAVE
|
||||
if (!naomi_cart_SelectFile(libPvr_GetRenderTarget()))
|
||||
return rv_serror;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (s32 rv = libAICA_Init())
|
||||
return rv;
|
||||
|
@ -148,18 +130,12 @@ s32 plugins_Init()
|
|||
if (s32 rv = libARM_Init())
|
||||
return rv;
|
||||
|
||||
//if (s32 rv = libExtDevice_Init())
|
||||
// return rv;
|
||||
|
||||
|
||||
|
||||
return rv_ok;
|
||||
}
|
||||
|
||||
void plugins_Term()
|
||||
{
|
||||
//term all plugins
|
||||
//libExtDevice_Term();
|
||||
libARM_Term();
|
||||
libAICA_Term();
|
||||
libGDR_Term();
|
||||
|
@ -168,6 +144,7 @@ void plugins_Term()
|
|||
|
||||
void plugins_Reset(bool Manual)
|
||||
{
|
||||
reios_reset();
|
||||
libPvr_Reset(Manual);
|
||||
libGDR_Reset(Manual);
|
||||
libAICA_Reset(Manual);
|
||||
|
@ -175,17 +152,6 @@ void plugins_Reset(bool Manual)
|
|||
//libExtDevice_Reset(Manual);
|
||||
}
|
||||
|
||||
#if !defined(TARGET_NO_WEBUI) && !defined(TARGET_NO_THREADS)
|
||||
|
||||
void* webui_th(void* p)
|
||||
{
|
||||
webui_start();
|
||||
return 0;
|
||||
}
|
||||
|
||||
cThread webui_thd(&webui_th,0);
|
||||
#endif
|
||||
|
||||
void LoadSpecialSettings()
|
||||
{
|
||||
#if DC_PLATFORM == DC_PLATFORM_DREAMCAST
|
||||
|
@ -308,48 +274,60 @@ void dc_reset()
|
|||
|
||||
static bool init_done;
|
||||
|
||||
int dc_init(int argc,wchar* argv[])
|
||||
int reicast_init(int argc, char* argv[])
|
||||
{
|
||||
setbuf(stdin,0);
|
||||
setbuf(stdout,0);
|
||||
setbuf(stderr,0);
|
||||
if (init_done)
|
||||
{
|
||||
if(ParseCommandLine(argc,argv))
|
||||
{
|
||||
return 69;
|
||||
}
|
||||
InitSettings();
|
||||
LoadSettings(false);
|
||||
if (DiscSwap())
|
||||
LoadCustom();
|
||||
dc_reset();
|
||||
|
||||
return 0;
|
||||
}
|
||||
if (!_vmem_reserve())
|
||||
{
|
||||
printf("Failed to alloc mem\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if !defined(TARGET_NO_WEBUI) && !defined(TARGET_NO_THREADS)
|
||||
webui_thd.Start();
|
||||
#endif
|
||||
|
||||
if(ParseCommandLine(argc,argv))
|
||||
if (ParseCommandLine(argc, argv))
|
||||
{
|
||||
return 69;
|
||||
}
|
||||
if(!cfgOpen())
|
||||
{
|
||||
msgboxf("Unable to open config file",MBX_ICONERROR);
|
||||
return -4;
|
||||
}
|
||||
InitSettings();
|
||||
LoadSettings(false);
|
||||
if (!cfgOpen())
|
||||
{
|
||||
printf("Config directory is not set. Starting onboarding\n");
|
||||
gui_open_onboarding();
|
||||
}
|
||||
else
|
||||
LoadSettings(false);
|
||||
|
||||
os_CreateWindow();
|
||||
os_SetupInput();
|
||||
|
||||
// Needed to avoid crash calling dc_is_running() in gui
|
||||
Get_Sh4Interpreter(&sh4_cpu);
|
||||
sh4_cpu.Init();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool game_started;
|
||||
|
||||
int dc_start_game(const char *path)
|
||||
{
|
||||
cfgSetVirtual("config", "image", path);
|
||||
|
||||
if (init_done)
|
||||
{
|
||||
InitSettings();
|
||||
LoadSettings(false);
|
||||
#if DC_PLATFORM == DC_PLATFORM_DREAMCAST
|
||||
if (DiscSwap())
|
||||
LoadCustom();
|
||||
#elif DC_PLATFORM == DC_PLATFORM_NAOMI || DC_PLATFORM == DC_PLATFORM_ATOMISWAVE
|
||||
if (!naomi_cart_SelectFile(libPvr_GetRenderTarget()))
|
||||
return rv_serror;
|
||||
#endif
|
||||
dc_reset();
|
||||
|
||||
game_started = true;
|
||||
dc_resume();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if HOST_OS != OS_DARWIN
|
||||
#define DATA_PATH "/data/"
|
||||
|
@ -357,6 +335,7 @@ int dc_init(int argc,wchar* argv[])
|
|||
#define DATA_PATH "/"
|
||||
#endif
|
||||
|
||||
settings.dreamcast.RTC = GetRTC_now(); // FIXME This shouldn't be in settings anymore
|
||||
if (settings.bios.UseReios || !LoadRomFiles(get_readonly_data_path(DATA_PATH)))
|
||||
{
|
||||
#ifdef USE_REIOS
|
||||
|
@ -375,9 +354,7 @@ int dc_init(int argc,wchar* argv[])
|
|||
}
|
||||
|
||||
if (plugins_Init())
|
||||
{
|
||||
return -3;
|
||||
}
|
||||
|
||||
LoadCustom();
|
||||
|
||||
|
@ -392,20 +369,14 @@ int dc_init(int argc,wchar* argv[])
|
|||
#endif
|
||||
{
|
||||
Get_Sh4Interpreter(&sh4_cpu);
|
||||
#if FEAT_SHREC == DYNAREC_NONE
|
||||
sh4_cpu.Init();
|
||||
#endif
|
||||
printf("Using Interpreter\n");
|
||||
}
|
||||
|
||||
InitAudio();
|
||||
|
||||
mem_Init();
|
||||
|
||||
mem_map_default();
|
||||
|
||||
os_SetupInput();
|
||||
|
||||
#if DC_PLATFORM == DC_PLATFORM_NAOMI
|
||||
mcfg_CreateNAOMIJamma();
|
||||
#elif DC_PLATFORM == DC_PLATFORM_ATOMISWAVE
|
||||
|
@ -415,6 +386,9 @@ int dc_init(int argc,wchar* argv[])
|
|||
|
||||
dc_reset();
|
||||
|
||||
game_started = true;
|
||||
dc_resume();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -424,46 +398,26 @@ bool dc_is_running()
|
|||
}
|
||||
|
||||
#ifndef TARGET_DISPFRAME
|
||||
void dc_run()
|
||||
void* dc_run(void*)
|
||||
{
|
||||
resume_mainloop.Set();
|
||||
InitAudio();
|
||||
|
||||
while ( true )
|
||||
{
|
||||
bool dynarec_enabled = settings.dynarec.Enable;
|
||||
continue_running = false ;
|
||||
mtx_mainloop.Lock() ;
|
||||
sh4_cpu.Run();
|
||||
mtx_mainloop.Unlock() ;
|
||||
if (settings.dynarec.Enable)
|
||||
{
|
||||
Get_Sh4Recompiler(&sh4_cpu);
|
||||
printf("Using Recompiler\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
Get_Sh4Interpreter(&sh4_cpu);
|
||||
printf("Using Interpreter\n");
|
||||
}
|
||||
sh4_cpu.Run();
|
||||
|
||||
#ifdef _WIN32
|
||||
// Avoid the looping audio when the emulator is paused
|
||||
TermAudio();
|
||||
#endif
|
||||
while (!resume_mainloop.Wait(20))
|
||||
os_DoEvents();
|
||||
resume_mainloop.Set();
|
||||
SaveRomFiles(get_writable_data_path("/data/"));
|
||||
TermAudio();
|
||||
|
||||
if (dynarec_enabled != settings.dynarec.Enable)
|
||||
{
|
||||
if (settings.dynarec.Enable)
|
||||
{
|
||||
Get_Sh4Recompiler(&sh4_cpu);
|
||||
printf("Using Recompiler\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
Get_Sh4Interpreter(&sh4_cpu);
|
||||
printf("Using Interpreter\n");
|
||||
}
|
||||
sh4_cpu.ResetCache();
|
||||
}
|
||||
if (!continue_running)
|
||||
break ;
|
||||
#ifdef _WIN32
|
||||
InitAudio();
|
||||
#endif
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -476,36 +430,19 @@ void dc_term()
|
|||
mcfg_DestroyDevices();
|
||||
|
||||
SaveSettings();
|
||||
SaveRomFiles(get_writable_data_path("/data/"));
|
||||
|
||||
TermAudio();
|
||||
|
||||
#if !defined(TARGET_NO_WEBUI) && !defined(TARGET_NO_THREADS)
|
||||
extern void sighandler(int sig);
|
||||
sighandler(0);
|
||||
webui_thd.WaitToEnd();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#if defined(_ANDROID)
|
||||
void dc_pause()
|
||||
{
|
||||
SaveRomFiles(get_writable_data_path("/data/"));
|
||||
}
|
||||
#endif
|
||||
|
||||
void dc_stop()
|
||||
{
|
||||
if (sh4_cpu.IsCpuRunning())
|
||||
sh4_cpu.Stop();
|
||||
else
|
||||
dc_resume_emu(false);
|
||||
sh4_cpu.Stop();
|
||||
rend_cancel_emu_wait();
|
||||
emu_thread.WaitToEnd();
|
||||
}
|
||||
|
||||
void dc_start()
|
||||
void dc_exit()
|
||||
{
|
||||
sh4_cpu.Start();
|
||||
dc_stop();
|
||||
rend_stop_renderer();
|
||||
}
|
||||
|
||||
void InitSettings()
|
||||
|
@ -659,6 +596,23 @@ void LoadSettings(bool game_specific)
|
|||
settings.omx.Audio_HDMI = cfgLoadBool(game_specific ? cfgGetGameId() : "omx", "audio_hdmi", settings.omx.Audio_HDMI);
|
||||
#endif
|
||||
|
||||
if (!game_specific)
|
||||
{
|
||||
settings.dreamcast.ContentPath.clear();
|
||||
std::string paths = cfgLoadStr(config_section, "Dreamcast.ContentPath", "");
|
||||
std::string::size_type start = 0;
|
||||
while (true)
|
||||
{
|
||||
std::string::size_type end = paths.find(';', start);
|
||||
if (end == std::string::npos)
|
||||
end = paths.size();
|
||||
if (start != end)
|
||||
settings.dreamcast.ContentPath.push_back(paths.substr(start, end - start));
|
||||
if (end == paths.size())
|
||||
break;
|
||||
start = end + 1;
|
||||
}
|
||||
}
|
||||
/*
|
||||
//make sure values are valid
|
||||
settings.dreamcast.cable = min(max(settings.dreamcast.cable, 0),3);
|
||||
|
@ -734,74 +688,24 @@ void SaveSettings()
|
|||
sprintf(device_name, "device%d.2", i + 1);
|
||||
cfgSaveInt("input", device_name, (s32)settings.input.maple_expansion_devices[i][1]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static bool wait_until_dc_running()
|
||||
{
|
||||
int64_t start_time = get_time_usec() ;
|
||||
const int64_t FIVE_SECONDS = 5*1000000 ;
|
||||
while(!dc_is_running())
|
||||
// FIXME This should never be a game-specific setting
|
||||
std::string paths;
|
||||
for (auto path : settings.dreamcast.ContentPath)
|
||||
{
|
||||
if ( start_time+FIVE_SECONDS < get_time_usec() )
|
||||
{
|
||||
//timeout elapsed - dc not getting a chance to run - just bail
|
||||
return false ;
|
||||
}
|
||||
if (!paths.empty())
|
||||
paths += ";";
|
||||
paths += path;
|
||||
}
|
||||
return true ;
|
||||
}
|
||||
|
||||
static bool acquire_mainloop_lock()
|
||||
{
|
||||
bool result = false ;
|
||||
int64_t start_time = get_time_usec() ;
|
||||
const int64_t FIVE_SECONDS = 5*1000000 ;
|
||||
|
||||
while ( ( start_time+FIVE_SECONDS > get_time_usec() ) && !(result = mtx_mainloop.TryLock()) )
|
||||
{
|
||||
rend_cancel_emu_wait() ;
|
||||
}
|
||||
|
||||
return result ;
|
||||
}
|
||||
|
||||
bool dc_pause_emu()
|
||||
{
|
||||
if (sh4_cpu.IsCpuRunning())
|
||||
{
|
||||
#ifndef TARGET_NO_THREADS
|
||||
if (!wait_until_dc_running()) {
|
||||
printf("Can't open settings - dc loop kept running\n");
|
||||
return false;
|
||||
}
|
||||
resume_mainloop.Reset();
|
||||
|
||||
dc_stop();
|
||||
|
||||
if (!acquire_mainloop_lock())
|
||||
{
|
||||
printf("Can't open settings - could not acquire main loop lock\n");
|
||||
continue_running = true;
|
||||
resume_mainloop.Set();
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
dc_stop();
|
||||
cfgSaveStr("config", "Dreamcast.ContentPath", paths.c_str());
|
||||
#ifdef _ANDROID
|
||||
void SaveAndroidSettings();
|
||||
SaveAndroidSettings();
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void dc_resume_emu(bool continue_running)
|
||||
void dc_resume()
|
||||
{
|
||||
if (!sh4_cpu.IsCpuRunning())
|
||||
{
|
||||
::continue_running = continue_running;
|
||||
rend_cancel_emu_wait();
|
||||
resume_mainloop.Set();
|
||||
mtx_mainloop.Unlock();
|
||||
}
|
||||
emu_thread.Start();
|
||||
}
|
||||
|
||||
static void cleanup_serialize(void *data)
|
||||
|
@ -809,7 +713,7 @@ static void cleanup_serialize(void *data)
|
|||
if ( data != NULL )
|
||||
free(data) ;
|
||||
|
||||
dc_resume_emu(true);
|
||||
dc_resume();
|
||||
}
|
||||
|
||||
static string get_savestate_file_path()
|
||||
|
@ -825,7 +729,7 @@ static string get_savestate_file_path()
|
|||
return get_writable_data_path("/data/") + state_file;
|
||||
}
|
||||
|
||||
static void* dc_savestate_thread(void* p)
|
||||
void dc_savestate()
|
||||
{
|
||||
string filename;
|
||||
unsigned int total_size = 0 ;
|
||||
|
@ -833,14 +737,13 @@ static void* dc_savestate_thread(void* p)
|
|||
void *data_ptr = NULL ;
|
||||
FILE *f ;
|
||||
|
||||
if (!dc_pause_emu())
|
||||
return NULL;
|
||||
dc_stop();
|
||||
|
||||
if ( ! dc_serialize(&data, &total_size) )
|
||||
{
|
||||
printf("Failed to save state - could not initialize total size\n") ;
|
||||
cleanup_serialize(data) ;
|
||||
return NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
data = malloc(total_size) ;
|
||||
|
@ -848,7 +751,7 @@ static void* dc_savestate_thread(void* p)
|
|||
{
|
||||
printf("Failed to save state - could not malloc %d bytes", total_size) ;
|
||||
cleanup_serialize(data) ;
|
||||
return NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
data_ptr = data ;
|
||||
|
@ -857,7 +760,7 @@ static void* dc_savestate_thread(void* p)
|
|||
{
|
||||
printf("Failed to save state - could not serialize data\n") ;
|
||||
cleanup_serialize(data) ;
|
||||
return NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
filename = get_savestate_file_path();
|
||||
|
@ -867,7 +770,7 @@ static void* dc_savestate_thread(void* p)
|
|||
{
|
||||
printf("Failed to save state - could not open %s for writing\n", filename.c_str()) ;
|
||||
cleanup_serialize(data) ;
|
||||
return NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
fwrite(data, 1, total_size, f) ;
|
||||
|
@ -875,11 +778,9 @@ static void* dc_savestate_thread(void* p)
|
|||
|
||||
cleanup_serialize(data) ;
|
||||
printf("Saved state to %s\n size %d", filename.c_str(), total_size) ;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void* dc_loadstate_thread(void* p)
|
||||
void dc_loadstate()
|
||||
{
|
||||
string filename;
|
||||
unsigned int total_size = 0 ;
|
||||
|
@ -887,8 +788,7 @@ static void* dc_loadstate_thread(void* p)
|
|||
void *data_ptr = NULL ;
|
||||
FILE *f ;
|
||||
|
||||
if (!dc_pause_emu())
|
||||
return NULL;
|
||||
dc_stop();
|
||||
|
||||
filename = get_savestate_file_path();
|
||||
f = fopen(filename.c_str(), "rb") ;
|
||||
|
@ -897,7 +797,7 @@ static void* dc_loadstate_thread(void* p)
|
|||
{
|
||||
printf("Failed to load state - could not open %s for reading\n", filename.c_str()) ;
|
||||
cleanup_serialize(data) ;
|
||||
return NULL;
|
||||
return;
|
||||
}
|
||||
fseek(f, 0, SEEK_END);
|
||||
total_size = ftell(f);
|
||||
|
@ -907,7 +807,7 @@ static void* dc_loadstate_thread(void* p)
|
|||
{
|
||||
printf("Failed to load state - could not malloc %d bytes", total_size) ;
|
||||
cleanup_serialize(data) ;
|
||||
return NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
fread(data, 1, total_size, f) ;
|
||||
|
@ -925,7 +825,7 @@ static void* dc_loadstate_thread(void* p)
|
|||
{
|
||||
printf("Failed to load state - could not unserialize data\n") ;
|
||||
cleanup_serialize(data) ;
|
||||
return NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
dsp.dyndirty = true;
|
||||
|
@ -934,19 +834,4 @@ static void* dc_loadstate_thread(void* p)
|
|||
|
||||
cleanup_serialize(data) ;
|
||||
printf("Loaded state from %s size %d\n", filename.c_str(), total_size) ;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void dc_savestate()
|
||||
{
|
||||
cThread thd(dc_savestate_thread,0);
|
||||
thd.Start() ;
|
||||
}
|
||||
|
||||
void dc_loadstate()
|
||||
{
|
||||
cThread thd(dc_loadstate_thread,0);
|
||||
thd.Start() ;
|
||||
}
|
||||
|
|
|
@ -689,7 +689,7 @@ bool reios_init(u8* rom, u8* flash) {
|
|||
}
|
||||
|
||||
void reios_reset() {
|
||||
|
||||
pre_init = false;
|
||||
}
|
||||
|
||||
void reios_term() {
|
||||
|
|
|
@ -16,7 +16,10 @@
|
|||
You should have received a copy of the GNU General Public License
|
||||
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "oslib/oslib.h"
|
||||
#include "cfg/cfg.h"
|
||||
|
@ -26,14 +29,18 @@
|
|||
#include "imgui/roboto_medium.h"
|
||||
#include "gles/gles.h"
|
||||
#include "input/gamepad_device.h"
|
||||
#include "input/keyboard_device.h"
|
||||
#include "linux-dist/main.h" // FIXME for kcode[]
|
||||
#include "gui_util.h"
|
||||
|
||||
extern bool dc_pause_emu();
|
||||
extern void dc_resume_emu(bool continue_running);
|
||||
extern void dc_loadstate();
|
||||
extern void dc_savestate();
|
||||
extern void dc_stop();
|
||||
extern void dc_reset();
|
||||
extern void dc_resume();
|
||||
extern int dc_start_game(const char *path);
|
||||
extern void UpdateInputState(u32 port);
|
||||
extern bool game_started;
|
||||
|
||||
extern int screen_width, screen_height;
|
||||
extern u8 kb_shift; // shift keys pressed (bitmask)
|
||||
|
@ -41,13 +48,16 @@ extern u8 kb_key[6]; // normal keys pressed
|
|||
extern s32 mo_x_abs;
|
||||
extern s32 mo_y_abs;
|
||||
extern u32 mo_buttons;
|
||||
extern f32 mo_x_delta;
|
||||
extern f32 mo_y_delta;
|
||||
extern f32 mo_wheel_delta;
|
||||
extern bool renderer_changed;
|
||||
|
||||
int screen_dpi = 96;
|
||||
|
||||
static bool inited = false;
|
||||
static float scaling = 1;
|
||||
static enum { Closed, Commands, Settings, ClosedNoResume } gui_state;
|
||||
static enum { Closed, Commands, Settings, ClosedNoResume, Main, Onboarding } gui_state = Main;
|
||||
static bool settings_opening;
|
||||
static bool touch_up;
|
||||
|
||||
|
@ -172,6 +182,14 @@ static void ImGui_Impl_NewFrame()
|
|||
touch_up = true;
|
||||
}
|
||||
#endif
|
||||
if (io.WantCaptureMouse)
|
||||
{
|
||||
io.MouseWheel = -mo_wheel_delta / 16;
|
||||
// Reset all relative mouse positions
|
||||
mo_x_delta = 0;
|
||||
mo_y_delta = 0;
|
||||
mo_wheel_delta = 0;
|
||||
}
|
||||
io.MouseDown[0] = (mo_buttons & (1 << 2)) == 0;
|
||||
io.MouseDown[1] = (mo_buttons & (1 << 1)) == 0;
|
||||
io.MouseDown[2] = (mo_buttons & (1 << 3)) == 0;
|
||||
|
@ -180,7 +198,7 @@ static void ImGui_Impl_NewFrame()
|
|||
io.NavInputs[ImGuiNavInput_Activate] = (kcode[0] & DC_BTN_A) == 0;
|
||||
io.NavInputs[ImGuiNavInput_Cancel] = (kcode[0] & DC_BTN_B) == 0;
|
||||
io.NavInputs[ImGuiNavInput_Input] = (kcode[0] & DC_BTN_X) == 0;
|
||||
io.NavInputs[ImGuiNavInput_Menu] = (kcode[0] & DC_BTN_Y) == 0;
|
||||
//io.NavInputs[ImGuiNavInput_Menu] = (kcode[0] & DC_BTN_Y) == 0;
|
||||
io.NavInputs[ImGuiNavInput_DpadLeft] = (kcode[0] & DC_DPAD_LEFT) == 0;
|
||||
io.NavInputs[ImGuiNavInput_DpadRight] = (kcode[0] & DC_DPAD_RIGHT) == 0;
|
||||
io.NavInputs[ImGuiNavInput_DpadUp] = (kcode[0] & DC_DPAD_UP) == 0;
|
||||
|
@ -197,6 +215,13 @@ static void ImGui_Impl_NewFrame()
|
|||
io.NavInputs[ImGuiNavInput_LStickDown] = joyy[0] > 0 ? (float)joyy[0] / 128.f : 0.f;
|
||||
if (io.NavInputs[ImGuiNavInput_LStickDown] < 0.1f)
|
||||
io.NavInputs[ImGuiNavInput_LStickDown] = 0.f;
|
||||
|
||||
if (KeyboardDevice::GetInstance() != NULL)
|
||||
{
|
||||
std:string input_text = KeyboardDevice::GetInstance()->get_character_input();
|
||||
if (io.WantCaptureKeyboard)
|
||||
io.AddInputCharactersUTF8(input_text.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
static double last_render;
|
||||
|
@ -256,11 +281,7 @@ bool gui_is_open()
|
|||
|
||||
static void gui_display_commands()
|
||||
{
|
||||
if (!dc_pause_emu())
|
||||
{
|
||||
gui_state = Closed;
|
||||
return;
|
||||
}
|
||||
dc_stop();
|
||||
|
||||
ImGui_Impl_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
@ -305,8 +326,9 @@ static void gui_display_commands()
|
|||
ImGui::NextColumn();
|
||||
if (ImGui::Button("Exit", ImVec2(150 * scaling, 50 * scaling)))
|
||||
{
|
||||
dc_resume_emu(false);
|
||||
gui_state = ClosedNoResume;
|
||||
// Exit to main menu
|
||||
gui_state = Main;
|
||||
game_started = false;
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
|
@ -538,6 +560,18 @@ static void controller_mapping_popup(std::shared_ptr<GamepadDevice> gamepad)
|
|||
}
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
static bool game_list_done; // Set to false to refresh the game list
|
||||
|
||||
void directory_selected_callback(bool cancelled, std::string selection)
|
||||
{
|
||||
if (!cancelled)
|
||||
{
|
||||
settings.dreamcast.ContentPath.push_back(selection);
|
||||
game_list_done = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void gui_display_settings()
|
||||
{
|
||||
ImGui_Impl_NewFrame();
|
||||
|
@ -558,7 +592,10 @@ static void gui_display_settings()
|
|||
|
||||
if (ImGui::Button("Done", ImVec2(100 * scaling, 30 * scaling)))
|
||||
{
|
||||
gui_state = Commands;
|
||||
if (game_started)
|
||||
gui_state = Commands;
|
||||
else
|
||||
gui_state = Main;
|
||||
#if DC_PLATFORM == DC_PLATFORM_DREAMCAST
|
||||
// TODO only if changed? sleep time on emu thread?
|
||||
mcfg_DestroyDevices();
|
||||
|
@ -566,23 +603,26 @@ static void gui_display_settings()
|
|||
#endif
|
||||
SaveSettings();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(16 * scaling, normal_padding.y));
|
||||
if (cfgHasGameSpecificConfig())
|
||||
{
|
||||
if (ImGui::Button("Delete Game Config", ImVec2(0, 30 * scaling)))
|
||||
{
|
||||
cfgDeleteGameSpecificConfig();
|
||||
InitSettings();
|
||||
LoadSettings(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ImGui::Button("Make Game Config", ImVec2(0, 30 * scaling)))
|
||||
cfgMakeGameSpecificConfig();
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
if (game_started)
|
||||
{
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(16 * scaling, normal_padding.y));
|
||||
if (cfgHasGameSpecificConfig())
|
||||
{
|
||||
if (ImGui::Button("Delete Game Config", ImVec2(0, 30 * scaling)))
|
||||
{
|
||||
cfgDeleteGameSpecificConfig();
|
||||
InitSettings();
|
||||
LoadSettings(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ImGui::Button("Make Game Config", ImVec2(0, 30 * scaling)))
|
||||
cfgMakeGameSpecificConfig();
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(16 * scaling, 6 * scaling)); // from 4, 3
|
||||
|
||||
|
@ -655,6 +695,46 @@ static void gui_display_settings()
|
|||
ImGui::SameLine();
|
||||
ShowHelpMarker("Video connection type");
|
||||
|
||||
static int current_item;
|
||||
std::vector<const char *> paths;
|
||||
for (auto path : settings.dreamcast.ContentPath)
|
||||
paths.push_back(path.c_str());
|
||||
|
||||
ImVec2 size;
|
||||
size.x = 0.0f;
|
||||
size.y = (ImGui::GetTextLineHeightWithSpacing() + ImGui::GetStyle().FramePadding.y * 2.f)
|
||||
* (settings.dreamcast.ContentPath.size() + 1) ;//+ ImGui::GetStyle().FramePadding.y * 2.f;
|
||||
|
||||
if (ImGui::ListBoxHeader("Content Location", size))
|
||||
{
|
||||
int to_delete = -1;
|
||||
for (int i = 0; i < settings.dreamcast.ContentPath.size(); i++)
|
||||
{
|
||||
ImGui::PushID(settings.dreamcast.ContentPath[i].c_str());
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("%s", settings.dreamcast.ContentPath[i].c_str());
|
||||
ImGui::SameLine(ImGui::GetContentRegionAvailWidth() - ImGui::CalcTextSize("X").x - ImGui::GetStyle().FramePadding.x);
|
||||
if (ImGui::Button("X"))
|
||||
to_delete = i;
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(24 * scaling, 3 * scaling));
|
||||
if (ImGui::Button("Add"))
|
||||
ImGui::OpenPopup("Select Directory");
|
||||
select_directory_popup("Select Directory", scaling, &directory_selected_callback);
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::ListBoxFooter();
|
||||
if (to_delete >= 0)
|
||||
{
|
||||
settings.dreamcast.ContentPath.erase(settings.dreamcast.ContentPath.begin() + to_delete);
|
||||
game_list_done = false;
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ShowHelpMarker("The directories where your games are stored");
|
||||
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
@ -903,6 +983,182 @@ static void gui_display_settings()
|
|||
settings.dynarec.Enable = (bool)dynarec_enabled;
|
||||
}
|
||||
|
||||
#ifdef _ANDROID
|
||||
static std::string current_library_path("/storage/emulated/0/Download");
|
||||
#else
|
||||
static std::string current_library_path("/home/raph/RetroPie/roms/dreamcast/");
|
||||
#endif
|
||||
struct GameMedia {
|
||||
std::string name;
|
||||
std::string path;
|
||||
};
|
||||
|
||||
static bool operator<(const GameMedia &left, const GameMedia &right)
|
||||
{
|
||||
return left.name < right.name;
|
||||
}
|
||||
|
||||
static void add_game_directory(const std::string& path, std::vector<GameMedia>& game_list)
|
||||
{
|
||||
//printf("Exploring %s\n", path.c_str());
|
||||
DIR *dir = opendir(path.c_str());
|
||||
if (dir == NULL)
|
||||
return;
|
||||
while (true)
|
||||
{
|
||||
struct dirent *entry = readdir(dir);
|
||||
if (entry == NULL)
|
||||
break;
|
||||
std:string name(entry->d_name);
|
||||
if (name == "." || name == "..")
|
||||
continue;
|
||||
std::string child_path = path + "/" + name;
|
||||
if (entry->d_type == DT_UNKNOWN)
|
||||
{
|
||||
struct stat st;
|
||||
if (stat(child_path.c_str(), &st) != 0)
|
||||
continue;
|
||||
if (S_ISDIR(st.st_mode))
|
||||
entry->d_type = DT_DIR;
|
||||
}
|
||||
if (entry->d_type == DT_DIR)
|
||||
{
|
||||
add_game_directory(child_path, game_list);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if DC_PLATFORM == DC_PLATFORM_DREAMCAST
|
||||
if (name.size() >= 4)
|
||||
{
|
||||
std::string extension = name.substr(name.size() - 4).c_str();
|
||||
//printf(" found game %s ext %s\n", entry->d_name, extension.c_str());
|
||||
if (stricmp(extension.c_str(), ".cdi") && stricmp(extension.c_str(), ".gdi") && stricmp(extension.c_str(), ".chd") && stricmp(extension.c_str(), ".cue"))
|
||||
continue;
|
||||
game_list.push_back({ name, child_path });
|
||||
}
|
||||
#else
|
||||
std::string::size_type dotpos = name.find_last_of(".");
|
||||
if (dotpos == std::string::npos || dotpos == name.size() - 1)
|
||||
continue;
|
||||
std::string extension = name.substr(dotpos);
|
||||
if (stricmp(extension.c_str(), ".zip") && stricmp(extension.c_str(), ".7z") && stricmp(extension.c_str(), ".bin")
|
||||
&& stricmp(extension.c_str(), ".lst") && stricmp(extension.c_str(), ".dat"))
|
||||
continue;
|
||||
game_list.push_back({ name, child_path });
|
||||
#endif
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
static std::vector<GameMedia> game_list;
|
||||
|
||||
static void fetch_game_list()
|
||||
{
|
||||
if (game_list_done)
|
||||
return;
|
||||
game_list.clear();
|
||||
for (auto path : settings.dreamcast.ContentPath)
|
||||
add_game_directory(path, game_list);
|
||||
std::stable_sort(game_list.begin(), game_list.end());
|
||||
game_list_done = true;
|
||||
}
|
||||
|
||||
static void gui_display_demo()
|
||||
{
|
||||
ImGui_Impl_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
ImGui::ShowDemoWindow();
|
||||
ImGui::Render();
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData(), false);
|
||||
}
|
||||
|
||||
static void gui_start_game(const std::string& path)
|
||||
{
|
||||
dc_start_game(path.c_str());
|
||||
}
|
||||
|
||||
static void gui_display_content()
|
||||
{
|
||||
ImGui_Impl_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0));
|
||||
ImGui::SetNextWindowSize(ImVec2(screen_width, screen_height));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0);
|
||||
|
||||
ImGui::Begin("##main", NULL, ImGuiWindowFlags_NoDecoration);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(20 * scaling, 8 * scaling)); // from 8, 4
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("GAMES");
|
||||
|
||||
static ImGuiTextFilter filter;
|
||||
if (KeyboardDevice::GetInstance() != NULL)
|
||||
{
|
||||
ImGui::SameLine(0, 32 * scaling);
|
||||
filter.Draw("Filter");
|
||||
}
|
||||
|
||||
ImGui::SameLine(ImGui::GetContentRegionAvailWidth() - ImGui::CalcTextSize("Settings").x - ImGui::GetStyle().FramePadding.x * 2.0f /*+ ImGui::GetStyle().ItemSpacing.x*/);
|
||||
if (ImGui::Button("Settings"))//, ImVec2(0, 30 * scaling)))
|
||||
gui_state = Settings;
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
fetch_game_list();
|
||||
|
||||
// Only if Filter and Settings aren't focused... ImGui::SetNextWindowFocus();
|
||||
ImGui::BeginChild(ImGui::GetID("library"), ImVec2(0, 0), true);
|
||||
{
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8 * scaling, 20 * scaling)); // from 8, 4
|
||||
|
||||
for (auto game : game_list)
|
||||
if (filter.PassFilter(game.name.c_str()))
|
||||
{
|
||||
if (ImGui::Selectable(game.name.c_str()))
|
||||
{
|
||||
gui_start_game(game.path);
|
||||
gui_state = ClosedNoResume;
|
||||
}
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::Render();
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData(), false);
|
||||
}
|
||||
|
||||
void systemdir_selected_callback(bool cancelled, std::string selection)
|
||||
{
|
||||
if (!cancelled)
|
||||
{
|
||||
set_user_config_dir(selection);
|
||||
set_user_data_dir(selection);
|
||||
if (cfgOpen())
|
||||
{
|
||||
LoadSettings(false);
|
||||
gui_state = Main;
|
||||
// FIXME Save config dir in android app prefs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gui_display_onboarding()
|
||||
{
|
||||
ImGui_Impl_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
ImGui::OpenPopup("Select System Directory");
|
||||
select_directory_popup("Select System Directory", scaling, &systemdir_selected_callback);
|
||||
|
||||
ImGui::Render();
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData(), false);
|
||||
}
|
||||
|
||||
void gui_display_ui()
|
||||
{
|
||||
switch (gui_state)
|
||||
|
@ -913,13 +1169,20 @@ void gui_display_ui()
|
|||
case Commands:
|
||||
gui_display_commands();
|
||||
break;
|
||||
case Main:
|
||||
//gui_display_demo();
|
||||
gui_display_content();
|
||||
break;
|
||||
case Closed:
|
||||
case ClosedNoResume:
|
||||
break;
|
||||
case Onboarding:
|
||||
gui_display_onboarding();
|
||||
break;
|
||||
}
|
||||
|
||||
if (gui_state == Closed)
|
||||
dc_resume_emu(true);
|
||||
dc_resume();
|
||||
else if (gui_state == ClosedNoResume)
|
||||
gui_state = Closed;
|
||||
}
|
||||
|
@ -942,6 +1205,11 @@ void gui_display_fps(const char *string)
|
|||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
}
|
||||
|
||||
void gui_open_onboarding()
|
||||
{
|
||||
gui_state = Onboarding;
|
||||
}
|
||||
|
||||
void gui_term()
|
||||
{
|
||||
inited = false;
|
||||
|
|
|
@ -23,6 +23,7 @@ void gui_open_settings();
|
|||
bool gui_is_open();
|
||||
void gui_display_ui();
|
||||
void gui_display_fps(const char *string);
|
||||
void gui_open_onboarding();
|
||||
void gui_term();
|
||||
|
||||
extern int screen_dpi;
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
Copyright 2019 flyinghead
|
||||
|
||||
This file is part of reicast.
|
||||
|
||||
reicast 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.
|
||||
|
||||
reicast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "gui_util.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "stdclass.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
extern int screen_width, screen_height;
|
||||
|
||||
static std::string select_current_directory;
|
||||
static std::vector<std::string> select_subfolders;
|
||||
static bool subfolders_read;
|
||||
#ifdef _WIN32
|
||||
static const std::string separators = "/\\";
|
||||
static const std::string native_separator = "\\";
|
||||
#else
|
||||
static const std::string separators = "/";
|
||||
static const std::string native_separator = "/";
|
||||
#endif
|
||||
|
||||
void select_directory_popup(const char *prompt, float scaling, StringCallback callback)
|
||||
{
|
||||
if (select_current_directory.empty())
|
||||
{
|
||||
#if defined(_ANDROID)
|
||||
const char *home = getenv("REICAST_HOME");
|
||||
if (home != NULL)
|
||||
select_current_directory = home;
|
||||
#elif HOST_OS == OS_LINUX || HOST_OS == OS_DARWIN
|
||||
const char *home = getenv("HOME");
|
||||
if (home != NULL)
|
||||
select_current_directory = home;
|
||||
#elif HOST_OS == OS_WINDOWS
|
||||
const char *home = getenv("HOMEPATH");
|
||||
if (home != NULL)
|
||||
select_current_directory = home;
|
||||
#endif
|
||||
if (select_current_directory.empty())
|
||||
{
|
||||
select_current_directory = get_writable_config_path("");
|
||||
if (select_current_directory.empty())
|
||||
select_current_directory = ".";
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0));
|
||||
ImGui::SetNextWindowSize(ImVec2(screen_width, screen_height));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0);
|
||||
|
||||
if (ImGui::BeginPopupModal(prompt, NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize ))
|
||||
{
|
||||
std::string path = select_current_directory;
|
||||
int last_sep = path.find_last_of(separators);
|
||||
if (last_sep == path.size() - 1)
|
||||
path.pop_back();
|
||||
|
||||
static std::string error_message;
|
||||
|
||||
if (!subfolders_read)
|
||||
{
|
||||
select_subfolders.clear();
|
||||
error_message.clear();
|
||||
#ifdef _WIN32
|
||||
if (select_current_directory.empty())
|
||||
{
|
||||
// List all the drives
|
||||
u32 drives = GetLogicalDrives();
|
||||
for (int i = 0; i < 32; i++)
|
||||
if ((drives & (1 << i)) != 0)
|
||||
select_subfolders.push_back(std::string(1, (char)('A' + i) + ":\\");
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
DIR *dir = opendir(select_current_directory.c_str());
|
||||
if (dir == NULL)
|
||||
{
|
||||
error_message = "Cannot read " + select_current_directory;
|
||||
select_subfolders.push_back("..");
|
||||
}
|
||||
else
|
||||
{
|
||||
bool dotdot_seen = false;
|
||||
while (true)
|
||||
{
|
||||
struct dirent *entry = readdir(dir);
|
||||
if (entry == NULL)
|
||||
break;
|
||||
std:string name(entry->d_name);
|
||||
if (name == ".")
|
||||
continue;
|
||||
if (name == "..")
|
||||
dotdot_seen = true;
|
||||
std::string child_path = path + "/" + name;
|
||||
if (entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK)
|
||||
{
|
||||
struct stat st;
|
||||
if (stat(child_path.c_str(), &st) != 0)
|
||||
continue;
|
||||
if (S_ISDIR(st.st_mode))
|
||||
entry->d_type = DT_DIR;
|
||||
}
|
||||
if (entry->d_type == DT_DIR && access(child_path.c_str(), R_OK) == 0)
|
||||
select_subfolders.push_back(name);
|
||||
}
|
||||
closedir(dir);
|
||||
#ifdef _WIN32
|
||||
if (!dotdot_seen)
|
||||
select_subfolders.push_back("..");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
std::stable_sort(select_subfolders.begin(), select_subfolders.end());
|
||||
subfolders_read = true;
|
||||
}
|
||||
|
||||
ImGui::Text("%s", error_message.empty() ? select_current_directory.c_str() : error_message.c_str());
|
||||
ImGui::BeginChild(ImGui::GetID("dir_list"), ImVec2(0, -ImGui::CalcTextSize("Cancel").y - ImGui::GetStyle().FramePadding. y * 2 - ImGui::GetStyle().ItemSpacing.y), true);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8 * scaling, 20 * scaling)); // from 8, 4
|
||||
|
||||
|
||||
for (auto name : select_subfolders)
|
||||
{
|
||||
std::string child_path;
|
||||
if (name == "..")
|
||||
{
|
||||
std::string::size_type last_sep = path.find_last_of(separators);
|
||||
if (last_sep == std::string::npos)
|
||||
{
|
||||
if (path.empty())
|
||||
// Root folder
|
||||
continue;
|
||||
#ifdef _WIN32
|
||||
if (path.size() == 2 && path[1] == ':')
|
||||
child_path = "";
|
||||
else
|
||||
#endif
|
||||
if (path == ".")
|
||||
child_path = "..";
|
||||
else if (path == "..")
|
||||
child_path = ".." + native_separator + "..";
|
||||
else
|
||||
child_path = ".";
|
||||
}
|
||||
else if (last_sep == 0)
|
||||
child_path = native_separator;
|
||||
else if (path.size() >= 2 && path.substr(path.size() - 2) == "..")
|
||||
child_path = path + native_separator + "..";
|
||||
else
|
||||
child_path = path.substr(0, last_sep);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!path.empty())
|
||||
{
|
||||
std::string::size_type last_sep = path.find_last_of(separators);
|
||||
if (last_sep == path.size() - 1)
|
||||
path.pop_back();
|
||||
}
|
||||
child_path = path + native_separator + name;
|
||||
}
|
||||
if (ImGui::Selectable(name.c_str()))
|
||||
{
|
||||
subfolders_read = false;
|
||||
select_current_directory = child_path;
|
||||
}
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::EndChild();
|
||||
if (ImGui::Button("Select Current Directory"))
|
||||
{
|
||||
subfolders_read = false;
|
||||
callback(false, select_current_directory);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel"))
|
||||
{
|
||||
subfolders_read = false;
|
||||
callback(true, "");
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
Copyright 2019 flyinghead
|
||||
|
||||
This file is part of reicast.
|
||||
|
||||
reicast 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.
|
||||
|
||||
reicast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <string>
|
||||
|
||||
typedef void (*StringCallback)(bool cancelled, std::string selection);
|
||||
|
||||
void select_directory_popup(const char *prompt, float scaling, StringCallback callback);
|
|
@ -28,7 +28,7 @@ static std::shared_ptr<SDLMouseGamepadDevice> sdl_mouse_gamepad;
|
|||
static std::shared_ptr<SDLKbGamepadDevice> sdl_kb_gamepad;
|
||||
static SDLKeyboardDevice* sdl_keyboard = NULL;
|
||||
|
||||
extern void dc_stop();
|
||||
extern void dc_exit();
|
||||
|
||||
#ifdef TARGET_PANDORA
|
||||
extern char OSD_Info[128];
|
||||
|
@ -122,7 +122,7 @@ void input_sdl_handle(u32 port)
|
|||
switch (event.type)
|
||||
{
|
||||
case SDL_QUIT:
|
||||
dc_stop();
|
||||
dc_exit();
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
|
|
|
@ -750,7 +750,7 @@ struct settings_t
|
|||
u32 region; // 0 -> JP, 1 -> USA, 2 -> EU, 3 -> default
|
||||
u32 broadcast; // 0 -> NTSC, 1 -> PAL, 2 -> PAL/M, 3 -> PAL/N, 4 -> default
|
||||
u32 language; // 0 -> JP, 1 -> EN, 2 -> DE, 3 -> FR, 4 -> SP, 5 -> IT, 6 -> default
|
||||
|
||||
std::vector<std::string> ContentPath;
|
||||
} dreamcast;
|
||||
|
||||
struct
|
||||
|
@ -918,11 +918,6 @@ u32 libGDR_GetDiscType();
|
|||
void libGDR_GetSessionInfo(u8* pout,u8 session);
|
||||
|
||||
|
||||
//ExtDev
|
||||
s32 libExtDevice_Init();
|
||||
void libExtDevice_Reset(bool M);
|
||||
void libExtDevice_Term();
|
||||
|
||||
// 0x00600000 - 0x006007FF [NAOMI] (modem area for dreamcast)
|
||||
u32 libExtDevice_ReadMem_A0_006(u32 addr,u32 size);
|
||||
void libExtDevice_WriteMem_A0_006(u32 addr,u32 data,u32 size);
|
||||
|
|
|
@ -106,7 +106,7 @@ PCHAR*
|
|||
return argv;
|
||||
}
|
||||
|
||||
void dc_stop(void);
|
||||
void dc_exit(void);
|
||||
|
||||
bool VramLockedWrite(u8* address);
|
||||
bool ngen_Rewrite(unat& addr,unat retadr,unat acc);
|
||||
|
@ -658,16 +658,19 @@ int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine
|
|||
#endif
|
||||
#endif
|
||||
{
|
||||
int dc_init(int argc,wchar* argv[]);
|
||||
void dc_run();
|
||||
int reicast_init(int argc, char* argv[]);
|
||||
void *rend_thread(void *);
|
||||
void dc_term();
|
||||
dc_init(argc,argv);
|
||||
|
||||
if (reicast_init(argc, argv) != 0)
|
||||
die("Reicast initialization failed");
|
||||
|
||||
#ifdef _WIN64
|
||||
setup_seh();
|
||||
#endif
|
||||
|
||||
dc_run();
|
||||
rend_thread(NULL);
|
||||
|
||||
dc_term();
|
||||
}
|
||||
#ifndef __GNUC__
|
||||
|
@ -710,7 +713,7 @@ void os_DoEvents()
|
|||
// If the message is WM_QUIT, exit the while loop
|
||||
if (msg.message == WM_QUIT)
|
||||
{
|
||||
dc_stop();
|
||||
dc_exit();
|
||||
}
|
||||
|
||||
// Translate the message and dispatch it to WindowProc()
|
||||
|
|
|
@ -8,6 +8,7 @@ import android.preference.PreferenceManager;
|
|||
import android.support.v7.app.AppCompatDelegate;
|
||||
import android.util.Log;
|
||||
|
||||
import com.reicast.emulator.config.Config;
|
||||
import com.reicast.emulator.emu.JNIdc;
|
||||
|
||||
public class Emulator extends Application {
|
||||
|
@ -188,9 +189,9 @@ public class Emulator extends Application {
|
|||
* Fetch current configuration settings from the emulator and save them
|
||||
*
|
||||
*/
|
||||
public void SaveSettings()
|
||||
public void SaveAndroidSettings(String homeDirectory)
|
||||
{
|
||||
Log.i("Emulator", "SaveSettings: saving preferences");
|
||||
Log.i("Emulator", "SaveAndroidSettings: saving preferences");
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
|
||||
Emulator.dynarecopt = JNIdc.getDynarec();
|
||||
|
@ -223,6 +224,7 @@ public class Emulator extends Application {
|
|||
JNIdc.getControllers(maple_devices, maple_expansion_devices);
|
||||
|
||||
SharedPreferences.Editor editor = prefs.edit()
|
||||
.putString(Config.pref_home, homeDirectory)
|
||||
.putBoolean(Emulator.pref_dynarecopt, Emulator.dynarecopt)
|
||||
.putBoolean(Emulator.pref_idleskip, Emulator.idleskip)
|
||||
.putBoolean(Emulator.pref_unstable, Emulator.unstableopt)
|
||||
|
|
|
@ -396,7 +396,7 @@ public class FileBrowser extends Fragment {
|
|||
mCallback.onFolderSelected(
|
||||
Uri.fromFile(new File(home_directory)));
|
||||
}
|
||||
JNIdc.config(home_directory);
|
||||
//JNIdc.config(home_directory);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -545,7 +545,7 @@ public class FileBrowser extends Fragment {
|
|||
browser.get().mCallback.onFolderSelected(Uri.fromFile(
|
||||
new File(browser.get().home_directory)));
|
||||
}
|
||||
JNIdc.config(browser.get().home_directory);
|
||||
//JNIdc.config(browser.get().home_directory);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -333,7 +333,7 @@ public class GL2JNIActivity extends Activity implements ActivityCompat.OnRequest
|
|||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
if (permissions.length > 0 && permissions[0] == Manifest.permission.RECORD_AUDIO && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
if (permissions.length > 0 && Manifest.permission.RECORD_AUDIO .equals(permissions[0]) && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
SipEmulator sip = new SipEmulator();
|
||||
sip.startRecording();
|
||||
JNIdc.setupMic(sip);
|
||||
|
|
|
@ -38,7 +38,6 @@ import com.reicast.emulator.config.Config;
|
|||
import com.reicast.emulator.config.EditVJoyActivity;
|
||||
import com.reicast.emulator.config.InputFragment;
|
||||
import com.reicast.emulator.config.OptionsFragment;
|
||||
import com.reicast.emulator.config.PGConfigFragment;
|
||||
import com.reicast.emulator.debug.GenerateLogs;
|
||||
import com.reicast.emulator.emu.JNIdc;
|
||||
|
||||
|
@ -47,7 +46,7 @@ import java.util.List;
|
|||
|
||||
public class MainActivity extends AppCompatActivity implements
|
||||
NavigationView.OnNavigationItemSelectedListener, FileBrowser.OnItemSelectedListener,
|
||||
OptionsFragment.OnClickListener, InputFragment.OnClickListener {
|
||||
OptionsFragment.OnClickListener, InputFragment.OnClickListener {
|
||||
private static final int PERMISSION_REQUEST = 1001;
|
||||
|
||||
private SharedPreferences mPrefs;
|
||||
|
@ -119,7 +118,14 @@ public class MainActivity extends AppCompatActivity implements
|
|||
if (!getFilesDir().exists()) {
|
||||
getFilesDir().mkdir();
|
||||
}
|
||||
JNIdc.initEnvironment((Emulator)getApplicationContext());
|
||||
String home_directory = mPrefs.getString(Config.pref_home, "");
|
||||
String result = JNIdc.initEnvironment((Emulator)getApplicationContext(), home_directory);
|
||||
if (result != null)
|
||||
showToastMessage("Initialization failed: " + result,
|
||||
Snackbar.LENGTH_LONG);
|
||||
|
||||
String android_home_directory = Environment.getExternalStorageDirectory().getAbsolutePath();
|
||||
JNIdc.config(android_home_directory);
|
||||
|
||||
// When viewing a resource, pass its URI to the native code for opening
|
||||
Intent intent = getIntent();
|
||||
|
@ -231,7 +237,7 @@ public class MainActivity extends AppCompatActivity implements
|
|||
String home_directory = mPrefs.getString(Config.pref_home,
|
||||
Environment.getExternalStorageDirectory().getAbsolutePath());
|
||||
|
||||
JNIdc.config(home_directory);
|
||||
//JNIdc.config(home_directory);
|
||||
|
||||
startActivity(new Intent("com.reicast.EMULATOR", uri,
|
||||
getApplicationContext(), EditVJoyActivity.class));
|
||||
|
@ -244,7 +250,7 @@ public class MainActivity extends AppCompatActivity implements
|
|||
String home_directory = mPrefs.getString(Config.pref_home,
|
||||
Environment.getExternalStorageDirectory().getAbsolutePath());
|
||||
|
||||
JNIdc.config(home_directory);
|
||||
//JNIdc.config(home_directory);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
uri = Uri.parse(uri.toString().replace("content://"
|
||||
|
@ -547,4 +553,14 @@ public class MainActivity extends AppCompatActivity implements
|
|||
.getDimensionPixelOffset(R.dimen.snackbar_icon_padding));
|
||||
snackbar.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
if (permissions.length > 0 && (Manifest.permission.READ_EXTERNAL_STORAGE.equals(permissions[0]) || Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permissions[0]))
|
||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
onGameSelected(Uri.parse("file://whatever"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -129,7 +129,7 @@ public class NativeGLActivity extends BaseNativeGLActivity implements ActivityCo
|
|||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
if (permissions.length > 0 && permissions[0] == Manifest.permission.RECORD_AUDIO && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
if (permissions.length > 0 && Manifest.permission.RECORD_AUDIO .equals(permissions[0]) && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
SipEmulator sip = new SipEmulator();
|
||||
sip.startRecording();
|
||||
JNIdc.setupMic(sip);
|
||||
|
|
|
@ -111,7 +111,7 @@ public class OptionsFragment extends Fragment {
|
|||
|
||||
mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||
|
||||
// Specialized handler for devices with an extSdCard mount for external
|
||||
// FIXME Specialized handler for devices with an extSdCard mount for external
|
||||
HashSet<String> extStorage = FileBrowser.getExternalMounts();
|
||||
if (extStorage != null && !extStorage.isEmpty()) {
|
||||
for (String sd : extStorage) {
|
||||
|
@ -157,7 +157,7 @@ public class OptionsFragment extends Fragment {
|
|||
Snackbar.LENGTH_SHORT);
|
||||
}
|
||||
mPrefs.edit().putString(Config.pref_home, home_directory).apply();
|
||||
JNIdc.config(home_directory);
|
||||
//JNIdc.config(home_directory);
|
||||
new LocateThemes(OptionsFragment.this).execute(home_directory + "/themes");
|
||||
}
|
||||
hideSoftKeyBoard();
|
||||
|
|
|
@ -134,8 +134,8 @@ public class GL2JNIView extends GLSurfaceView implements IEmulatorView
|
|||
DisplayMetrics dm = context.getResources().getDisplayMetrics();
|
||||
JNIdc.screenDpi((int)Math.max(dm.xdpi, dm.ydpi));
|
||||
|
||||
JNIdc.config(prefs.getString(Config.pref_home,
|
||||
Environment.getExternalStorageDirectory().getAbsolutePath()));
|
||||
//JNIdc.config(prefs.getString(Config.pref_home,
|
||||
// Environment.getExternalStorageDirectory().getAbsolutePath()));
|
||||
|
||||
ethd = new EmuThread(this);
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ public final class JNIdc
|
|||
{
|
||||
static { System.loadLibrary("dc"); }
|
||||
|
||||
public static native void initEnvironment(Emulator emulator);
|
||||
public static native String initEnvironment(Emulator emulator, String homeDirectory);
|
||||
public static native void config(String dirName);
|
||||
public static native String init(String fileName);
|
||||
public static native void query(EmuThread thread);
|
||||
|
|
|
@ -90,8 +90,8 @@ public class NativeGLView extends SurfaceView implements IEmulatorView {
|
|||
DisplayMetrics dm = context.getResources().getDisplayMetrics();
|
||||
JNIdc.screenDpi((int)Math.max(dm.xdpi, dm.ydpi));
|
||||
|
||||
JNIdc.config(prefs.getString(Config.pref_home,
|
||||
Environment.getExternalStorageDirectory().getAbsolutePath()));
|
||||
//JNIdc.config(prefs.getString(Config.pref_home,
|
||||
// Environment.getExternalStorageDirectory().getAbsolutePath()));
|
||||
|
||||
ethd = new EmuThread(this);
|
||||
|
||||
|
@ -109,7 +109,7 @@ public class NativeGLView extends SurfaceView implements IEmulatorView {
|
|||
|
||||
if (NativeGLActivity.syms != null)
|
||||
JNIdc.data(1, NativeGLActivity.syms);
|
||||
|
||||
/*
|
||||
final String initStatus = JNIdc.init(fileName);
|
||||
if (initStatus != null)
|
||||
{
|
||||
|
@ -120,25 +120,25 @@ public class NativeGLView extends SurfaceView implements IEmulatorView {
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
throw new EmulatorInitFailed();
|
||||
}
|
||||
JNIdc.query(ethd);
|
||||
|
||||
*/
|
||||
// Continuously render frames until the emulator stops
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (ethd.getState() == Thread.State.TERMINATED)
|
||||
((Activity)getContext()).finish();
|
||||
else {
|
||||
//if (ethd.getState() == Thread.State.TERMINATED)
|
||||
// ((Activity)getContext()).finish();
|
||||
//else
|
||||
{
|
||||
JNIdc.rendframeNative();
|
||||
handler.post(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ethd.start();
|
||||
ethd.run(); // FIXME Not a thread anymore
|
||||
|
||||
}
|
||||
|
||||
|
@ -509,11 +509,13 @@ public class NativeGLView extends SurfaceView implements IEmulatorView {
|
|||
Log.i("NativeGLView", "Stopping emulator");
|
||||
//JNIdc.destroy();
|
||||
JNIdc.stop();
|
||||
/*
|
||||
try {
|
||||
ethd.join();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
*/
|
||||
Log.i("NativeGLView", "Stopping emulator completed");
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <stdarg.h>
|
||||
#include <android/log.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <GLES2/gl2.h>
|
||||
|
@ -13,6 +14,7 @@
|
|||
#include <android/native_window_jni.h>
|
||||
|
||||
#include "hw/maple/maple_cfg.h"
|
||||
#include "hw/pvr/Renderer_if.h"
|
||||
#include "profiler/profiler.h"
|
||||
#include "rend/TexCache.h"
|
||||
#include "hw/maple/maple_devs.h"
|
||||
|
@ -26,8 +28,7 @@
|
|||
JavaVM* g_jvm;
|
||||
|
||||
// Convenience class to get the java environment for the current thread.
|
||||
// Also attach the threads, and detach it on destruction, if needed. This is probably not very efficient
|
||||
// but shouldn't be needed except for error reporting.
|
||||
// Also attach the threads, and detach it on destruction, if needed.
|
||||
class JVMAttacher {
|
||||
public:
|
||||
JVMAttacher() : env(NULL), detach_thread(false) {
|
||||
|
@ -55,14 +56,16 @@ public:
|
|||
g_jvm->DetachCurrentThread();
|
||||
}
|
||||
|
||||
bool failed() { return env == NULL; }
|
||||
|
||||
JNIEnv *env;
|
||||
|
||||
private:
|
||||
void log_error(const char *reason)
|
||||
{
|
||||
LOGE("JVMAttacher cannot attach to JVM: %s", reason);
|
||||
}
|
||||
|
||||
bool failed() { return env == NULL; }
|
||||
|
||||
JNIEnv *env;
|
||||
bool detach_thread = false;
|
||||
};
|
||||
|
||||
|
@ -82,7 +85,7 @@ JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_set ## jsetting(JNIEn
|
|||
|
||||
extern "C"
|
||||
{
|
||||
JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_initEnvironment(JNIEnv *env, jobject obj, jobject emulator) __attribute__((visibility("default")));
|
||||
JNIEXPORT jstring JNICALL Java_com_reicast_emulator_emu_JNIdc_initEnvironment(JNIEnv *env, jobject obj, jobject emulator, jstring homeDirectory) __attribute__((visibility("default")));
|
||||
JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_config(JNIEnv *env,jobject obj,jstring dirName) __attribute__((visibility("default")));
|
||||
JNIEXPORT jstring JNICALL Java_com_reicast_emulator_emu_JNIdc_init(JNIEnv *env,jobject obj,jstring fileName) __attribute__((visibility("default")));
|
||||
JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_query(JNIEnv *env,jobject obj,jobject emu_thread) __attribute__((visibility("default")));
|
||||
|
@ -159,28 +162,15 @@ JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_screenDpi(JNIEnv *env
|
|||
screen_dpi = screenDpi;
|
||||
}
|
||||
|
||||
void SetApplicationPath(wchar *path);
|
||||
int dc_init(int argc, wchar* argv[]);
|
||||
void dc_run();
|
||||
void dc_pause();
|
||||
void dc_pause_emu();
|
||||
void dc_resume_emu(bool continue_running);
|
||||
int reicast_init(int argc, char* argv[]);
|
||||
void dc_resume();
|
||||
void dc_stop();
|
||||
void dc_term();
|
||||
bool dc_is_running();
|
||||
|
||||
bool VramLockedWrite(u8* address);
|
||||
|
||||
bool rend_single_frame();
|
||||
void rend_init_renderer();
|
||||
void rend_term_renderer();
|
||||
void rend_cancel_emu_wait();
|
||||
bool egl_makecurrent();
|
||||
|
||||
//extern cResetEvent rs,re;
|
||||
extern int screen_width,screen_height;
|
||||
|
||||
static u64 tvs_base;
|
||||
static char gamedisk[256];
|
||||
|
||||
u16 kcode[4] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
|
||||
|
@ -197,7 +187,7 @@ extern bool print_stats;
|
|||
|
||||
//stuff for saving prefs
|
||||
jobject g_emulator;
|
||||
jmethodID saveSettingsMid;
|
||||
jmethodID saveAndroidSettingsMid;
|
||||
static ANativeWindow *g_window = 0;
|
||||
|
||||
void os_DoEvents()
|
||||
|
@ -231,21 +221,6 @@ void *libPvr_GetRenderSurface()
|
|||
|
||||
void common_linux_setup();
|
||||
|
||||
MapleDeviceType GetMapleDeviceType(int value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case 1:
|
||||
return MDT_SegaVMU;
|
||||
case 2:
|
||||
return MDT_Microphone;
|
||||
case 3:
|
||||
return MDT_PurupuruPack;
|
||||
default:
|
||||
return MDT_None;
|
||||
}
|
||||
}
|
||||
|
||||
void os_SetupInput()
|
||||
{
|
||||
#if DC_PLATFORM == DC_PLATFORM_DREAMCAST
|
||||
|
@ -258,7 +233,7 @@ void os_SetWindowText(char const *Text)
|
|||
putinf("%s",Text);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_initEnvironment(JNIEnv *env, jobject obj, jobject emulator)
|
||||
JNIEXPORT jstring JNICALL Java_com_reicast_emulator_emu_JNIdc_initEnvironment(JNIEnv *env, jobject obj, jobject emulator, jstring homeDirectory)
|
||||
{
|
||||
// Initialize platform-specific stuff
|
||||
common_linux_setup();
|
||||
|
@ -268,18 +243,37 @@ JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_initEnvironment(JNIEn
|
|||
env->GetJavaVM(&g_jvm);
|
||||
if (g_emulator == NULL) {
|
||||
g_emulator = env->NewGlobalRef(emulator);
|
||||
saveSettingsMid = env->GetMethodID(env->GetObjectClass(emulator), "SaveSettings", "()V");
|
||||
saveAndroidSettingsMid = env->GetMethodID(env->GetObjectClass(emulator), "SaveAndroidSettings", "(Ljava/lang/String;)V");
|
||||
}
|
||||
// Set home directory based on User config
|
||||
const char* path = homeDirectory != NULL ? env->GetStringUTFChars(homeDirectory, 0) : "";
|
||||
set_user_config_dir(path);
|
||||
set_user_data_dir(path);
|
||||
printf("Config dir is: %s\n", get_writable_config_path("").c_str());
|
||||
printf("Data dir is: %s\n", get_writable_data_path("").c_str());
|
||||
if (homeDirectory != NULL)
|
||||
env->ReleaseStringUTFChars(homeDirectory, path);
|
||||
|
||||
jstring msg = NULL;
|
||||
int rc = reicast_init(0, NULL);
|
||||
if (rc == -4)
|
||||
msg = env->NewStringUTF("Cannot find configuration");
|
||||
else if (rc == 69)
|
||||
msg = env->NewStringUTF("Invalid command line");
|
||||
else if (rc == -1)
|
||||
msg = env->NewStringUTF("Memory initialization failed");
|
||||
return msg;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_config(JNIEnv *env,jobject obj,jstring dirName)
|
||||
{
|
||||
// Set home directory based on User config
|
||||
const char* D = dirName? env->GetStringUTFChars(dirName,0):0;
|
||||
set_user_config_dir(D);
|
||||
set_user_data_dir(D);
|
||||
printf("Config dir is: %s\n", get_writable_config_path("/").c_str());
|
||||
printf("Data dir is: %s\n", get_writable_data_path("/").c_str());
|
||||
// set_user_config_dir(D);
|
||||
// set_user_data_dir(D);
|
||||
// printf("Config dir is: %s\n", get_writable_config_path("").c_str());
|
||||
// printf("Data dir is: %s\n", get_writable_data_path("").c_str());
|
||||
setenv("REICAST_HOME", D, 1);
|
||||
env->ReleaseStringUTFChars(dirName,D);
|
||||
}
|
||||
|
||||
|
@ -299,6 +293,7 @@ JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_bootdisk(JNIEnv *env,
|
|||
|
||||
JNIEXPORT jstring JNICALL Java_com_reicast_emulator_emu_JNIdc_init(JNIEnv *env,jobject obj,jstring fileName)
|
||||
{
|
||||
/* FIXME
|
||||
// Get filename string from Java
|
||||
const char* P = fileName ? env->GetStringUTFChars(fileName,0) : 0;
|
||||
if (!P) gamedisk[0] = '\0';
|
||||
|
@ -313,23 +308,8 @@ JNIEXPORT jstring JNICALL Java_com_reicast_emulator_emu_JNIdc_init(JNIEnv *env,j
|
|||
// Set configuration
|
||||
settings.profile.run_counts = 0;
|
||||
|
||||
// Make up argument list
|
||||
char *args[3];
|
||||
args[0] = "dc";
|
||||
args[1] = "-config";
|
||||
args[2] = gamedisk[0] != 0 ? (char *)malloc(strlen(gamedisk) + 32) : NULL;
|
||||
|
||||
if (args[2] != NULL)
|
||||
{
|
||||
strcpy(args[2], "config:image=");
|
||||
strcat(args[2], gamedisk);
|
||||
}
|
||||
|
||||
// Run nullDC emulator
|
||||
int rc = dc_init(args[2] ? 3 : 1, args);
|
||||
|
||||
if (args[2] != NULL)
|
||||
free(args[2]);
|
||||
int rc = dc_start_game(gamedisk);
|
||||
|
||||
jstring msg = NULL;
|
||||
if (rc == -5)
|
||||
|
@ -344,6 +324,8 @@ JNIEXPORT jstring JNICALL Java_com_reicast_emulator_emu_JNIdc_init(JNIEnv *env,j
|
|||
msg = env->NewStringUTF("Memory initialization failed");
|
||||
|
||||
return msg;
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_diskSwap(JNIEnv *env,jobject obj,jstring disk)
|
||||
|
@ -384,9 +366,11 @@ jmethodID getmicdata;
|
|||
jobject vmulcd = NULL;
|
||||
jbyteArray jpix = NULL;
|
||||
jmethodID updatevmuscreen;
|
||||
extern bool game_started;
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_query(JNIEnv *env,jobject obj,jobject emu_thread)
|
||||
{
|
||||
/* FIXME
|
||||
jmethodID reiosInfoMid=env->GetMethodID(env->GetObjectClass(emu_thread),"reiosInfo","(Ljava/lang/String;Ljava/lang/String;)V");
|
||||
|
||||
char *id = (char*)malloc(11);
|
||||
|
@ -398,23 +382,25 @@ JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_query(JNIEnv *env,job
|
|||
jstring reios_name = env->NewStringUTF(name);
|
||||
|
||||
env->CallVoidMethod(emu_thread, reiosInfoMid, reios_id, reios_name);
|
||||
*/
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_run(JNIEnv *env,jobject obj,jobject emu_thread)
|
||||
{
|
||||
install_prof_handler(0);
|
||||
jenv = env;
|
||||
emu = env->NewGlobalRef(emu_thread);
|
||||
emu = env->NewGlobalRef(emu_thread); // FIXME Delete ref
|
||||
|
||||
jsamples=env->NewShortArray(SAMPLE_COUNT*2);
|
||||
jsamples = (jshortArray)env->NewGlobalRef(jsamples); // FIXME Delete ref
|
||||
writemid=env->GetMethodID(env->GetObjectClass(emu),"WriteBuffer","([SI)I");
|
||||
coreMessageMid=env->GetMethodID(env->GetObjectClass(emu),"coreMessage","([B)I");
|
||||
dieMid=env->GetMethodID(env->GetObjectClass(emu),"Die","()V");
|
||||
|
||||
dc_run();
|
||||
//dc_run(NULL);
|
||||
|
||||
env->DeleteGlobalRef(emu);
|
||||
emu = NULL;
|
||||
//env->DeleteGlobalRef(emu);
|
||||
//emu = NULL;
|
||||
}
|
||||
|
||||
int msgboxf(const wchar* text,unsigned int type,...) {
|
||||
|
@ -455,21 +441,20 @@ JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_setupVmu(JNIEnv *env,
|
|||
|
||||
JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_pause(JNIEnv *env,jobject obj)
|
||||
{
|
||||
dc_pause();
|
||||
dc_pause_emu();
|
||||
if (game_started)
|
||||
dc_stop();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_resume(JNIEnv *env,jobject obj)
|
||||
{
|
||||
dc_resume_emu(true);
|
||||
if (game_started)
|
||||
dc_resume();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_stop(JNIEnv *env,jobject obj)
|
||||
{
|
||||
if (dc_is_running()) {
|
||||
if (game_started)
|
||||
dc_stop();
|
||||
rend_cancel_emu_wait();
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_destroy(JNIEnv *env,jobject obj)
|
||||
|
@ -651,9 +636,12 @@ JNIEXPORT jboolean JNICALL Java_com_reicast_emulator_emu_JNIdc_guiIsOpen(JNIEnv
|
|||
u32 androidaudio_push(void* frame, u32 amt, bool wait)
|
||||
{
|
||||
verify(amt==SAMPLE_COUNT);
|
||||
JVMAttacher attacher; // FIXME don't attach/detach every call...
|
||||
if (attacher.failed())
|
||||
return 0;
|
||||
//yeah, do some audio piping magic here !
|
||||
jenv->SetShortArrayRegion(jsamples,0,amt*2,(jshort*)frame);
|
||||
return jenv->CallIntMethod(emu,writemid,jsamples,wait);
|
||||
attacher.env->SetShortArrayRegion(jsamples, 0, amt * 2, (jshort *)frame);
|
||||
return attacher.env->CallIntMethod(emu, writemid, jsamples, wait);
|
||||
}
|
||||
|
||||
void androidaudio_init()
|
||||
|
@ -705,13 +693,25 @@ void os_DebugBreak()
|
|||
{
|
||||
// TODO: notify the parent thread about it ...
|
||||
|
||||
//raise(SIGABRT);
|
||||
pthread_exit(NULL);
|
||||
raise(SIGABRT);
|
||||
//pthread_exit(NULL);
|
||||
|
||||
// Attach debugger here to figure out what went wrong
|
||||
for(;;) ;
|
||||
}
|
||||
|
||||
void SaveAndroidSettings()
|
||||
{
|
||||
JVMAttacher attacher;
|
||||
if (attacher.failed())
|
||||
return;
|
||||
|
||||
jstring homeDirectory = attacher.env->NewStringUTF(get_writable_config_path("").c_str());
|
||||
|
||||
attacher.env->CallVoidMethod(g_emulator, saveAndroidSettingsMid, homeDirectory);
|
||||
attacher.env->DeleteLocalRef(homeDirectory);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_init(JNIEnv *env, jobject obj)
|
||||
{
|
||||
input_device_manager = env->NewGlobalRef(obj);
|
||||
|
|
Loading…
Reference in New Issue