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:
Flyinghead 2019-02-25 17:52:53 +01:00
parent 56c0358eaf
commit 492e771272
29 changed files with 820 additions and 511 deletions

View File

@ -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);

View File

@ -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;
}

View File

@ -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()

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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();
}

View File

@ -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));
}

View File

@ -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)

View File

@ -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

View File

@ -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;

View File

@ -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() ;
}

View File

@ -689,7 +689,7 @@ bool reios_init(u8* rom, u8* flash) {
}
void reios_reset() {
pre_init = false;
}
void reios_term() {

View File

@ -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;

View File

@ -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;

213
core/rend/gui_util.cpp Normal file
View File

@ -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();
}

23
core/rend/gui_util.h Normal file
View File

@ -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);

View File

@ -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:

View File

@ -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);

View File

@ -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()

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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"));
}
}
}

View File

@ -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);

View File

@ -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();

View File

@ -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);

View File

@ -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);

View File

@ -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");
}

View File

@ -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);