Files
emulate/app/src/main/cpp/libretro_frontend.h
Matt Hills 981ac2eed9 Add search, favorites tab, and persistent favorites
Replace static console filter with swipeable tabs (All / Favorites /
per-console) and a collapsible search icon in the app bar. Persist
favorites via SharedPreferences keyed by ROM path. Display titles are
now cleaned (region tags stripped) for better UX while raw filenames
are preserved for cover art lookups. Sort games alphabetically and
fetch cover art in parallel (8 concurrent) for faster initial load.

Also fix PS1 controller setup so it works with both new games and save
states: pcsx_rearmed needs retro_set_controller_port_device called at
a specific point inside loadCore (between callbacks and get_system_info)
which FCEUmm crashes on. Added a "pending controller device" mechanism
that the Kotlin core can pre-set before nativeLoadCore. Save states are
now loaded BEFORE start() so the game's first frame already has the
restored state, avoiding the "controller removed" detection race.
2026-04-10 19:33:25 -04:00

137 lines
4.7 KiB
C++

#pragma once
#include "libretro.h"
#include "audio_engine.h"
#include <android/native_window.h>
#include <atomic>
#include <mutex>
#include <string>
#include <thread>
#include <unordered_map>
#include <vector>
enum class DisplayMode : int {
STRETCH = 0,
ASPECT_RATIO = 1,
INTEGER_SCALE = 2
};
class LibretroFrontend {
public:
static LibretroFrontend& instance();
bool loadCore(const char* soPath);
bool loadGame(const char* romPath);
void start();
void pause();
void resume();
void stop();
void reset();
void setWindow(ANativeWindow* window);
void releaseWindow();
void setButtonState(uint16_t state);
void setAnalogState(int stick, int16_t x, int16_t y);
bool saveState(const char* path);
bool loadState(const char* path);
void setAudioEnabled(bool enabled);
void setFrameSkip(int skip);
void setDisplayMode(int mode);
void setSystemDir(const char* dir);
void setSaveDir(const char* dir);
void setControllerPortDevice(unsigned port, unsigned device);
void setPendingControllerDevice(int device);
void setCoreOption(const char* key, const char* value);
bool isRunning() const { return running_.load(); }
private:
LibretroFrontend() = default;
~LibretroFrontend();
void runLoop();
void unloadCore();
// Libretro callbacks (static because libretro uses C function pointers)
static bool environmentCallback(unsigned cmd, void* data);
static void videoRefreshCallback(const void* data, unsigned width, unsigned height, size_t pitch);
static size_t audioSampleBatchCallback(const int16_t* data, size_t frames);
static void audioSampleCallback(int16_t left, int16_t right);
static void inputPollCallback();
static int16_t inputStateCallback(unsigned port, unsigned device, unsigned index, unsigned id);
static void logCallback(enum retro_log_level level, const char* fmt, ...);
// Core function pointers (resolved via dlsym)
void* core_handle_ = nullptr;
retro_init_t core_init_ = nullptr;
retro_deinit_t core_deinit_ = nullptr;
retro_api_version_t core_api_version_ = nullptr;
retro_get_system_info_t core_get_system_info_ = nullptr;
retro_get_system_av_info_t core_get_system_av_info_ = nullptr;
retro_set_environment_t core_set_environment_ = nullptr;
retro_set_video_refresh_t core_set_video_refresh_ = nullptr;
retro_set_audio_sample_t core_set_audio_sample_ = nullptr;
retro_set_audio_sample_batch_t core_set_audio_sample_batch_ = nullptr;
retro_set_input_poll_t core_set_input_poll_ = nullptr;
retro_set_input_state_t core_set_input_state_ = nullptr;
retro_set_controller_port_device_t core_set_controller_port_device_ = nullptr;
retro_load_game_t core_load_game_ = nullptr;
retro_unload_game_t core_unload_game_ = nullptr;
retro_run_t core_run_ = nullptr;
retro_reset_t core_reset_ = nullptr;
retro_serialize_size_t core_serialize_size_ = nullptr;
retro_serialize_t core_serialize_ = nullptr;
retro_unserialize_t core_unserialize_ = nullptr;
retro_get_memory_data_t core_get_memory_data_ = nullptr;
retro_get_memory_size_t core_get_memory_size_ = nullptr;
// State
std::atomic<bool> running_{false};
std::atomic<bool> paused_{false};
std::atomic<bool> game_loaded_{false};
std::thread emu_thread_;
// Video
ANativeWindow* window_ = nullptr;
std::mutex window_mutex_;
unsigned pixel_format_ = RETRO_PIXEL_FORMAT_RGB565;
std::atomic<DisplayMode> display_mode_{DisplayMode::STRETCH};
float core_aspect_ratio_ = 0.0f;
unsigned core_base_width_ = 0;
unsigned core_base_height_ = 0;
// Audio
AudioEngine audio_engine_;
// Input
std::atomic<uint16_t> button_state_{0};
std::atomic<int16_t> analog_lx_{0}, analog_ly_{0};
std::atomic<int16_t> analog_rx_{0}, analog_ry_{0};
// Paths
std::string system_dir_;
std::string save_dir_;
// ROM data (kept alive for cores with need_fullpath=false)
std::vector<uint8_t> rom_data_;
bool need_fullpath_ = true;
// Pending controller device (set by frontend before loadCore, applied during loadCore)
// -1 means "do not call retro_set_controller_port_device" (default)
int pending_controller_device_ = -1;
// Frame timing
double target_fps_ = 60.0;
int frame_skip_ = 0;
// Core options
std::unordered_map<std::string, std::string> core_options_;
std::atomic<bool> options_updated_{false};
};