openGL is working!

This commit is contained in:
David Griswold 2025-03-11 16:24:32 -03:00
parent c22ac3367e
commit 3929d44815
8 changed files with 264 additions and 168 deletions

View File

@ -126,8 +126,9 @@ object NativeLibrary {
external fun doFrame() external fun doFrame()
//Second window //Second window
external fun enableSecondWindow(secondary_surface: Surface) external fun secondarySurfaceChanged(secondary_surface: Surface)
external fun disableSecondWindow() external fun secondarySurfaceDestroyed()
external fun disableSecondaryScreen()
/** /**
* Unpauses emulation from a paused state. * Unpauses emulation from a paused state.
*/ */

View File

@ -67,17 +67,23 @@ class EmulationActivity : AppCompatActivity() {
private fun updatePresentation() { private fun updatePresentation() {
displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
val display = getCustomerDisplay(); val display = getCustomerDisplay();
if (secondScreenPresentation == null || secondScreenPresentation?.display != display) { if (secondScreenPresentation != null && (IntSetting.SECONDARY_SCREEN_LAYOUT.int == SecondaryScreenLayout.NONE.int || display == null)) {
secondScreenPresentation?.dismiss() releasePresentation();
if (display != null && IntSetting.SECONDARY_SCREEN_LAYOUT.int != SecondaryScreenLayout.NONE.int) { }
secondScreenPresentation = SecondScreenPresentation(this, display) if (secondScreenPresentation == null || secondScreenPresentation?.display != display) {
secondScreenPresentation?.show(); secondScreenPresentation?.dismiss()
} if (display != null && IntSetting.SECONDARY_SCREEN_LAYOUT.int != SecondaryScreenLayout.NONE.int) {
secondScreenPresentation = SecondScreenPresentation(this, display)
secondScreenPresentation?.show();
} }
}
} }
private fun releasePresentation() { private fun releasePresentation() {
secondScreenPresentation?.dismiss(); if (secondScreenPresentation != null) {
secondScreenPresentation = null; NativeLibrary.disableSecondaryScreen()
secondScreenPresentation?.dismiss();
secondScreenPresentation = null;
}
} }
private fun getCustomerDisplay(): Display? { private fun getCustomerDisplay(): Display? {

View File

@ -6,6 +6,8 @@ import android.view.SurfaceHolder
import android.view.SurfaceView import android.view.SurfaceView
import org.citra.citra_emu.NativeLibrary import org.citra.citra_emu.NativeLibrary
class SecondScreenPresentation( class SecondScreenPresentation(
context: Context, context: Context,
display: Display, display: Display,
@ -19,7 +21,7 @@ class SecondScreenPresentation(
surfaceView = SurfaceView(context) surfaceView = SurfaceView(context)
surfaceView.holder.addCallback(object : SurfaceHolder.Callback { surfaceView.holder.addCallback(object : SurfaceHolder.Callback {
override fun surfaceCreated(holder: SurfaceHolder) { override fun surfaceCreated(holder: SurfaceHolder) {
NativeLibrary.enableSecondWindow(holder.surface)
} }
override fun surfaceChanged( override fun surfaceChanged(
@ -28,11 +30,11 @@ class SecondScreenPresentation(
width: Int, width: Int,
height: Int height: Int
) { ) {
NativeLibrary.enableSecondWindow(holder.surface) NativeLibrary.secondarySurfaceChanged(holder.surface)
} }
override fun surfaceDestroyed(holder: SurfaceHolder) { override fun surfaceDestroyed(holder: SurfaceHolder) {
NativeLibrary.disableSecondWindow(); NativeLibrary.secondarySurfaceDestroyed()
} }
}) })

View File

@ -16,11 +16,15 @@
#include <core/hle/service/cfg/cfg.h> #include <core/hle/service/cfg/cfg.h>
#include "audio_core/dsp_interface.h" #include "audio_core/dsp_interface.h"
#include "common/arch.h" #include "common/arch.h"
#if CITRA_ARCH(arm64) #if CITRA_ARCH(arm64)
#include "common/aarch64/cpu_detect.h" #include "common/aarch64/cpu_detect.h"
#elif CITRA_ARCH(x86_64) #elif CITRA_ARCH(x86_64)
#include "common/x64/cpu_detect.h" #include "common/x64/cpu_detect.h"
#endif #endif
#include "common/common_paths.h" #include "common/common_paths.h"
#include "common/dynamic_library/dynamic_library.h" #include "common/dynamic_library/dynamic_library.h"
#include "common/file_util.h" #include "common/file_util.h"
@ -44,12 +48,18 @@
#include "jni/camera/ndk_camera.h" #include "jni/camera/ndk_camera.h"
#include "jni/camera/still_image_camera.h" #include "jni/camera/still_image_camera.h"
#include "jni/config.h" #include "jni/config.h"
#ifdef ENABLE_OPENGL #ifdef ENABLE_OPENGL
#include "jni/emu_window/emu_window_gl.h" #include "jni/emu_window/emu_window_gl.h"
#endif #endif
#ifdef ENABLE_VULKAN #ifdef ENABLE_VULKAN
#include "jni/emu_window/emu_window_vk.h" #include "jni/emu_window/emu_window_vk.h"
#endif #endif
#include "jni/id_cache.h" #include "jni/id_cache.h"
#include "jni/input_manager.h" #include "jni/input_manager.h"
#include "jni/ndk_motion.h" #include "jni/ndk_motion.h"
@ -59,61 +69,63 @@
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
#if defined(ENABLE_VULKAN) && CITRA_ARCH(arm64) #if defined(ENABLE_VULKAN) && CITRA_ARCH(arm64)
#include <adrenotools/driver.h> #include <adrenotools/driver.h>
#endif #endif
namespace { namespace {
ANativeWindow* s_surf; ANativeWindow *s_surf;
ANativeWindow* s_secondary_surface; ANativeWindow *s_secondary_surface;
bool secondary_enabled = false; bool secondary_enabled = false;
std::shared_ptr<Common::DynamicLibrary> vulkan_library{}; std::shared_ptr<Common::DynamicLibrary> vulkan_library{};
std::unique_ptr<EmuWindow_Android> window; std::unique_ptr<EmuWindow_Android> window;
std::unique_ptr<EmuWindow_Android> second_window; std::unique_ptr<EmuWindow_Android> second_window;
std::atomic<bool> stop_run{true}; std::atomic<bool> stop_run{true};
std::atomic<bool> pause_emulation{false}; std::atomic<bool> pause_emulation{false};
std::mutex paused_mutex; std::mutex paused_mutex;
std::mutex running_mutex; std::mutex running_mutex;
std::condition_variable running_cv; std::condition_variable running_cv;
} // Anonymous namespace } // Anonymous namespace
static jobject ToJavaCoreError(Core::System::ResultStatus result) { static jobject ToJavaCoreError(Core::System::ResultStatus result) {
static const std::map<Core::System::ResultStatus, const char*> CoreErrorNameMap{ static const std::map<Core::System::ResultStatus, const char *> CoreErrorNameMap{
{Core::System::ResultStatus::ErrorSystemFiles, "ErrorSystemFiles"}, {Core::System::ResultStatus::ErrorSystemFiles, "ErrorSystemFiles"},
{Core::System::ResultStatus::ErrorSavestate, "ErrorSavestate"}, {Core::System::ResultStatus::ErrorSavestate, "ErrorSavestate"},
{Core::System::ResultStatus::ErrorArticDisconnected, "ErrorArticDisconnected"}, {Core::System::ResultStatus::ErrorArticDisconnected, "ErrorArticDisconnected"},
{Core::System::ResultStatus::ErrorUnknown, "ErrorUnknown"}, {Core::System::ResultStatus::ErrorUnknown, "ErrorUnknown"},
}; };
const auto name = CoreErrorNameMap.count(result) ? CoreErrorNameMap.at(result) : "ErrorUnknown"; const auto name = CoreErrorNameMap.count(result) ? CoreErrorNameMap.at(result) : "ErrorUnknown";
JNIEnv* env = IDCache::GetEnvForThread(); JNIEnv *env = IDCache::GetEnvForThread();
const jclass core_error_class = IDCache::GetCoreErrorClass(); const jclass core_error_class = IDCache::GetCoreErrorClass();
return env->GetStaticObjectField( return env->GetStaticObjectField(
core_error_class, env->GetStaticFieldID(core_error_class, name, core_error_class, env->GetStaticFieldID(core_error_class, name,
"Lorg/citra/citra_emu/NativeLibrary$CoreError;")); "Lorg/citra/citra_emu/NativeLibrary$CoreError;"));
} }
static bool HandleCoreError(Core::System::ResultStatus result, const std::string& details) { static bool HandleCoreError(Core::System::ResultStatus result, const std::string &details) {
JNIEnv* env = IDCache::GetEnvForThread(); JNIEnv *env = IDCache::GetEnvForThread();
return env->CallStaticBooleanMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnCoreError(), return env->CallStaticBooleanMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnCoreError(),
ToJavaCoreError(result), ToJavaCoreError(result),
env->NewStringUTF(details.c_str())) != JNI_FALSE; env->NewStringUTF(details.c_str())) != JNI_FALSE;
} }
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max) { static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max) {
JNIEnv* env = IDCache::GetEnvForThread(); JNIEnv *env = IDCache::GetEnvForThread();
env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(), env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(),
IDCache::GetDiskCacheLoadProgress(), IDCache::GetDiskCacheLoadProgress(),
IDCache::GetJavaLoadCallbackStage(stage), static_cast<jint>(progress), IDCache::GetJavaLoadCallbackStage(stage), static_cast<jint>(progress),
static_cast<jint>(max)); static_cast<jint>(max));
} }
static Camera::NDK::Factory* g_ndk_factory{}; static Camera::NDK::Factory *g_ndk_factory{};
static void TryShutdown() { static void TryShutdown() {
if (!window) { if (!window) {
@ -134,7 +146,7 @@ static bool CheckMicPermission() {
IDCache::GetRequestMicPermission()); IDCache::GetRequestMicPermission());
} }
static Core::System::ResultStatus RunCitra(const std::string& filepath) { static Core::System::ResultStatus RunCitra(const std::string &filepath) {
// Citra core only supports a single running instance // Citra core only supports a single running instance
std::scoped_lock lock(running_mutex); std::scoped_lock lock(running_mutex);
@ -147,43 +159,49 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
return Core::System::ResultStatus::ErrorLoader; return Core::System::ResultStatus::ErrorLoader;
} }
Core::System& system{Core::System::GetInstance()}; Core::System &system{Core::System::GetInstance()};
const auto graphics_api = Settings::values.graphics_api.GetValue(); const auto graphics_api = Settings::values.graphics_api.GetValue();
switch (graphics_api) { switch (graphics_api) {
#ifdef ENABLE_OPENGL #ifdef ENABLE_OPENGL
case Settings::GraphicsAPI::OpenGL: case Settings::GraphicsAPI::OpenGL:
window = std::make_unique<EmuWindow_Android_OpenGL>(system, s_surf,false); window = std::make_unique<EmuWindow_Android_OpenGL>(system, s_surf, false);
if (secondary_enabled) { if (secondary_enabled) {
EGLContext* c = window->GetEGLContext(); EGLContext *c = window->GetEGLContext();
second_window = std::make_unique<EmuWindow_Android_OpenGL>(system,s_secondary_surface, true, c); second_window = std::make_unique<EmuWindow_Android_OpenGL>(system,
} s_secondary_surface,
break; true, c);
}
break;
#endif #endif
#ifdef ENABLE_VULKAN #ifdef ENABLE_VULKAN
case Settings::GraphicsAPI::Vulkan: case Settings::GraphicsAPI::Vulkan:
window = std::make_unique<EmuWindow_Android_Vulkan>(s_surf, vulkan_library,false); window = std::make_unique<EmuWindow_Android_Vulkan>(s_surf, vulkan_library, false);
if (secondary_enabled) second_window = std::make_unique<EmuWindow_Android_Vulkan>(s_secondary_surface, vulkan_library, true); if (secondary_enabled)
break; second_window = std::make_unique<EmuWindow_Android_Vulkan>(s_secondary_surface,
vulkan_library, true);
break;
#endif #endif
default: default:
LOG_CRITICAL(Frontend, LOG_CRITICAL(Frontend,
"Unknown or unsupported graphics API {}, falling back to available default", "Unknown or unsupported graphics API {}, falling back to available default",
graphics_api); graphics_api);
#ifdef ENABLE_OPENGL #ifdef ENABLE_OPENGL
window = std::make_unique<EmuWindow_Android_OpenGL>(system, s_surf, false); window = std::make_unique<EmuWindow_Android_OpenGL>(system, s_surf, false);
if (secondary_enabled) { if (secondary_enabled) {
EGLContext *c = window->GetEGLContext(); EGLContext *c = window->GetEGLContext();
second_window = std::make_unique<EmuWindow_Android_OpenGL>(system, s_secondary_surface,true, c); second_window = std::make_unique<EmuWindow_Android_OpenGL>(system,
} s_secondary_surface,
true, c);
}
#elif ENABLE_VULKAN #elif ENABLE_VULKAN
window = std::make_unique<EmuWindow_Android_Vulkan>(s_surf, vulkan_library); window = std::make_unique<EmuWindow_Android_Vulkan>(s_surf, vulkan_library);
if (secondary_enabled) second_window = std::make_unique<EmuWindow_Android_Vulkan>(s_secondary_surface, vulkan_library, true); if (secondary_enabled) second_window = std::make_unique<EmuWindow_Android_Vulkan>(s_secondary_surface, vulkan_library, true);
#else #else
// TODO: Add a null renderer backend for this, perhaps. // TODO: Add a null renderer backend for this, perhaps.
#error "At least one renderer must be enabled." #error "At least one renderer must be enabled."
#endif #endif
break; break;
} }
// Forces a config reload on game boot, if the user changed settings in the UI // Forces a config reload on game boot, if the user changed settings in the UI
@ -217,8 +235,8 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
InputManager::Init(); InputManager::Init();
window->MakeCurrent(); window->MakeCurrent();
//if (second_window) second_window->MakeCurrent(); const Core::System::ResultStatus load_result{
const Core::System::ResultStatus load_result{system.Load(*window, filepath,second_window.get())}; system.Load(*window, filepath, second_window.get())};
if (load_result != Core::System::ResultStatus::Success) { if (load_result != Core::System::ResultStatus::Success) {
return load_result; return load_result;
} }
@ -272,12 +290,12 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
return Core::System::ResultStatus::Success; return Core::System::ResultStatus::Success;
} }
void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir, void InitializeGpuDriver(const std::string &hook_lib_dir, const std::string &custom_driver_dir,
const std::string& custom_driver_name, const std::string &custom_driver_name,
const std::string& file_redirect_dir) { const std::string &file_redirect_dir) {
#if defined(ENABLE_VULKAN) && CITRA_ARCH(arm64) #if defined(ENABLE_VULKAN) && CITRA_ARCH(arm64)
void* handle{}; void *handle{};
const char* file_redirect_dir_{}; const char *file_redirect_dir_{};
int featureFlags{}; int featureFlags{};
// Enable driver file redirection when renderer debugging is enabled. // Enable driver file redirection when renderer debugging is enabled.
@ -289,8 +307,8 @@ void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& cus
// Try to load a custom driver. // Try to load a custom driver.
if (custom_driver_name.size()) { if (custom_driver_name.size()) {
handle = adrenotools_open_libvulkan( handle = adrenotools_open_libvulkan(
RTLD_NOW, featureFlags | ADRENOTOOLS_DRIVER_CUSTOM, nullptr, hook_lib_dir.c_str(), RTLD_NOW, featureFlags | ADRENOTOOLS_DRIVER_CUSTOM, nullptr, hook_lib_dir.c_str(),
custom_driver_dir.c_str(), custom_driver_name.c_str(), file_redirect_dir_, nullptr); custom_driver_dir.c_str(), custom_driver_name.c_str(), file_redirect_dir_, nullptr);
} }
// Try to load the system driver. // Try to load the system driver.
@ -305,7 +323,7 @@ void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& cus
extern "C" { extern "C" {
void Java_org_citra_citra_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_surfaceChanged(JNIEnv *env,
[[maybe_unused]] jobject obj, [[maybe_unused]] jobject obj,
jobject surf) { jobject surf) {
s_surf = ANativeWindow_fromSurface(env, surf); s_surf = ANativeWindow_fromSurface(env, surf);
@ -315,7 +333,7 @@ void Java_org_citra_citra_1emu_NativeLibrary_surfaceChanged(JNIEnv* env,
notify = window->OnSurfaceChanged(s_surf); notify = window->OnSurfaceChanged(s_surf);
} }
auto& system = Core::System::GetInstance(); auto &system = Core::System::GetInstance();
if (notify && system.IsPoweredOn()) { if (notify && system.IsPoweredOn()) {
system.GPU().Renderer().NotifySurfaceChanged(false); system.GPU().Renderer().NotifySurfaceChanged(false);
} }
@ -323,15 +341,39 @@ void Java_org_citra_citra_1emu_NativeLibrary_surfaceChanged(JNIEnv* env,
LOG_INFO(Frontend, "Surface changed"); LOG_INFO(Frontend, "Surface changed");
} }
void Java_org_citra_citra_1emu_NativeLibrary_enableSecondWindow(JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_secondarySurfaceChanged(JNIEnv *env,
[[maybe_unused]] jobject obj,jobject surf) { [[maybe_unused]] jobject obj,
jobject surf) {
auto &system = Core::System::GetInstance();
s_secondary_surface = ANativeWindow_fromSurface(env, surf); s_secondary_surface = ANativeWindow_fromSurface(env, surf);
secondary_enabled = true; secondary_enabled = true;
bool notify = false; bool notify = false;
if (second_window) { if (!s_secondary_surface) {
notify = second_window->OnSurfaceChanged(s_secondary_surface); // did not create the surface, so disable second screen
secondary_enabled = false;
if (system.IsPoweredOn()) {
system.GPU().Renderer().setSecondaryWindow(nullptr);
}
return;
} }
auto& system = Core::System::GetInstance(); if (second_window) {
//second window already created, so update it
notify = second_window->OnSurfaceChanged(s_secondary_surface);
} else if (system.IsPoweredOn() && window) {
// emulation running, window is new
// create a new window and set it
const auto graphics_api = Settings::values.graphics_api.GetValue();
if (graphics_api == Settings::GraphicsAPI::OpenGL) {
EGLContext *c = window->GetEGLContext();
second_window = std::make_unique<EmuWindow_Android_OpenGL>(system,
s_secondary_surface,true, c);
}else{
second_window = std::make_unique<EmuWindow_Android_Vulkan>(s_secondary_surface,
vulkan_library, true);
}
system.GPU().Renderer().setSecondaryWindow(second_window.get());
}
if (notify && system.IsPoweredOn()) { if (notify && system.IsPoweredOn()) {
system.GPU().Renderer().NotifySurfaceChanged(true); system.GPU().Renderer().NotifySurfaceChanged(true);
} }
@ -340,23 +382,37 @@ void Java_org_citra_citra_1emu_NativeLibrary_enableSecondWindow(JNIEnv* env,
} }
void Java_org_citra_citra_1emu_NativeLibrary_secondarySurfaceDestroyed(JNIEnv *env,
void Java_org_citra_citra_1emu_NativeLibrary_disableSecondWindow(JNIEnv* env, [[maybe_unused]] jobject obj) {
[[maybe_unused]] jobject obj) { //auto &system = Core::System::GetInstance();
secondary_enabled = false; secondary_enabled = false;
if (s_secondary_surface != nullptr) { if (s_secondary_surface != nullptr) {
ANativeWindow_release(s_secondary_surface); ANativeWindow_release(s_secondary_surface);
s_secondary_surface = nullptr; s_secondary_surface = nullptr;
if (second_window) {
second_window->OnSurfaceChanged(s_secondary_surface);
}
} }
LOG_INFO(Frontend, "Secondary Surface Disabled"); LOG_INFO(Frontend, "Secondary Surface Destroyed");
} }
void Java_org_citra_citra_1emu_NativeLibrary_surfaceDestroyed([[maybe_unused]] JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_disableSecondaryScreen(JNIEnv *env,
[[maybe_unused]] jobject obj) {
auto &system = Core::System::GetInstance();
secondary_enabled = false;
if (s_secondary_surface != nullptr) {
ANativeWindow_release(s_secondary_surface);
s_secondary_surface = nullptr;
}
if (system.IsPoweredOn()) {
system.GPU().Renderer().setSecondaryWindow(nullptr);
}
if (second_window) {
second_window.release();
second_window = nullptr;
}
LOG_INFO(Frontend, "Secondary Window Disabled");
}
void Java_org_citra_citra_1emu_NativeLibrary_surfaceDestroyed([[maybe_unused]] JNIEnv *env,
[[maybe_unused]] jobject obj) { [[maybe_unused]] jobject obj) {
if (s_surf != nullptr) { if (s_surf != nullptr) {
ANativeWindow_release(s_surf); ANativeWindow_release(s_surf);
@ -367,7 +423,7 @@ void Java_org_citra_citra_1emu_NativeLibrary_surfaceDestroyed([[maybe_unused]] J
} }
} }
void Java_org_citra_citra_1emu_NativeLibrary_doFrame([[maybe_unused]] JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_doFrame([[maybe_unused]] JNIEnv *env,
[[maybe_unused]] jobject obj) { [[maybe_unused]] jobject obj) {
if (stop_run || pause_emulation) { if (stop_run || pause_emulation) {
return; return;
@ -381,33 +437,33 @@ void Java_org_citra_citra_1emu_NativeLibrary_doFrame([[maybe_unused]] JNIEnv* en
} }
void JNICALL Java_org_citra_citra_1emu_NativeLibrary_initializeGpuDriver( void JNICALL Java_org_citra_citra_1emu_NativeLibrary_initializeGpuDriver(
JNIEnv* env, jobject obj, jstring hook_lib_dir, jstring custom_driver_dir, JNIEnv *env, jobject obj, jstring hook_lib_dir, jstring custom_driver_dir,
jstring custom_driver_name, jstring file_redirect_dir) { jstring custom_driver_name, jstring file_redirect_dir) {
InitializeGpuDriver(GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir), InitializeGpuDriver(GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir),
GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir)); GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir));
} }
void Java_org_citra_citra_1emu_NativeLibrary_notifyOrientationChange([[maybe_unused]] JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_notifyOrientationChange([[maybe_unused]] JNIEnv *env,
[[maybe_unused]] jobject obj, [[maybe_unused]] jobject obj,
jint layout_option, jint layout_option,
jint rotation, jint rotation,
jboolean portrait) { jboolean portrait) {
Settings::values.layout_option = static_cast<Settings::LayoutOption>(layout_option); Settings::values.layout_option = static_cast<Settings::LayoutOption>(layout_option);
} }
void Java_org_citra_citra_1emu_NativeLibrary_updateFramebuffer([[maybe_unused]] JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_updateFramebuffer([[maybe_unused]] JNIEnv *env,
[[maybe_unused]] jobject obj, [[maybe_unused]] jobject obj,
jboolean is_portrait_mode) { jboolean is_portrait_mode) {
auto& system = Core::System::GetInstance(); auto &system = Core::System::GetInstance();
if (system.IsPoweredOn()) { if (system.IsPoweredOn()) {
system.GPU().Renderer().UpdateCurrentFramebufferLayout(is_portrait_mode); system.GPU().Renderer().UpdateCurrentFramebufferLayout(is_portrait_mode);
} }
} }
void Java_org_citra_citra_1emu_NativeLibrary_swapScreens([[maybe_unused]] JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_swapScreens([[maybe_unused]] JNIEnv *env,
[[maybe_unused]] jobject obj, [[maybe_unused]] jobject obj,
jboolean swap_screens, jint rotation) { jboolean swap_screens, jint rotation) {
Settings::values.swap_screen = swap_screens; Settings::values.swap_screen = swap_screens;
auto& system = Core::System::GetInstance(); auto &system = Core::System::GetInstance();
if (system.IsPoweredOn()) { if (system.IsPoweredOn()) {
system.GPU().Renderer().UpdateCurrentFramebufferLayout(IsPortraitMode()); system.GPU().Renderer().UpdateCurrentFramebufferLayout(IsPortraitMode());
} }
@ -415,14 +471,14 @@ void Java_org_citra_citra_1emu_NativeLibrary_swapScreens([[maybe_unused]] JNIEnv
Camera::NDK::g_rotation = rotation; Camera::NDK::g_rotation = rotation;
} }
jboolean Java_org_citra_citra_1emu_NativeLibrary_areKeysAvailable([[maybe_unused]] JNIEnv* env, jboolean Java_org_citra_citra_1emu_NativeLibrary_areKeysAvailable([[maybe_unused]] JNIEnv *env,
[[maybe_unused]] jobject obj) { [[maybe_unused]] jobject obj) {
HW::AES::InitKeys(); HW::AES::InitKeys();
return HW::AES::IsKeyXAvailable(HW::AES::KeySlotID::NCCHSecure1) && return HW::AES::IsKeyXAvailable(HW::AES::KeySlotID::NCCHSecure1) &&
HW::AES::IsKeyXAvailable(HW::AES::KeySlotID::NCCHSecure2); HW::AES::IsKeyXAvailable(HW::AES::KeySlotID::NCCHSecure2);
} }
jstring Java_org_citra_citra_1emu_NativeLibrary_getHomeMenuPath(JNIEnv* env, jstring Java_org_citra_citra_1emu_NativeLibrary_getHomeMenuPath(JNIEnv *env,
[[maybe_unused]] jobject obj, [[maybe_unused]] jobject obj,
jint region) { jint region) {
const std::string path = Core::GetHomeMenuNcchPath(region); const std::string path = Core::GetHomeMenuNcchPath(region);
@ -432,43 +488,44 @@ jstring Java_org_citra_citra_1emu_NativeLibrary_getHomeMenuPath(JNIEnv* env,
return ToJString(env, ""); return ToJString(env, "");
} }
void Java_org_citra_citra_1emu_NativeLibrary_setUserDirectory(JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_setUserDirectory(JNIEnv *env,
[[maybe_unused]] jobject obj, [[maybe_unused]] jobject obj,
jstring j_directory) { jstring j_directory) {
FileUtil::SetCurrentDir(GetJString(env, j_directory)); FileUtil::SetCurrentDir(GetJString(env, j_directory));
} }
jobjectArray Java_org_citra_citra_1emu_NativeLibrary_getInstalledGamePaths( jobjectArray Java_org_citra_citra_1emu_NativeLibrary_getInstalledGamePaths(
JNIEnv* env, [[maybe_unused]] jclass clazz) { JNIEnv *env, [[maybe_unused]] jclass clazz) {
std::vector<std::string> games; std::vector<std::string> games;
const FileUtil::DirectoryEntryCallable ScanDir = const FileUtil::DirectoryEntryCallable ScanDir =
[&games, &ScanDir](u64*, const std::string& directory, const std::string& virtual_name) { [&games, &ScanDir](u64 *, const std::string &directory,
std::string path = directory + virtual_name; const std::string &virtual_name) {
if (FileUtil::IsDirectory(path)) { std::string path = directory + virtual_name;
path += '/'; if (FileUtil::IsDirectory(path)) {
FileUtil::ForeachDirectoryEntry(nullptr, path, ScanDir); path += '/';
} else { FileUtil::ForeachDirectoryEntry(nullptr, path, ScanDir);
if (!FileUtil::Exists(path)) } else {
return false; if (!FileUtil::Exists(path))
auto loader = Loader::GetLoader(path); return false;
if (loader) { auto loader = Loader::GetLoader(path);
bool executable{}; if (loader) {
const Loader::ResultStatus result = loader->IsExecutable(executable); bool executable{};
if (Loader::ResultStatus::Success == result && executable) { const Loader::ResultStatus result = loader->IsExecutable(executable);
games.emplace_back(path); if (Loader::ResultStatus::Success == result && executable) {
games.emplace_back(path);
}
} }
} }
} return true;
return true; };
};
ScanDir(nullptr, "", ScanDir(nullptr, "",
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) +
"Nintendo " "Nintendo "
"3DS/00000000000000000000000000000000/" "3DS/00000000000000000000000000000000/"
"00000000000000000000000000000000/title/00040000"); "00000000000000000000000000000000/title/00040000");
ScanDir(nullptr, "", ScanDir(nullptr, "",
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
"00000000000000000000000000000000/title/00040010"); "00000000000000000000000000000000/title/00040010");
jobjectArray jgames = env->NewObjectArray(static_cast<jsize>(games.size()), jobjectArray jgames = env->NewObjectArray(static_cast<jsize>(games.size()),
env->FindClass("java/lang/String"), nullptr); env->FindClass("java/lang/String"), nullptr);
for (jsize i = 0; i < games.size(); ++i) for (jsize i = 0; i < games.size(); ++i)
@ -476,7 +533,7 @@ jobjectArray Java_org_citra_citra_1emu_NativeLibrary_getInstalledGamePaths(
return jgames; return jgames;
} }
jlongArray Java_org_citra_citra_1emu_NativeLibrary_getSystemTitleIds(JNIEnv* env, jlongArray Java_org_citra_citra_1emu_NativeLibrary_getSystemTitleIds(JNIEnv *env,
[[maybe_unused]] jobject obj, [[maybe_unused]] jobject obj,
jint system_type, jint system_type,
jint region) { jint region) {
@ -484,11 +541,11 @@ jlongArray Java_org_citra_citra_1emu_NativeLibrary_getSystemTitleIds(JNIEnv* env
const std::vector<u64> titles = Core::GetSystemTitleIds(mode, region); const std::vector<u64> titles = Core::GetSystemTitleIds(mode, region);
jlongArray jTitles = env->NewLongArray(titles.size()); jlongArray jTitles = env->NewLongArray(titles.size());
env->SetLongArrayRegion(jTitles, 0, titles.size(), env->SetLongArrayRegion(jTitles, 0, titles.size(),
reinterpret_cast<const jlong*>(titles.data())); reinterpret_cast<const jlong *>(titles.data()));
return jTitles; return jTitles;
} }
jobject Java_org_citra_citra_1emu_NativeLibrary_downloadTitleFromNus([[maybe_unused]] JNIEnv* env, jobject Java_org_citra_citra_1emu_NativeLibrary_downloadTitleFromNus([[maybe_unused]] JNIEnv *env,
[[maybe_unused]] jobject obj, [[maybe_unused]] jobject obj,
jlong title) { jlong title) {
[[maybe_unused]] const auto title_id = static_cast<u64>(title); [[maybe_unused]] const auto title_id = static_cast<u64>(title);
@ -506,7 +563,7 @@ jobject Java_org_citra_citra_1emu_NativeLibrary_downloadTitleFromNus([[maybe_unu
} }
jboolean JNICALL Java_org_citra_citra_1emu_utils_GpuDriverHelper_supportsCustomDriverLoading( jboolean JNICALL Java_org_citra_citra_1emu_utils_GpuDriverHelper_supportsCustomDriverLoading(
JNIEnv* env, jobject instance) { JNIEnv *env, jobject instance) {
#ifdef CITRA_ARCH_arm64 #ifdef CITRA_ARCH_arm64
// If the KGSL device exists custom drivers can be loaded using adrenotools // If the KGSL device exists custom drivers can be loaded using adrenotools
return SupportsCustomDriver(); return SupportsCustomDriver();
@ -516,20 +573,20 @@ jboolean JNICALL Java_org_citra_citra_1emu_utils_GpuDriverHelper_supportsCustomD
} }
// TODO(xperia64): ensure these cannot be called in an invalid state (e.g. after StopEmulation) // TODO(xperia64): ensure these cannot be called in an invalid state (e.g. after StopEmulation)
void Java_org_citra_citra_1emu_NativeLibrary_unPauseEmulation([[maybe_unused]] JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_unPauseEmulation([[maybe_unused]] JNIEnv *env,
[[maybe_unused]] jobject obj) { [[maybe_unused]] jobject obj) {
pause_emulation = false; pause_emulation = false;
running_cv.notify_all(); running_cv.notify_all();
InputManager::NDKMotionHandler()->EnableSensors(); InputManager::NDKMotionHandler()->EnableSensors();
} }
void Java_org_citra_citra_1emu_NativeLibrary_pauseEmulation([[maybe_unused]] JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_pauseEmulation([[maybe_unused]] JNIEnv *env,
[[maybe_unused]] jobject obj) { [[maybe_unused]] jobject obj) {
pause_emulation = true; pause_emulation = true;
InputManager::NDKMotionHandler()->DisableSensors(); InputManager::NDKMotionHandler()->DisableSensors();
} }
void Java_org_citra_citra_1emu_NativeLibrary_stopEmulation([[maybe_unused]] JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_stopEmulation([[maybe_unused]] JNIEnv *env,
[[maybe_unused]] jobject obj) { [[maybe_unused]] jobject obj) {
stop_run = true; stop_run = true;
pause_emulation = false; pause_emulation = false;
@ -538,19 +595,19 @@ void Java_org_citra_citra_1emu_NativeLibrary_stopEmulation([[maybe_unused]] JNIE
running_cv.notify_all(); running_cv.notify_all();
} }
jboolean Java_org_citra_citra_1emu_NativeLibrary_isRunning([[maybe_unused]] JNIEnv* env, jboolean Java_org_citra_citra_1emu_NativeLibrary_isRunning([[maybe_unused]] JNIEnv *env,
[[maybe_unused]] jobject obj) { [[maybe_unused]] jobject obj) {
return static_cast<jboolean>(!stop_run); return static_cast<jboolean>(!stop_run);
} }
jlong Java_org_citra_citra_1emu_NativeLibrary_getRunningTitleId([[maybe_unused]] JNIEnv* env, jlong Java_org_citra_citra_1emu_NativeLibrary_getRunningTitleId([[maybe_unused]] JNIEnv *env,
[[maybe_unused]] jobject obj) { [[maybe_unused]] jobject obj) {
u64 title_id{}; u64 title_id{};
Core::System::GetInstance().GetAppLoader().ReadProgramId(title_id); Core::System::GetInstance().GetAppLoader().ReadProgramId(title_id);
return static_cast<jlong>(title_id); return static_cast<jlong>(title_id);
} }
jboolean Java_org_citra_citra_1emu_NativeLibrary_onGamePadEvent([[maybe_unused]] JNIEnv* env, jboolean Java_org_citra_citra_1emu_NativeLibrary_onGamePadEvent([[maybe_unused]] JNIEnv *env,
[[maybe_unused]] jobject obj, [[maybe_unused]] jobject obj,
[[maybe_unused]] jstring j_device, [[maybe_unused]] jstring j_device,
jint j_button, jint action) { jint j_button, jint action) {
@ -565,8 +622,9 @@ jboolean Java_org_citra_citra_1emu_NativeLibrary_onGamePadEvent([[maybe_unused]]
} }
jboolean Java_org_citra_citra_1emu_NativeLibrary_onGamePadMoveEvent( jboolean Java_org_citra_citra_1emu_NativeLibrary_onGamePadMoveEvent(
[[maybe_unused]] JNIEnv* env, [[maybe_unused]] jobject obj, [[maybe_unused]] jstring j_device, [[maybe_unused]] JNIEnv *env, [[maybe_unused]] jobject obj,
jint axis, jfloat x, jfloat y) { [[maybe_unused]] jstring j_device,
jint axis, jfloat x, jfloat y) {
// Clamp joystick movement to supported minimum and maximum // Clamp joystick movement to supported minimum and maximum
// Citra uses an inverted y axis sent by the frontend // Citra uses an inverted y axis sent by the frontend
x = std::clamp(x, -1.f, 1.f); x = std::clamp(x, -1.f, 1.f);
@ -584,27 +642,28 @@ jboolean Java_org_citra_citra_1emu_NativeLibrary_onGamePadMoveEvent(
} }
jboolean Java_org_citra_citra_1emu_NativeLibrary_onGamePadAxisEvent( jboolean Java_org_citra_citra_1emu_NativeLibrary_onGamePadAxisEvent(
[[maybe_unused]] JNIEnv* env, [[maybe_unused]] jobject obj, [[maybe_unused]] jstring j_device, [[maybe_unused]] JNIEnv *env, [[maybe_unused]] jobject obj,
jint axis_id, jfloat axis_val) { [[maybe_unused]] jstring j_device,
jint axis_id, jfloat axis_val) {
return static_cast<jboolean>( return static_cast<jboolean>(
InputManager::ButtonHandler()->AnalogButtonEvent(axis_id, axis_val)); InputManager::ButtonHandler()->AnalogButtonEvent(axis_id, axis_val));
} }
jboolean Java_org_citra_citra_1emu_NativeLibrary_onTouchEvent([[maybe_unused]] JNIEnv* env, jboolean Java_org_citra_citra_1emu_NativeLibrary_onTouchEvent([[maybe_unused]] JNIEnv *env,
[[maybe_unused]] jobject obj, [[maybe_unused]] jobject obj,
jfloat x, jfloat y, jfloat x, jfloat y,
jboolean pressed) { jboolean pressed) {
return static_cast<jboolean>( return static_cast<jboolean>(
window->OnTouchEvent(static_cast<int>(x + 0.5), static_cast<int>(y + 0.5), pressed)); window->OnTouchEvent(static_cast<int>(x + 0.5), static_cast<int>(y + 0.5), pressed));
} }
void Java_org_citra_citra_1emu_NativeLibrary_onTouchMoved([[maybe_unused]] JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_onTouchMoved([[maybe_unused]] JNIEnv *env,
[[maybe_unused]] jobject obj, jfloat x, [[maybe_unused]] jobject obj, jfloat x,
jfloat y) { jfloat y) {
window->OnTouchMoved((int)x, (int)y); window->OnTouchMoved((int) x, (int) y);
} }
jlong Java_org_citra_citra_1emu_NativeLibrary_getTitleId(JNIEnv* env, [[maybe_unused]] jobject obj, jlong Java_org_citra_citra_1emu_NativeLibrary_getTitleId(JNIEnv *env, [[maybe_unused]] jobject obj,
jstring j_filename) { jstring j_filename) {
std::string filepath = GetJString(env, j_filename); std::string filepath = GetJString(env, j_filename);
const auto loader = Loader::GetLoader(filepath); const auto loader = Loader::GetLoader(filepath);
@ -616,7 +675,7 @@ jlong Java_org_citra_citra_1emu_NativeLibrary_getTitleId(JNIEnv* env, [[maybe_un
return static_cast<jlong>(title_id); return static_cast<jlong>(title_id);
} }
jboolean Java_org_citra_citra_1emu_NativeLibrary_getIsSystemTitle(JNIEnv* env, jboolean Java_org_citra_citra_1emu_NativeLibrary_getIsSystemTitle(JNIEnv *env,
[[maybe_unused]] jobject obj, [[maybe_unused]] jobject obj,
jstring path) { jstring path) {
const std::string filepath = GetJString(env, path); const std::string filepath = GetJString(env, path);
@ -632,19 +691,19 @@ jboolean Java_org_citra_citra_1emu_NativeLibrary_getIsSystemTitle(JNIEnv* env,
return ((program_id >> 32) & 0xFFFFFFFF) == 0x00040010; return ((program_id >> 32) & 0xFFFFFFFF) == 0x00040010;
} }
void Java_org_citra_citra_1emu_NativeLibrary_createConfigFile([[maybe_unused]] JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_createConfigFile([[maybe_unused]] JNIEnv *env,
[[maybe_unused]] jobject obj) { [[maybe_unused]] jobject obj) {
Config{}; Config{};
} }
void Java_org_citra_citra_1emu_NativeLibrary_createLogFile([[maybe_unused]] JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_createLogFile([[maybe_unused]] JNIEnv *env,
[[maybe_unused]] jobject obj) { [[maybe_unused]] jobject obj) {
Common::Log::Initialize(); Common::Log::Initialize();
Common::Log::Start(); Common::Log::Start();
LOG_INFO(Frontend, "Logging backend initialised"); LOG_INFO(Frontend, "Logging backend initialised");
} }
void Java_org_citra_citra_1emu_NativeLibrary_logUserDirectory(JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_logUserDirectory(JNIEnv *env,
[[maybe_unused]] jobject obj, [[maybe_unused]] jobject obj,
jstring j_path) { jstring j_path) {
std::string_view path = env->GetStringUTFChars(j_path, 0); std::string_view path = env->GetStringUTFChars(j_path, 0);
@ -652,10 +711,10 @@ void Java_org_citra_citra_1emu_NativeLibrary_logUserDirectory(JNIEnv* env,
env->ReleaseStringUTFChars(j_path, path.data()); env->ReleaseStringUTFChars(j_path, path.data());
} }
void Java_org_citra_citra_1emu_NativeLibrary_reloadSettings([[maybe_unused]] JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_reloadSettings([[maybe_unused]] JNIEnv *env,
[[maybe_unused]] jobject obj) { [[maybe_unused]] jobject obj) {
Config{}; Config{};
Core::System& system{Core::System::GetInstance()}; Core::System &system{Core::System::GetInstance()};
// Replace with game-specific settings // Replace with game-specific settings
if (system.IsPoweredOn()) { if (system.IsPoweredOn()) {
@ -666,9 +725,9 @@ void Java_org_citra_citra_1emu_NativeLibrary_reloadSettings([[maybe_unused]] JNI
system.ApplySettings(); system.ApplySettings();
} }
jdoubleArray Java_org_citra_citra_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jdoubleArray Java_org_citra_citra_1emu_NativeLibrary_getPerfStats(JNIEnv *env,
[[maybe_unused]] jobject obj) { [[maybe_unused]] jobject obj) {
auto& core = Core::System::GetInstance(); auto &core = Core::System::GetInstance();
jdoubleArray j_stats = env->NewDoubleArray(4); jdoubleArray j_stats = env->NewDoubleArray(4);
if (core.IsPoweredOn()) { if (core.IsPoweredOn()) {
@ -684,7 +743,7 @@ jdoubleArray Java_org_citra_citra_1emu_NativeLibrary_getPerfStats(JNIEnv* env,
return j_stats; return j_stats;
} }
void Java_org_citra_citra_1emu_NativeLibrary_run__Ljava_lang_String_2(JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_run__Ljava_lang_String_2(JNIEnv *env,
[[maybe_unused]] jobject obj, [[maybe_unused]] jobject obj,
jstring j_path) { jstring j_path) {
const std::string path = GetJString(env, j_path); const std::string path = GetJString(env, j_path);
@ -701,19 +760,19 @@ void Java_org_citra_citra_1emu_NativeLibrary_run__Ljava_lang_String_2(JNIEnv* en
} }
} }
void Java_org_citra_citra_1emu_NativeLibrary_reloadCameraDevices([[maybe_unused]] JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_reloadCameraDevices([[maybe_unused]] JNIEnv *env,
[[maybe_unused]] jobject obj) { [[maybe_unused]] jobject obj) {
if (g_ndk_factory) { if (g_ndk_factory) {
g_ndk_factory->ReloadCameraDevices(); g_ndk_factory->ReloadCameraDevices();
} }
} }
jboolean Java_org_citra_citra_1emu_NativeLibrary_loadAmiibo(JNIEnv* env, jboolean Java_org_citra_citra_1emu_NativeLibrary_loadAmiibo(JNIEnv *env,
[[maybe_unused]] jobject obj, [[maybe_unused]] jobject obj,
jstring j_file) { jstring j_file) {
std::string filepath = GetJString(env, j_file); std::string filepath = GetJString(env, j_file);
Core::System& system{Core::System::GetInstance()}; Core::System &system{Core::System::GetInstance()};
Service::SM::ServiceManager& sm = system.ServiceManager(); Service::SM::ServiceManager &sm = system.ServiceManager();
auto nfc = sm.GetService<Service::NFC::Module::Interface>("nfc:u"); auto nfc = sm.GetService<Service::NFC::Module::Interface>("nfc:u");
if (nfc == nullptr) { if (nfc == nullptr) {
return static_cast<jboolean>(false); return static_cast<jboolean>(false);
@ -722,10 +781,10 @@ jboolean Java_org_citra_citra_1emu_NativeLibrary_loadAmiibo(JNIEnv* env,
return static_cast<jboolean>(nfc->LoadAmiibo(filepath)); return static_cast<jboolean>(nfc->LoadAmiibo(filepath));
} }
void Java_org_citra_citra_1emu_NativeLibrary_removeAmiibo([[maybe_unused]] JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_removeAmiibo([[maybe_unused]] JNIEnv *env,
[[maybe_unused]] jobject obj) { [[maybe_unused]] jobject obj) {
Core::System& system{Core::System::GetInstance()}; Core::System &system{Core::System::GetInstance()};
Service::SM::ServiceManager& sm = system.ServiceManager(); Service::SM::ServiceManager &sm = system.ServiceManager();
auto nfc = sm.GetService<Service::NFC::Module::Interface>("nfc:u"); auto nfc = sm.GetService<Service::NFC::Module::Interface>("nfc:u");
if (nfc == nullptr) { if (nfc == nullptr) {
return; return;
@ -735,19 +794,20 @@ void Java_org_citra_citra_1emu_NativeLibrary_removeAmiibo([[maybe_unused]] JNIEn
} }
JNIEXPORT jobject JNICALL Java_org_citra_citra_1emu_utils_CiaInstallWorker_installCIA( JNIEXPORT jobject JNICALL Java_org_citra_citra_1emu_utils_CiaInstallWorker_installCIA(
JNIEnv* env, jobject jobj, jstring jpath) { JNIEnv *env, jobject jobj, jstring jpath) {
std::string path = GetJString(env, jpath); std::string path = GetJString(env, jpath);
Service::AM::InstallStatus res = Service::AM::InstallCIA( Service::AM::InstallStatus res = Service::AM::InstallCIA(
path, [env, jobj](std::size_t total_bytes_read, std::size_t file_size) { path, [env, jobj](std::size_t total_bytes_read, std::size_t file_size) {
env->CallVoidMethod(jobj, IDCache::GetCiaInstallHelperSetProgress(), env->CallVoidMethod(jobj, IDCache::GetCiaInstallHelperSetProgress(),
static_cast<jint>(file_size), static_cast<jint>(total_bytes_read)); static_cast<jint>(file_size),
}); static_cast<jint>(total_bytes_read));
});
return IDCache::GetJavaCiaInstallStatus(res); return IDCache::GetJavaCiaInstallStatus(res);
} }
jobjectArray Java_org_citra_citra_1emu_NativeLibrary_getSavestateInfo( jobjectArray Java_org_citra_citra_1emu_NativeLibrary_getSavestateInfo(
JNIEnv* env, [[maybe_unused]] jobject obj) { JNIEnv *env, [[maybe_unused]] jobject obj) {
const jclass date_class = env->FindClass("java/util/Date"); const jclass date_class = env->FindClass("java/util/Date");
const auto date_constructor = env->GetMethodID(date_class, "<init>", "(J)V"); const auto date_constructor = env->GetMethodID(date_class, "<init>", "(J)V");
@ -755,7 +815,7 @@ jobjectArray Java_org_citra_citra_1emu_NativeLibrary_getSavestateInfo(
const auto slot_field = env->GetFieldID(savestate_info_class, "slot", "I"); const auto slot_field = env->GetFieldID(savestate_info_class, "slot", "I");
const auto date_field = env->GetFieldID(savestate_info_class, "time", "Ljava/util/Date;"); const auto date_field = env->GetFieldID(savestate_info_class, "time", "Ljava/util/Date;");
const Core::System& system{Core::System::GetInstance()}; const Core::System &system{Core::System::GetInstance()};
if (!system.IsPoweredOn()) { if (!system.IsPoweredOn()) {
return nullptr; return nullptr;
} }
@ -767,7 +827,8 @@ jobjectArray Java_org_citra_citra_1emu_NativeLibrary_getSavestateInfo(
const auto savestates = Core::ListSaveStates(title_id, system.Movie().GetCurrentMovieID()); const auto savestates = Core::ListSaveStates(title_id, system.Movie().GetCurrentMovieID());
const jobjectArray array = const jobjectArray array =
env->NewObjectArray(static_cast<jsize>(savestates.size()), savestate_info_class, nullptr); env->NewObjectArray(static_cast<jsize>(savestates.size()), savestate_info_class,
nullptr);
for (std::size_t i = 0; i < savestates.size(); ++i) { for (std::size_t i = 0; i < savestates.size(); ++i) {
const jobject object = env->AllocObject(savestate_info_class); const jobject object = env->AllocObject(savestate_info_class);
env->SetIntField(object, slot_field, static_cast<jint>(savestates[i].slot)); env->SetIntField(object, slot_field, static_cast<jint>(savestates[i].slot));
@ -780,17 +841,17 @@ jobjectArray Java_org_citra_citra_1emu_NativeLibrary_getSavestateInfo(
return array; return array;
} }
void Java_org_citra_citra_1emu_NativeLibrary_saveState([[maybe_unused]] JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_saveState([[maybe_unused]] JNIEnv *env,
[[maybe_unused]] jobject obj, jint slot) { [[maybe_unused]] jobject obj, jint slot) {
Core::System::GetInstance().SendSignal(Core::System::Signal::Save, slot); Core::System::GetInstance().SendSignal(Core::System::Signal::Save, slot);
} }
void Java_org_citra_citra_1emu_NativeLibrary_loadState([[maybe_unused]] JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_loadState([[maybe_unused]] JNIEnv *env,
[[maybe_unused]] jobject obj, jint slot) { [[maybe_unused]] jobject obj, jint slot) {
Core::System::GetInstance().SendSignal(Core::System::Signal::Load, slot); Core::System::GetInstance().SendSignal(Core::System::Signal::Load, slot);
} }
void Java_org_citra_citra_1emu_NativeLibrary_logDeviceInfo([[maybe_unused]] JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_logDeviceInfo([[maybe_unused]] JNIEnv *env,
[[maybe_unused]] jobject obj) { [[maybe_unused]] jobject obj) {
LOG_INFO(Frontend, "Azahar Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, LOG_INFO(Frontend, "Azahar Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch,
Common::g_scm_desc); Common::g_scm_desc);

View File

@ -67,4 +67,13 @@ void RendererBase::RequestScreenshot(void* data, std::function<void(bool)> callb
settings.screenshot_requested = true; settings.screenshot_requested = true;
} }
Frontend::EmuWindow *RendererBase::getSecondaryWindow() const {
return secondary_window;
}
void RendererBase::setSecondaryWindow(Frontend::EmuWindow *secondaryWindow) {
secondary_window = secondaryWindow;
if (secondary_window) secondary_window->PollEvents();
}
} // namespace VideoCore } // namespace VideoCore

View File

@ -111,7 +111,14 @@ protected:
Core::System& system; Core::System& system;
RendererSettings settings; RendererSettings settings;
Frontend::EmuWindow& render_window; ///< Reference to the render window handle. Frontend::EmuWindow& render_window; ///< Reference to the render window handle.
Frontend::EmuWindow* secondary_window; ///< Reference to the secondary render window handle. Frontend::EmuWindow* secondary_window;
public:
Frontend::EmuWindow *getSecondaryWindow() const;
virtual void setSecondaryWindow(Frontend::EmuWindow *secondaryWindow);
protected:
///< Reference to the secondary render window handle.
f32 current_fps = 0.0f; ///< Current framerate, should be set by the renderer f32 current_fps = 0.0f; ///< Current framerate, should be set by the renderer
s32 current_frame = 0; ///< Current frame, should be set by the renderer s32 current_frame = 0; ///< Current frame, should be set by the renderer
}; };

View File

@ -87,6 +87,15 @@ RendererOpenGL::RendererOpenGL(Core::System& system, Pica::PicaCore& pica_,
} }
RendererOpenGL::~RendererOpenGL() = default; RendererOpenGL::~RendererOpenGL() = default;
void RendererOpenGL::setSecondaryWindow(Frontend::EmuWindow *secondaryWindow) {
if (secondaryWindow) {
secondary_window = secondaryWindow;
secondary_window->mailbox = std::make_unique<OGLTextureMailbox>(driver.HasDebugTool());
}else {
secondary_window = nullptr;
// should I release something here? The mailbox??
}
}
void RendererOpenGL::SwapBuffers() { void RendererOpenGL::SwapBuffers() {
// Maintain the rasterizer's state as a priority // Maintain the rasterizer's state as a priority

View File

@ -54,6 +54,7 @@ public:
void PrepareVideoDumping() override; void PrepareVideoDumping() override;
void CleanupVideoDumping() override; void CleanupVideoDumping() override;
void Sync() override; void Sync() override;
void setSecondaryWindow(Frontend::EmuWindow *secondaryWindow) override;
private: private:
void InitOpenGLObjects(); void InitOpenGLObjects();