Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- diff --git a/.travis.yml b/.travis.yml
- deleted file mode 100644
- index 19e2664f..00000000
- --- a/.travis.yml
- +++ /dev/null
- @@ -1,86 +0,0 @@
- -language: cpp
- -matrix:
- - include:
- - - os: linux
- - env: NAME="clang-format"
- - sudo: required
- - dist: trusty
- - services: docker
- - install: "./.travis/clang-format/deps.sh"
- - script: "./.travis/clang-format/build.sh"
- - - os: linux
- - env: NAME="linux build"
- - sudo: required
- - dist: trusty
- - services: docker
- - addons:
- - apt:
- - packages:
- - - p7zip-full
- - install: "./.travis/linux/deps.sh"
- - script: "./.travis/linux/build.sh"
- - after_success: "./.travis/linux/upload.sh"
- - cache: ccache
- - - if: repo = citra-emu/citra AND branch = master AND type = push
- - os: linux
- - env: NAME="transifex push"
- - sudo: required
- - dist: trusty
- - services: docker
- - install: "./.travis/transifex/deps.sh"
- - script: "./.travis/transifex/build.sh"
- - - os: osx
- - env: NAME="macos build"
- - sudo: false
- - osx_image: xcode10
- - install: "./.travis/macos/deps.sh"
- - script: "./.travis/macos/build.sh"
- - after_success: "./.travis/macos/upload.sh"
- - cache: ccache
- - - os: linux
- - env: NAME="linux build (frozen versions of dependencies)"
- - sudo: required
- - dist: trusty
- - services: docker
- - cache: ccache
- - script: "./.travis/linux-frozen/build.sh"
- - - os: linux
- - env: NAME="MinGW build"
- - sudo: required
- - dist: trusty
- - services: docker
- - addons:
- - apt:
- - packages:
- - - p7zip-full
- - install: "./.travis/linux-mingw/deps.sh"
- - script: "./.travis/linux-mingw/build.sh"
- - after_success: "./.travis/linux-mingw/upload.sh"
- - cache: ccache
- - - if: repo =~ ^.*\/(citra-canary|citra-nightly)$ AND tag IS present
- - git:
- - depth: false
- - os: linux
- - env: NAME="flatpak build"
- - sudo: required
- - dist: trusty
- - services: docker
- - cache: ccache
- - install: "./.travis/linux-flatpak/deps.sh"
- - script: "./.travis/linux-flatpak/build.sh"
- - after_script: "./.travis/linux-flatpak/finish.sh"
- -
- -deploy:
- - provider: releases
- - api_key:
- - secure: Mck15DIWaJdxDiS3aYVlM9N3G6y8VKUI1rnwII7/iolfm1s94U+tgvbheZDmT7SSbFyaGaYO/E8HrV/uZR9Vvs7ev20sHsTN1u60OTWfDIIyHs9SqjhcGbtq95m9/dMFschOYqTOR+gAs5BsxjuoeAotHdhpQEwvkO2oo5oR0zhGy45gjFnVvtcxT/IfpZBIpVgcK3aLb9zT6ekcJbSiPmEB15iLq3xXd0nFUNtEZdX3D6Veye4n5jB6n72qN8JVoKvPZAwaC2K0pZxpcGJaXDchLsw1q+4eCvdz6UJfUemeQ/uMAmjfeQ3wrzYGXe3nCM3WmX5wosCsB0mw4zYatzl3si6CZ1W+0GkV4Rwlx03dfp7v3EeFhTsXYCaXqhwuLZnWOLUik8t9vaSoFUx4nUIRwfO9kAMUJQSpLuHNO2nT01s3GxvqxzczuLQ9he5nGSi0RRodUzDwek1qUp6I4uV3gRHKz4B07YIc1i2fK88NLXjyQ0uLVZ+7Oq1+kgDp6+N7vvXXZ5qZ17tdaysSbKEE0Y8zsoXw7Rk1tPN19vrCS+TSpomNMyQyne1k+I5iZ/qkxPTLAS5qI6Utc2dL3GJdxWRAEfGNO9AIX3GV/jmmKfdcvwGsCYP8hxqs5vLYfgacw3D8NLf1941lQUwavC17jm9EV9g5G3Pn1Cp516E=
- - file_glob: true
- - file: "artifacts/*"
- - skip_cleanup: true
- - on:
- - tags: true
- -
- -notifications:
- - webhooks:
- - urls:
- - - https://api.citra-emu.org/code/travis/notify
- diff --git a/CMakeLists.txt b/CMakeLists.txt
- index 46ad976a..a7781560 100644
- --- a/CMakeLists.txt
- +++ b/CMakeLists.txt
- @@ -6,6 +6,7 @@ include(DownloadExternals)
- include(CMakeDependentOption)
- project(citra)
- +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fpic")
- # Set bundled sdl2/qt as dependent options.
- # OFF by default, but if ENABLE_SDL2 and MSVC are true then ON
- @@ -16,6 +17,8 @@ option(ENABLE_QT "Enable the Qt frontend" ON)
- option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF)
- CMAKE_DEPENDENT_OPTION(CITRA_USE_BUNDLED_QT "Download bundled Qt binaries" ON "ENABLE_QT;MSVC" OFF)
- +option(ENABLE_LIBRETRO "Enable the LibRetro frontend" ON)
- +
- option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
- option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
- diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
- index ae92ce86..5341fe86 100644
- --- a/externals/CMakeLists.txt
- +++ b/externals/CMakeLists.txt
- @@ -78,6 +78,11 @@ if (USE_DISCORD_PRESENCE)
- target_include_directories(discord-rpc INTERFACE ./discord-rpc/include)
- endif()
- +# LibRetro
- +add_library(libretro INTERFACE)
- +target_include_directories(libretro INTERFACE ./libretro)
- +
- +
- if (ENABLE_WEB_SERVICE)
- # LibreSSL
- set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
- index 6138791c..75cb8e73 100644
- --- a/src/CMakeLists.txt
- +++ b/src/CMakeLists.txt
- @@ -19,6 +19,9 @@ if (ANDROID)
- else()
- add_subdirectory(dedicated_room)
- endif()
- +if (ENABLE_LIBRETRO)
- + add_subdirectory(citra_libretro)
- +endif()
- if (ENABLE_WEB_SERVICE)
- add_subdirectory(web_service)
- endif()
- diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
- index caddd0a3..8f948a51 100644
- --- a/src/audio_core/CMakeLists.txt
- +++ b/src/audio_core/CMakeLists.txt
- @@ -30,6 +30,7 @@ add_library(audio_core STATIC
- time_stretch.h
- $<$<BOOL:${SDL2_FOUND}>:sdl2_sink.cpp sdl2_sink.h>
- + $<$<BOOL:${ENABLE_LIBRETRO}>:libretro_sink.cpp libretro_sink.h>
- $<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h cubeb_input.cpp cubeb_input.h>
- $<$<BOOL:${FFMPEG_FOUND}>:hle/ffmpeg_decoder.cpp hle/ffmpeg_decoder.h hle/ffmpeg_dl.cpp hle/ffmpeg_dl.h>
- $<$<BOOL:${ENABLE_MF}>:hle/wmf_decoder.cpp hle/wmf_decoder.h hle/wmf_decoder_utils.cpp hle/wmf_decoder_utils.h>
- @@ -59,6 +60,11 @@ if(SDL2_FOUND)
- target_compile_definitions(audio_core PRIVATE HAVE_SDL2)
- endif()
- +if (ENABLE_LIBRETRO)
- + target_link_libraries(audio_core PRIVATE libretro)
- + target_compile_definitions(audio_core PRIVATE HAVE_LIBRETRO)
- +endif()
- +
- if(ENABLE_CUBEB)
- target_link_libraries(audio_core PRIVATE cubeb)
- target_compile_definitions(audio_core PUBLIC HAVE_CUBEB)
- diff --git a/src/audio_core/sink_details.cpp b/src/audio_core/sink_details.cpp
- index 25c71917..aa317595 100644
- --- a/src/audio_core/sink_details.cpp
- +++ b/src/audio_core/sink_details.cpp
- @@ -11,6 +11,9 @@
- #ifdef HAVE_SDL2
- #include "audio_core/sdl2_sink.h"
- #endif
- +#ifdef HAVE_LIBRETRO
- +#include "audio_core/libretro_sink.h"
- +#endif
- #ifdef HAVE_CUBEB
- #include "audio_core/cubeb_sink.h"
- #endif
- @@ -32,6 +35,13 @@ struct SinkDetails {
- // sink_details is ordered in terms of desirability, with the best choice at the top.
- constexpr SinkDetails sink_details[] = {
- +#ifdef HAVE_LIBRETRO
- + SinkDetails{"libretro",
- + [](std::string_view device_id) -> std::unique_ptr<Sink> {
- + return std::make_unique<LibRetroSink>(std::string(device_id));
- + },
- + &ListLibretroSinkDevices},
- +#endif
- #ifdef HAVE_CUBEB
- SinkDetails{"cubeb",
- [](std::string_view device_id) -> std::unique_ptr<Sink> {
- diff --git a/src/citra_libretro/CMakeLists.txt b/src/citra_libretro/CMakeLists.txt
- new file mode 100644
- index 00000000..62c329ec
- --- /dev/null
- +++ b/src/citra_libretro/CMakeLists.txt
- @@ -0,0 +1,28 @@
- +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
- +
- +add_library(citra_libretro SHARED
- + emu_window/libretro_window.cpp
- + emu_window/libretro_window.h
- + input/input_factory.cpp
- + input/input_factory.h
- + input/mouse_tracker.cpp
- + input/mouse_tracker.h
- + citra_libretro.cpp
- + citra_libretro.h
- + environment.cpp
- + environment.h
- + core_settings.cpp
- + core_settings.h
- + libretro_logger.cpp
- + libretro_logger.h)
- +
- +create_target_directory_groups(citra_libretro)
- +
- +target_link_libraries(citra_libretro PRIVATE common core)
- +target_link_libraries(citra_libretro PRIVATE glad libretro)
- +target_link_libraries(citra_libretro PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
- +if(DEFINED LIBRETRO_STATIC)
- +target_link_libraries(citra_libretro PRIVATE -static-libstdc++)
- +endif()
- +
- +set_target_properties(citra_libretro PROPERTIES PREFIX "")
- diff --git a/src/citra_libretro/citra_libretro.cpp b/src/citra_libretro/citra_libretro.cpp
- new file mode 100644
- index 00000000..b4f2f6cc
- --- /dev/null
- +++ b/src/citra_libretro/citra_libretro.cpp
- @@ -0,0 +1,545 @@
- +// Copyright 2017 Citra Emulator Project
- +// Licensed under GPLv2 or any later version
- +// Refer to the license.txt file included.
- +
- +#include <list>
- +#include <numeric>
- +#include <math.h>
- +#include <stdint.h>
- +#include <stdio.h>
- +#include <stdlib.h>
- +#include <common/file_util.h>
- +
- +#include "glad/glad.h"
- +#include "libretro.h"
- +
- +#include "audio_core/libretro_sink.h"
- +#include "citra_libretro/citra_libretro.h"
- +#include "citra_libretro/core_settings.h"
- +#include "citra_libretro/environment.h"
- +#include "citra_libretro/libretro_logger.h"
- +#include "citra_libretro/input/input_factory.h"
- +#include "common/logging/backend.h"
- +#include "common/logging/filter.h"
- +#include "common/string_util.h"
- +#include "core/core.h"
- +#include "core/memory.h"
- +#include "core/hle/kernel/memory.h"
- +#include "core/loader/loader.h"
- +#include "core/settings.h"
- +#include "core/frontend/applets/default_applets.h"
- +#include "video_core/renderer_opengl/renderer_opengl.h"
- +#include "video_core/video_core.h"
- +
- +class CitraLibRetro {
- +public:
- + CitraLibRetro() : log_filter(Log::Level::Info) {}
- +
- + Log::Filter log_filter;
- + std::unique_ptr<EmuWindow_LibRetro> emu_window;
- + struct retro_hw_render_callback hw_render {};
- +};
- +
- +CitraLibRetro* emu_instance;
- +
- +void* load_opengl_func(const char* name) {
- + return (void*)emu_instance->hw_render.get_proc_address(name);
- +}
- +
- +
- +void retro_init() {
- + emu_instance = new CitraLibRetro();
- + //Log::Init();
- + Log::SetGlobalFilter(emu_instance->log_filter);
- +
- + // Check to see if the frontend is providing us with logging functionality
- + auto callback = LibRetro::GetLoggingBackend();
- + if (callback != nullptr) {
- + Log::AddBackend(std::make_unique<LibRetroLogger>(callback));
- + } else {
- + Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
- + }
- +
- + LOG_DEBUG(Frontend, "Initialising core...");
- +
- + // Set up LLE cores
- + for (const auto& service_module : Service::service_module_map) {
- + Settings::values.lle_modules.emplace(service_module.name, false);
- + }
- +
- + // Setup default, stub handlers for HLE applets
- + Frontend::RegisterDefaultApplets();
- +
- + LibRetro::Input::Init();
- +
- +}
- +
- +void retro_deinit() {
- + LOG_DEBUG(Frontend, "Shutting down core...");
- + if (Core::System::GetInstance().IsPoweredOn()) {
- + Core::System::GetInstance().Shutdown();
- + }
- +
- + LibRetro::Input::Shutdown();
- +
- + delete emu_instance;
- +
- + //Log::Destroy();
- +}
- +
- +unsigned retro_api_version() {
- + return RETRO_API_VERSION;
- +}
- +
- +void LibRetro::OnConfigureEnvironment() {
- + static const retro_variable values[] = {
- + {"citra_use_cpu_jit", "Enable CPU JIT; enabled|disabled"},
- + {"citra_use_hw_renderer", "Enable hardware renderer; enabled|disabled"},
- + {"citra_use_shader_jit", "Enable shader JIT; enabled|disabled"},
- + {"citra_use_hw_shaders", "Enable hardware shaders; enabled|disabled"},
- + {"citra_use_acc_geo_shaders", "Enable accurate geometry shaders (only for H/W shaders); enabled|disabled"},
- + {"citra_use_acc_mul", "Enable accurate shaders multiplication (only for H/W shaders); enabled|disabled"},
- + {"citra_resolution_factor",
- + "Resolution scale factor; 1x (Native)|2x|3x|4x|5x|6x|7x|8x|9x|10x"},
- + {"citra_layout_option", "Screen layout positioning; Default Top-Bottom Screen|Single "
- + "Screen Only|Large Screen, Small Screen|Side by Side"},
- + {"citra_swap_screen", "Prominent 3DS screen; Top|Bottom"},
- + {"citra_analog_function",
- + "Right analog function; C-Stick and Touchscreen Pointer|Touchscreen Pointer|C-Stick"},
- + {"citra_deadzone", "Emulated pointer deadzone (%); 15|20|25|30|35|0|5|10"},
- + {"citra_use_virtual_sd", "Enable virtual SD card; enabled|disabled"},
- + {"citra_use_libretro_save_path", "Savegame location; LibRetro Default|Citra Default"},
- + {"citra_is_new_3ds", "3DS system model; Old 3DS|New 3DS"},
- + {"citra_region_value",
- + "3DS system region; Auto|Japan|USA|Europe|Australia|China|Korea|Taiwan"},
- + {"citra_use_gdbstub", "Enable GDB stub; disabled|enabled"},
- + {nullptr, nullptr}};
- +
- + LibRetro::SetVariables(values);
- +
- + static const struct retro_controller_description controllers[] = {
- + {"Nintendo 3DS", RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 0)},
- + };
- +
- + static const struct retro_controller_info ports[] = {
- + {controllers, 1},
- + {nullptr, 0},
- + };
- +
- + LibRetro::SetControllerInfo(ports);
- +}
- +
- +uintptr_t LibRetro::GetFramebuffer() {
- + return emu_instance->hw_render.get_current_framebuffer();
- +}
- +
- +/**
- + * Updates Citra's settings with Libretro's.
- + */
- +void UpdateSettings() {
- + struct retro_input_descriptor desc[] = {
- + {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left"},
- + {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Up"},
- + {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down"},
- + {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Right"},
- + {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "X"},
- + {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Y"},
- + {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B"},
- + {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "A"},
- + {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "L"},
- + {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2, "ZL"},
- + {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R"},
- + {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2, "ZR"},
- + {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start"},
- + {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select"},
- + {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3, "Home"},
- + {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3, "Touch Screen Touch"},
- + {0, 0},
- + };
- +
- + LibRetro::SetInputDescriptors(desc);
- +
- + // Some settings cannot be set by LibRetro frontends - options have to be
- + // finite. Make assumptions.
- + Settings::values.log_filter = "*:Info";
- + Settings::values.sink_id = "libretro";
- + Settings::values.volume = 1.0f;
- +
- + // We don't need these, as this is the frontend's responsibility.
- + Settings::values.enable_audio_stretching = false;
- + Settings::values.use_frame_limit = false;
- + Settings::values.frame_limit = 100;
- +
- + // For our other settings, import them from LibRetro.
- + Settings::values.use_cpu_jit =
- + LibRetro::FetchVariable("citra_use_cpu_jit", "enabled") == "enabled";
- + Settings::values.use_hw_renderer =
- + LibRetro::FetchVariable("citra_use_hw_renderer", "enabled") == "enabled";
- + Settings::values.use_hw_shader =
- + LibRetro::FetchVariable("citra_use_hw_shaders", "enabled") == "enabled";
- + Settings::values.use_shader_jit =
- + LibRetro::FetchVariable("citra_use_shader_jit", "enabled") == "enabled";
- + Settings::values.shaders_accurate_gs =
- + LibRetro::FetchVariable("citra_use_acc_geo_shaders", "enabled") == "enabled";
- + Settings::values.shaders_accurate_mul =
- + LibRetro::FetchVariable("citra_use_acc_mul", "enabled") == "enabled";
- + Settings::values.use_virtual_sd =
- + LibRetro::FetchVariable("citra_use_virtual_sd", "enabled") == "enabled";
- + Settings::values.is_new_3ds =
- + LibRetro::FetchVariable("citra_is_new_3ds", "Old 3DS") == "New 3DS";
- + Settings::values.swap_screen = LibRetro::FetchVariable("citra_swap_screen", "Top") == "Bottom";
- + Settings::values.use_gdbstub =
- + LibRetro::FetchVariable("citra_use_gdbstub", "disabled") == "enabled";
- +
- + // These values are a bit more hard to define, unfortunately.
- + auto scaling = LibRetro::FetchVariable("citra_resolution_factor", "1x (Native)");
- + auto endOfScale = scaling.find('x'); // All before 'x' in "_x ...", e.g "1x (Native)"
- + if (endOfScale == std::string::npos) {
- + LOG_ERROR(Frontend, "Failed to parse resolution scale!");
- + Settings::values.resolution_factor = 1;
- + } else {
- + int scale = stoi(scaling.substr(0, endOfScale));
- + Settings::values.resolution_factor = scale;
- + }
- +
- + auto layout = LibRetro::FetchVariable("citra_layout_option", "Default Top-Bottom Screen");
- +
- + if (layout == "Default Top-Bottom Screen") {
- + Settings::values.layout_option = Settings::LayoutOption::Default;
- + } else if (layout == "Single Screen Only") {
- + Settings::values.layout_option = Settings::LayoutOption::SingleScreen;
- + } else if (layout == "Large Screen, Small Screen") {
- + Settings::values.layout_option = Settings::LayoutOption::LargeScreen;
- + } else if (layout == "Side by Side") {
- + Settings::values.layout_option = Settings::LayoutOption::SideScreen;
- + } else {
- + LOG_ERROR(Frontend, "Unknown layout type: {}.", layout);
- + Settings::values.layout_option = Settings::LayoutOption::Default;
- + }
- +
- + auto deadzone = LibRetro::FetchVariable("citra_deadzone", "15");
- + LibRetro::settings.deadzone = (float)std::stoi(deadzone) / 100;
- +
- + auto analog_function =
- + LibRetro::FetchVariable("citra_analog_function", "C-Stick and Touchscreen Pointer");
- +
- + if (analog_function == "C-Stick and Touchscreen Pointer") {
- + LibRetro::settings.analog_function = LibRetro::CStickFunction::Both;
- + } else if (analog_function == "C-Stick") {
- + LibRetro::settings.analog_function = LibRetro::CStickFunction::CStick;
- + } else if (analog_function == "Touchscreen Pointer") {
- + LibRetro::settings.analog_function = LibRetro::CStickFunction::Touchscreen;
- + } else {
- + LOG_ERROR(Frontend, "Unknown right analog function: {}.", analog_function);
- + LibRetro::settings.analog_function = LibRetro::CStickFunction::Both;
- + }
- +
- + auto region = LibRetro::FetchVariable("citra_region_value", "Auto");
- + std::map<std::string, int> region_values;
- + region_values["Auto"] = -1;
- + region_values["Japan"] = 0;
- + region_values["USA"] = 1;
- + region_values["Europe"] = 2;
- + region_values["Australia"] = 3;
- + region_values["China"] = 4;
- + region_values["Korea"] = 5;
- + region_values["Taiwan"] = 6;
- +
- + auto result = region_values.find(region);
- + if (result == region_values.end()) {
- + LOG_ERROR(Frontend, "Invalid region: {}.", region);
- + Settings::values.region_value = -1;
- + } else {
- + Settings::values.region_value = result->second;
- + }
- +
- + Settings::values.current_input_profile.touch_device = "engine:emu_window";
- +
- + // Hardcode buttons to bind to libretro - it is entirely redundant to have
- + // two methods of rebinding controls.
- + // Citra: A = RETRO_DEVICE_ID_JOYPAD_A (8)
- + Settings::values.current_input_profile.buttons[0] = "button:8,joystick:0,engine:libretro";
- + // Citra: B = RETRO_DEVICE_ID_JOYPAD_B (0)
- + Settings::values.current_input_profile.buttons[1] = "button:0,joystick:0,engine:libretro";
- + // Citra: X = RETRO_DEVICE_ID_JOYPAD_X (9)
- + Settings::values.current_input_profile.buttons[2] = "button:9,joystick:0,engine:libretro";
- + // Citra: Y = RETRO_DEVICE_ID_JOYPAD_Y (1)
- + Settings::values.current_input_profile.buttons[3] = "button:1,joystick:0,engine:libretro";
- + // Citra: UP = RETRO_DEVICE_ID_JOYPAD_UP (4)
- + Settings::values.current_input_profile.buttons[4] = "button:4,joystick:0,engine:libretro";
- + // Citra: DOWN = RETRO_DEVICE_ID_JOYPAD_DOWN (5)
- + Settings::values.current_input_profile.buttons[5] = "button:5,joystick:0,engine:libretro";
- + // Citra: LEFT = RETRO_DEVICE_ID_JOYPAD_LEFT (6)
- + Settings::values.current_input_profile.buttons[6] = "button:6,joystick:0,engine:libretro";
- + // Citra: RIGHT = RETRO_DEVICE_ID_JOYPAD_RIGHT (7)
- + Settings::values.current_input_profile.buttons[7] = "button:7,joystick:0,engine:libretro";
- + // Citra: L = RETRO_DEVICE_ID_JOYPAD_L (10)
- + Settings::values.current_input_profile.buttons[8] = "button:10,joystick:0,engine:libretro";
- + // Citra: R = RETRO_DEVICE_ID_JOYPAD_R (11)
- + Settings::values.current_input_profile.buttons[9] = "button:11,joystick:0,engine:libretro";
- + // Citra: START = RETRO_DEVICE_ID_JOYPAD_START (3)
- + Settings::values.current_input_profile.buttons[10] = "button:3,joystick:0,engine:libretro";
- + // Citra: SELECT = RETRO_DEVICE_ID_JOYPAD_SELECT (2)
- + Settings::values.current_input_profile.buttons[11] = "button:2,joystick:0,engine:libretro";
- + // Citra: ZL = RETRO_DEVICE_ID_JOYPAD_L2 (12)
- + Settings::values.current_input_profile.buttons[12] = "button:12,joystick:0,engine:libretro";
- + // Citra: ZR = RETRO_DEVICE_ID_JOYPAD_R2 (13)
- + Settings::values.current_input_profile.buttons[13] = "button:13,joystick:0,engine:libretro";
- + // Citra: HOME = RETRO_DEVICE_ID_JOYPAD_L3 (as per above bindings) (14)
- + Settings::values.current_input_profile.buttons[14] = "button:14,joystick:0,engine:libretro";
- +
- + // Circle Pad
- + Settings::values.current_input_profile.analogs[0] = "axis:0,joystick:0,engine:libretro";
- + // C-Stick
- + if (LibRetro::settings.analog_function != LibRetro::CStickFunction::Touchscreen) {
- + Settings::values.current_input_profile.analogs[1] = "axis:1,joystick:0,engine:libretro";
- + } else {
- + Settings::values.current_input_profile.analogs[1] = "";
- + }
- +
- + // Configure the file storage location
- + auto use_libretro_saves = LibRetro::FetchVariable("citra_use_libretro_save_path",
- + "LibRetro Default") == "LibRetro Default";
- +
- + if (use_libretro_saves) {
- + auto target_dir = LibRetro::GetSaveDir();
- + if (target_dir.empty()) {
- + LOG_INFO(Frontend, "No save dir provided; trying system dir...");
- + target_dir = LibRetro::GetSystemDir();
- + }
- +
- + if (!target_dir.empty()) {
- + target_dir += "/Citra";
- +
- + // Ensure that this new dir exists
- + if (!FileUtil::CreateDir(target_dir)) {
- + LOG_ERROR(Frontend, "Failed to create \"{}\". Using Citra's default paths.", target_dir);
- + } else {
- + FileUtil::GetUserPath(FileUtil::UserPath::RootDir) + target_dir;
- + const auto& target_dir_result = FileUtil::GetUserPath(FileUtil::UserPath::UserDir) + target_dir;
- + LOG_INFO(Frontend, "User dir set to \"{}\".", target_dir_result);
- + }
- + }
- + }
- +
- + // Update the framebuffer sizing.
- + emu_instance->emu_window->UpdateLayout();
- +
- + Settings::Apply();
- +}
- +
- +/**
- + * libretro callback; Called every game tick.
- + */
- +void retro_run() {
- + // Check to see if we actually have any config updates to process.
- + if (LibRetro::HasUpdatedConfig()) {
- + UpdateSettings();
- + }
- +
- + // We can't assume that the frontend has been nice and preserved all OpenGL settings. Reset.
- + auto last_state = OpenGL::OpenGLState::GetCurState();
- + ResetGLState();
- + last_state.Apply();
- +
- + while (!emu_instance->emu_window->HasSubmittedFrame()) {
- + auto result = Core::System::GetInstance().RunLoop();
- +
- + if (result != Core::System::ResultStatus::Success) {
- + std::string errorContent = Core::System::GetInstance().GetStatusDetails();
- + std::string msg;
- +
- + switch (result) {
- + case Core::System::ResultStatus::ErrorSystemFiles:
- + msg = "Citra was unable to locate a 3DS system archive: " + errorContent;
- + break;
- + default:
- + msg = "Fatal Error encountered: " + errorContent;
- + break;
- + }
- +
- + LibRetro::DisplayMessage(msg.c_str());
- + }
- + }
- +}
- +
- +
- +void context_reset() {
- + if (!Core::System::GetInstance().IsPoweredOn()) {
- + LOG_CRITICAL(Frontend, "Cannot reset system core if isn't on!");
- + return;
- + }
- +
- + // Recreate our renderer, so it can reset it's state.
- + if (VideoCore::g_renderer != nullptr) {
- + LOG_ERROR(Frontend,
- + "Likely memory leak: context_destroy() was not called before context_reset()!");
- + }
- +
- + // Check to see if the frontend provides us with OpenGL symbols
- + if (emu_instance->hw_render.get_proc_address != nullptr) {
- + if (!gladLoadGLLoader((GLADloadproc)load_opengl_func)) {
- + LOG_CRITICAL(Frontend, "Glad failed to load (frontend-provided symbols)!");
- + return;
- + }
- + } else {
- + // Else, try to load them on our own
- + if (!gladLoadGL()) {
- + LOG_CRITICAL(Frontend, "Glad failed to load (internal symbols)!");
- + return;
- + }
- + }
- +
- + VideoCore::g_renderer = std::make_unique<OpenGL::RendererOpenGL>(*emu_instance->emu_window);
- + if (VideoCore::g_renderer->Init() != Core::System::ResultStatus::Success) {
- + LOG_DEBUG(Render, "initialized OK");
- + } else {
- + LOG_ERROR(Render, "initialization failed!");
- + }
- +
- + emu_instance->emu_window->UpdateLayout();
- + emu_instance->emu_window->CreateContext();
- +}
- +
- +void context_destroy() {
- + if (VideoCore::g_renderer != nullptr) {
- + VideoCore::g_renderer->ShutDown();
- + }
- +
- + emu_instance->emu_window->DestroyContext();
- + VideoCore::g_renderer = nullptr;
- +}
- +
- +void retro_reset() {
- + Core::System::GetInstance().Shutdown();
- + Core::System::GetInstance().Load(*emu_instance->emu_window, LibRetro::settings.file_path);
- + context_reset(); // Force the renderer to appear
- +}
- +
- +/**
- + * libretro callback; Called when a game is to be loaded.
- + */
- +bool retro_load_game(const struct retro_game_info* info) {
- + LOG_INFO(Frontend, "Starting Citra RetroArch game...");
- +
- + LibRetro::settings.file_path = info->path;
- +
- + LibRetro::SetHWSharedContext();
- +
- + if (!LibRetro::SetPixelFormat(RETRO_PIXEL_FORMAT_XRGB8888)) {
- + LOG_CRITICAL(Frontend, "XRGB8888 is not supported.");
- + LibRetro::DisplayMessage("XRGB8888 is not supported.");
- + return false;
- + }
- +
- + emu_instance->hw_render.context_type = RETRO_HW_CONTEXT_OPENGL_CORE;
- + emu_instance->hw_render.version_major = 3;
- + emu_instance->hw_render.version_minor = 3;
- + emu_instance->hw_render.context_reset = context_reset;
- + emu_instance->hw_render.context_destroy = context_destroy;
- + emu_instance->hw_render.cache_context = false;
- + emu_instance->hw_render.bottom_left_origin = true;
- + if (!LibRetro::SetHWRenderer(&emu_instance->hw_render)) {
- + LOG_CRITICAL(Frontend, "OpenGL 3.3 is not supported.");
- + LibRetro::DisplayMessage("OpenGL 3.3 is not supported.");
- + return false;
- + }
- +
- + emu_instance->emu_window = std::make_unique<EmuWindow_LibRetro>();
- + UpdateSettings();
- +
- + const Core::System::ResultStatus load_result{Core::System::GetInstance().Load(
- + *emu_instance->emu_window, LibRetro::settings.file_path)};
- +
- + switch (load_result) {
- + case Core::System::ResultStatus::ErrorGetLoader:
- + LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!",
- + LibRetro::settings.file_path);
- + LibRetro::DisplayMessage("Failed to obtain loader for specified ROM!");
- + return false;
- + case Core::System::ResultStatus::ErrorLoader:
- + LOG_CRITICAL(Frontend, "Failed to load ROM!");
- + LibRetro::DisplayMessage("Failed to load ROM!");
- + return false;
- + case Core::System::ResultStatus::ErrorLoader_ErrorEncrypted:
- + LOG_CRITICAL(Frontend, "The game that you are trying to load must be decrypted before "
- + "being used with Citra. \n\n For more information on dumping and "
- + "decrypting games, please refer to: "
- + "https://citra-emu.org/wiki/Dumping-Game-Cartridges");
- + LibRetro::DisplayMessage("The game that you are trying to load must be decrypted before "
- + "being used with Citra. \n\n For more information on dumping and "
- + "decrypting games, please refer to: "
- + "https://citra-emu.org/wiki/Dumping-Game-Cartridges");
- + return false;
- + case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat:
- + LOG_CRITICAL(Frontend, "Error while loading ROM: The ROM format is not supported.");
- + LibRetro::DisplayMessage("Error while loading ROM: The ROM format is not supported.");
- + return false;
- + case Core::System::ResultStatus::ErrorNotInitialized:
- + LOG_CRITICAL(Frontend, "CPUCore not initialized");
- + LibRetro::DisplayMessage("CPUCore not initialized");
- + return false;
- + case Core::System::ResultStatus::ErrorSystemMode:
- + LOG_CRITICAL(Frontend, "Failed to determine system mode!");
- + LibRetro::DisplayMessage("Failed to determine system mode!");
- + return false;
- + case Core::System::ResultStatus::ErrorVideoCore:
- + LOG_CRITICAL(Frontend, "VideoCore not initialized");
- + LibRetro::DisplayMessage("VideoCore not initialized");
- + return false;
- + case Core::System::ResultStatus::Success:
- + break; // Expected case
- + default:
- + LOG_CRITICAL(Frontend, "Unknown error");
- + LibRetro::DisplayMessage("Unknown error");
- + return false;
- + }
- +
- + return true;
- +}
- +
- +void retro_unload_game() {
- + LOG_DEBUG(Frontend, "Unloading game...");
- + Core::System::GetInstance().Shutdown();
- +}
- +
- +unsigned retro_get_region() {
- + return RETRO_REGION_NTSC;
- +}
- +
- +bool retro_load_game_special(unsigned game_type, const struct retro_game_info* info,
- + size_t num_info) {
- + return retro_load_game(info);
- +}
- +
- +size_t retro_serialize_size() {
- + return 0;
- +}
- +
- +bool retro_serialize(void* data_, size_t size) {
- + return true;
- +}
- +
- +bool retro_unserialize(const void* data_, size_t size) {
- + return true;
- +}
- +
- +void* retro_get_memory_data(unsigned id) {
- +/*
- + if ( id == RETRO_MEMORY_SYSTEM_RAM )
- + return impl->fcram.get();
- +*/
- + return NULL;
- +}
- +
- +size_t retro_get_memory_size(unsigned id) {
- +/*
- + if ( id == RETRO_MEMORY_SYSTEM_RAM )
- + return Kernel::memory_regions[0].size;
- +*/
- + return 0;
- +}
- +
- +void retro_cheat_reset() {}
- +
- +void retro_cheat_set(unsigned index, bool enabled, const char* code) {}
- diff --git a/src/citra_libretro/citra_libretro.h b/src/citra_libretro/citra_libretro.h
- new file mode 100644
- index 00000000..aa759532
- --- /dev/null
- +++ b/src/citra_libretro/citra_libretro.h
- @@ -0,0 +1,10 @@
- +// Copyright 2017 Citra Emulator Project
- +// Licensed under GPLv2 or any later version
- +// Refer to the license.txt file included.
- +
- +#pragma once
- +
- +#include "core/core.h"
- +#include "emu_window/libretro_window.h"
- +
- +namespace LibRetro {} // namespace LibRetro
- diff --git a/src/citra_libretro/core_settings.cpp b/src/citra_libretro/core_settings.cpp
- new file mode 100644
- index 00000000..9bdc90f5
- --- /dev/null
- +++ b/src/citra_libretro/core_settings.cpp
- @@ -0,0 +1,11 @@
- +// Copyright 2017 Citra Emulator Project
- +// Licensed under GPLv2 or any later version
- +// Refer to the license.txt file included.
- +
- +#include "core_settings.h"
- +
- +namespace LibRetro {
- +
- +CoreSettings settings = {};
- +
- +} // namespace LibRetro
- diff --git a/src/citra_libretro/core_settings.h b/src/citra_libretro/core_settings.h
- new file mode 100644
- index 00000000..8a1d4f55
- --- /dev/null
- +++ b/src/citra_libretro/core_settings.h
- @@ -0,0 +1,23 @@
- +// Copyright 2017 Citra Emulator Project
- +// Licensed under GPLv2 or any later version
- +// Refer to the license.txt file included.
- +
- +#pragma once
- +
- +#include <string>
- +
- +namespace LibRetro {
- +
- +enum CStickFunction { Both, CStick, Touchscreen };
- +
- +struct CoreSettings {
- +
- + ::std::string file_path;
- +
- + float deadzone = 1.f;
- +
- + LibRetro::CStickFunction analog_function;
- +
- +} extern settings;
- +
- +} // namespace LibRetro
- diff --git a/src/citra_libretro/emu_window/libretro_window.cpp b/src/citra_libretro/emu_window/libretro_window.cpp
- new file mode 100644
- index 00000000..1444169d
- --- /dev/null
- +++ b/src/citra_libretro/emu_window/libretro_window.cpp
- @@ -0,0 +1,237 @@
- +// Copyright 2017 Citra Emulator Project
- +// Licensed under GPLv2 or any later version
- +// Refer to the license.txt file included.
- +
- +#include "glad/glad.h"
- +#include <libretro.h>
- +
- +#include "audio_core/audio_types.h"
- +#include "citra_libretro/citra_libretro.h"
- +#include "citra_libretro/environment.h"
- +#include "citra_libretro/input/input_factory.h"
- +#include "core/3ds.h"
- +#include "core/settings.h"
- +#include "video_core/renderer_opengl/gl_state.h"
- +
- +/// LibRetro expects a "default" GL state.
- +void ResetGLState() {
- + // Reset internal state.
- + OpenGL::OpenGLState state {};
- + state.Apply();
- +
- + // Clean up global state.
- + glLogicOp(GL_COPY);
- +
- + glEnable(GL_DEPTH_TEST);
- + glDepthFunc(GL_LESS);
- + glDepthMask(GL_TRUE);
- +
- + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- +
- + glDisable(GL_STENCIL_TEST);
- + glStencilFunc(GL_ALWAYS, 0, 0xFFFFFFFF);
- +
- + glEnable(GL_BLEND);
- + glBlendFunc(GL_ONE, GL_ZERO);
- + glBlendEquation(GL_FUNC_ADD);
- + glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO);
- + glBlendColor(0, 0, 0, 0);
- +
- + glDisable(GL_COLOR_LOGIC_OP);
- +
- + glDisable(GL_DITHER);
- +
- + glDisable(GL_CULL_FACE);
- + glCullFace(GL_BACK);
- +
- + glActiveTexture(GL_TEXTURE0);
- +}
- +
- +EmuWindow_LibRetro::EmuWindow_LibRetro() {}
- +
- +EmuWindow_LibRetro::~EmuWindow_LibRetro() {}
- +
- +void EmuWindow_LibRetro::SwapBuffers() {
- + submittedFrame = true;
- +
- + auto current_state = OpenGL::OpenGLState::GetCurState();
- +
- + ResetGLState();
- +
- + if (tracker != nullptr) {
- + tracker->Render(width, height);
- + }
- +
- + LibRetro::UploadVideoFrame(RETRO_HW_FRAME_BUFFER_VALID, static_cast<unsigned>(width),
- + static_cast<unsigned>(height), 0);
- +
- + ResetGLState();
- +
- + current_state.Apply();
- +}
- +
- +/*
- +void EmuWindow_LibRetro::SetupFramebuffer() {
- + // TODO: Expose interface in renderer_opengl to configure this in it's internal state
- + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer);
- +
- + // glClear can be a slow path - skip clearing if we don't need to.
- + if (doCleanFrame) {
- + glClear(GL_COLOR_BUFFER_BIT);
- +
- + doCleanFrame = false;
- + }
- +}
- +*/
- +void EmuWindow_LibRetro::PollEvents() {
- + LibRetro::PollInput();
- +
- + // TODO: Poll for right click for motion emu
- +
- + if (tracker != nullptr) {
- + tracker->Update(width, height, GetFramebufferLayout().bottom_screen);
- +
- + if (tracker->IsPressed()) {
- + auto mousePos = tracker->GetPressedPosition();
- +
- + if (hasTouched) {
- + TouchMoved(mousePos.first, mousePos.second);
- + } else {
- + TouchPressed(mousePos.first, mousePos.second);
- + hasTouched = true;
- + }
- + } else if (hasTouched) {
- + hasTouched = false;
- + TouchReleased();
- + }
- + }
- +}
- +
- +
- +void EmuWindow_LibRetro::MakeCurrent() {
- + // They don't get any say in the matter - GL context is always current!
- +}
- +
- +void EmuWindow_LibRetro::DoneCurrent() {
- + // They don't get any say in the matter - GL context is always current!
- +}
- +
- +void EmuWindow_LibRetro::OnMinimalClientAreaChangeRequest(
- + const std::pair<unsigned, unsigned>& minimal_size) {
- + width = minimal_size.first;
- + height = minimal_size.second;
- +}
- +
- +void EmuWindow_LibRetro::UpdateLayout() {
- + // TODO: Handle custom layouts
- + // TODO: Extract this ugly thing somewhere else
- + unsigned baseX;
- + unsigned baseY;
- +
- + float scaling = Settings::values.resolution_factor;
- +
- + bool swapped = Settings::values.swap_screen;
- +
- + enableEmulatedPointer = true;
- +
- + switch (Settings::values.layout_option) {
- + case Settings::LayoutOption::SingleScreen:
- + if (swapped) { // Bottom screen visible
- + baseX = Core::kScreenBottomWidth;
- + baseY = Core::kScreenBottomHeight;
- + } else { // Top screen visible
- + baseX = Core::kScreenTopWidth;
- + baseY = Core::kScreenTopHeight;
- + enableEmulatedPointer = false;
- + }
- + baseX *= scaling;
- + baseY *= scaling;
- + break;
- + case Settings::LayoutOption::LargeScreen:
- + if (swapped) { // Bottom screen biggest
- + baseX = Core::kScreenBottomWidth + Core::kScreenTopWidth / 4;
- + baseY = Core::kScreenBottomHeight;
- + } else { // Top screen biggest
- + baseX = Core::kScreenTopWidth + Core::kScreenBottomWidth / 4;
- + baseY = Core::kScreenTopHeight;
- + }
- +
- + if (scaling < 4) {
- + // Unfortunately, to get this aspect ratio correct (and have non-blurry 1x scaling),
- + // we have to have a pretty large buffer for the minimum ratio.
- + baseX *= 4;
- + baseY *= 4;
- + } else {
- + baseX *= scaling;
- + baseY *= scaling;
- + }
- + break;
- + case Settings::LayoutOption::SideScreen:
- + baseX = Core::kScreenBottomWidth + Core::kScreenTopWidth;
- + baseY = Core::kScreenTopHeight;
- + baseX *= scaling;
- + baseY *= scaling;
- + break;
- + case Settings::LayoutOption::Default:
- + default:
- + if (swapped) { // Bottom screen on top
- + baseX = Core::kScreenBottomWidth;
- + } else { // Top screen on top
- + baseX = Core::kScreenTopWidth;
- + }
- + baseY = Core::kScreenTopHeight + Core::kScreenBottomHeight;
- + baseX *= scaling;
- + baseY *= scaling;
- + break;
- + }
- +
- + // Update Libretro with our status
- + struct retro_system_av_info info {};
- + info.timing.fps = 60.0;
- + info.timing.sample_rate = AudioCore::native_sample_rate;
- + info.geometry.aspect_ratio = (float)baseX / (float)baseY;
- + info.geometry.base_width = baseX;
- + info.geometry.base_height = baseY;
- + info.geometry.max_width = baseX;
- + info.geometry.max_height = baseY;
- + if (!LibRetro::SetGeometry(&info)) {
- + LOG_CRITICAL(Frontend, "Failed to update 3DS layout in frontend!");
- + }
- +
- + NotifyClientAreaSizeChanged(std::pair<unsigned, unsigned>(baseX, baseY));
- + OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned>(baseX, baseY));
- + UpdateCurrentFramebufferLayout(baseX, baseY);
- +}
- +
- +/*
- +bool EmuWindow_LibRetro::ShouldDeferRendererInit() const {
- + // load_game doesn't always provide a GL context.
- + return true;
- +}
- +
- +bool EmuWindow_LibRetro::NeedsClearing() const {
- + // We manage this ourselves.
- + return false;
- +}
- +*/
- +
- +bool EmuWindow_LibRetro::HasSubmittedFrame() {
- + bool state = submittedFrame;
- + submittedFrame = false;
- + return state;
- +}
- +
- +void EmuWindow_LibRetro::CreateContext() {
- + printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!! Create context\n");
- + framebuffer = static_cast<GLuint>(LibRetro::GetFramebuffer());
- +
- + if (enableEmulatedPointer) {
- + tracker = std::make_unique<LibRetro::Input::MouseTracker>();
- + }
- +
- + doCleanFrame = true;
- +}
- +
- +void EmuWindow_LibRetro::DestroyContext() {
- + tracker = nullptr;
- +}
- diff --git a/src/citra_libretro/emu_window/libretro_window.h b/src/citra_libretro/emu_window/libretro_window.h
- new file mode 100644
- index 00000000..56e01019
- --- /dev/null
- +++ b/src/citra_libretro/emu_window/libretro_window.h
- @@ -0,0 +1,77 @@
- +// Copyright 2017 Citra Emulator Project
- +// Licensed under GPLv2 or any later version
- +// Refer to the license.txt file included.
- +
- +#pragma once
- +
- +#include <glad/glad.h>
- +
- +#include <memory>
- +#include <utility>
- +#include "core/frontend/emu_window.h"
- +
- +#include "citra_libretro/input/mouse_tracker.h"
- +
- +void ResetGLState();
- +
- +class EmuWindow_LibRetro : public Frontend::EmuWindow {
- +public:
- + EmuWindow_LibRetro();
- + ~EmuWindow_LibRetro();
- +
- + /// Swap buffers to display the next frame
- + void SwapBuffers() override;
- +
- + /// Polls window events
- + void PollEvents() override;
- +
- + /// Makes the graphics context current for the caller thread
- + void MakeCurrent() override;
- +
- + /// Releases the GL context from the caller thread
- + void DoneCurrent() override;
- +
- + //void SetupFramebuffer() override;
- +
- + /// Prepares the window for rendering
- + void UpdateLayout();
- +
- + /// Enables for deferring a renderer's initalisation.
- + //bool ShouldDeferRendererInit() const override;
- +
- + /// States whether a frame has been submitted. Resets after call.
- + bool HasSubmittedFrame();
- +
- + /// Flags that the framebuffer should be cleared.
- + //bool NeedsClearing() const override;
- +
- + /// Creates state for a currently running OpenGL context.
- + void CreateContext();
- +
- + /// Destroys a currently running OpenGL context.
- + void DestroyContext();
- +
- +private:
- + /// Called when a configuration change affects the minimal size of the window
- + void OnMinimalClientAreaChangeRequest(
- + const std::pair<unsigned, unsigned>& minimal_size) override;
- +
- + float scale = 2;
- + int width;
- + int height;
- +
- + bool submittedFrame = false;
- +
- + // Hack to ensure stuff runs on the main thread
- + bool doCleanFrame = false;
- +
- + // For tracking LibRetro state
- + bool hasTouched = false;
- +
- + GLuint framebuffer;
- +
- + // For tracking mouse cursor
- + std::unique_ptr<LibRetro::Input::MouseTracker> tracker = nullptr;
- +
- + bool enableEmulatedPointer = true;
- +};
- diff --git a/src/citra_libretro/environment.cpp b/src/citra_libretro/environment.cpp
- new file mode 100644
- index 00000000..85a5d051
- --- /dev/null
- +++ b/src/citra_libretro/environment.cpp
- @@ -0,0 +1,168 @@
- +// Copyright 2017 Citra Emulator Project
- +// Licensed under GPLv2 or any later version
- +// Refer to the license.txt file included.
- +
- +#include <cstring>
- +
- +#include "core/settings.h"
- +#include "audio_core/audio_types.h"
- +#include "audio_core/libretro_sink.h"
- +#include "common/scm_rev.h"
- +#include "environment.h"
- +
- +using namespace LibRetro;
- +
- +namespace LibRetro {
- +
- +static retro_video_refresh_t video_cb;
- +static retro_audio_sample_t audio_cb;
- +static retro_environment_t environ_cb;
- +static retro_input_poll_t input_poll_cb;
- +static retro_input_state_t input_state_cb;
- +
- +void UploadVideoFrame(const void* data, unsigned width, unsigned height, size_t pitch) {
- + return video_cb(data, width, height, pitch);
- +}
- +
- +bool SetHWSharedContext() {
- + return environ_cb(RETRO_ENVIRONMENT_SET_HW_SHARED_CONTEXT, NULL);
- +}
- +
- +void PollInput() {
- + return input_poll_cb();
- +}
- +
- +bool SetVariables(const retro_variable vars[]) {
- + return environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void*)vars);
- +}
- +
- +bool SetControllerInfo(const retro_controller_info info[]) {
- + return environ_cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)info);
- +}
- +
- +bool SetPixelFormat(const retro_pixel_format fmt) {
- + return environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, (void*)&fmt);
- +}
- +
- +bool SetHWRenderer(retro_hw_render_callback* cb) {
- + return environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER, cb);
- +}
- +
- +bool SetAudioCallback(retro_audio_callback* cb) {
- + return environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_CALLBACK, cb);
- +}
- +
- +bool SetFrameTimeCallback(retro_frame_time_callback* cb) {
- + return environ_cb(RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK, cb);
- +}
- +
- +bool SetGeometry(retro_system_av_info* cb) {
- + return environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, cb);
- +}
- +
- +bool SetInputDescriptors(const retro_input_descriptor desc[]) {
- + return environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, (void*)desc);
- +}
- +
- +bool HasUpdatedConfig() {
- + bool updated = false;
- + return environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated;
- +}
- +
- +bool Shutdown() {
- + return environ_cb(RETRO_ENVIRONMENT_SHUTDOWN, NULL);
- +}
- +
- +/// Displays the specified message to the screen.
- +bool DisplayMessage(const char* sg) {
- + retro_message msg;
- + msg.msg = sg;
- + msg.frames = 60 * 10;
- + return environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE, &msg);
- +}
- +
- +std::string FetchVariable(std::string key, std::string def) {
- + struct retro_variable var = {nullptr};
- + var.key = key.c_str();
- + if (!environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value == nullptr) {
- + // Fetching variable failed.
- + LOG_ERROR(Frontend, "Fetching variable {} failed.", key);
- + return def;
- + }
- + return std::string(var.value);
- +}
- +
- +std::string GetSaveDir() {
- + char* var = nullptr;
- + if (!environ_cb(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &var) || var == nullptr) {
- + // Fetching variable failed.
- + LOG_ERROR(Frontend, "No save directory provided by LibRetro.");
- + return std::string();
- + }
- + return std::string(var);
- +}
- +
- +std::string GetSystemDir() {
- + char* var = nullptr;
- + if (!environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &var) || var == nullptr) {
- + // Fetching variable failed.
- + LOG_ERROR(Frontend, "No system directory provided by LibRetro.");
- + return std::string();
- + }
- + return std::string(var);
- +}
- +
- +retro_log_printf_t GetLoggingBackend() {
- + retro_log_callback callback{};
- + if (!environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &callback)) {
- + LOG_WARNING(Frontend, "No logging backend provided by LibRetro.");
- + return nullptr;
- + }
- + return callback.log;
- +}
- +
- +int16_t CheckInput(unsigned port, unsigned device, unsigned index, unsigned id) {
- + return input_state_cb(port, device, index, id);
- +}
- +}; // namespace LibRetro
- +
- +void retro_get_system_info(struct retro_system_info* info) {
- + memset(info, 0, sizeof(*info));
- + info->library_name = "Citra";
- + info->library_version = Common::g_scm_desc;
- + info->need_fullpath = true;
- + info->valid_extensions = "3ds|3dsx|cia|elf";
- +}
- +
- +void retro_set_audio_sample(retro_audio_sample_t cb) {
- + // We don't need single audio sample callbacks.
- +}
- +
- +void retro_set_input_poll(retro_input_poll_t cb) {
- + LibRetro::input_poll_cb = cb;
- +}
- +
- +void retro_set_video_refresh(retro_video_refresh_t cb) {
- + LibRetro::video_cb = cb;
- +}
- +void retro_set_environment(retro_environment_t cb) {
- + LibRetro::environ_cb = cb;
- + LibRetro::OnConfigureEnvironment();
- +}
- +
- +void retro_set_controller_port_device(unsigned port, unsigned device) {}
- +
- +void retro_set_input_state(retro_input_state_t cb) {
- + input_state_cb = cb;
- +}
- +
- +void retro_get_system_av_info(struct retro_system_av_info* info) {
- + // These are placeholders until we get control.
- + info->timing.fps = 60.0;
- + info->timing.sample_rate = AudioCore::native_sample_rate;
- + info->geometry.base_width = 400;
- + info->geometry.base_height = 480;
- + info->geometry.max_width = 400 * 10;
- + info->geometry.max_height = 480 * 10;
- + info->geometry.aspect_ratio = 0;
- +}
- diff --git a/src/citra_libretro/environment.h b/src/citra_libretro/environment.h
- new file mode 100644
- index 00000000..e6e18386
- --- /dev/null
- +++ b/src/citra_libretro/environment.h
- @@ -0,0 +1,85 @@
- +// Copyright 2017 Citra Emulator Project
- +// Licensed under GPLv2 or any later version
- +// Refer to the license.txt file included.
- +
- +#pragma once
- +
- +#include <cstdint>
- +#include "citra_libretro.h"
- +#include "common/logging/backend.h"
- +#include "common/logging/filter.h"
- +#include "common/logging/log.h"
- +#include "core/core.h"
- +#include "libretro.h"
- +
- +namespace LibRetro {
- +
- +/// Calls back to LibRetro to upload a particular video frame.
- +/// @see retro_video_refresh_t
- +void UploadVideoFrame(const void* data, unsigned width, unsigned height, size_t pitch);
- +
- +/// Calls back to LibRetro to poll input.
- +/// @see retro_input_poll_t
- +void PollInput();
- +
- +/// Sets the environmental variables used for settings.
- +bool SetVariables(const retro_variable vars[]);
- +
- +bool SetHWSharedContext(void);
- +
- +/// Returns the LibRetro save directory, or a empty string if one doesn't exist.
- +std::string GetSaveDir();
- +
- +/// Returns the LibRetro system directory, or a empty string if one doesn't exist.
- +std::string GetSystemDir();
- +
- +/// Fetches a variable by key name.
- +std::string FetchVariable(std::string key, std::string def);
- +
- +/// Returns a logging backend, or null if the frontend refuses to provide one.
- +retro_log_printf_t GetLoggingBackend();
- +
- +/// Displays information about the kinds of controllers that this Citra recreates.
- +bool SetControllerInfo(const retro_controller_info info[]);
- +
- +/// Sets the framebuffer pixel format.
- +bool SetPixelFormat(const retro_pixel_format fmt);
- +
- +/// Sets the H/W rendering context.
- +bool SetHWRenderer(retro_hw_render_callback* cb);
- +
- +/// Sets the async audio callback.
- +bool SetAudioCallback(retro_audio_callback* cb);
- +
- +/// Sets the frame time callback.
- +bool SetFrameTimeCallback(retro_frame_time_callback* cb);
- +
- +/// Set the size of the new screen buffer.
- +bool SetGeometry(retro_system_av_info* cb);
- +
- +/// Tells LibRetro what input buttons are labelled on the 3DS.
- +bool SetInputDescriptors(const retro_input_descriptor desc[]);
- +
- +/// Returns the current status of a input.
- +int16_t CheckInput(unsigned port, unsigned device, unsigned index, unsigned id);
- +
- +/// Called when the emulator environment is ready to be configured.
- +void OnConfigureEnvironment();
- +
- +/// Submits audio frames to LibRetro.
- +/// @see retro_audio_sample_batch_t
- +void SubmitAudio(const int16_t* data, size_t frames);
- +
- +/// Checks to see if the frontend configuration has been updated.
- +bool HasUpdatedConfig();
- +
- +/// Returns the current framebuffer.
- +uintptr_t GetFramebuffer();
- +
- +/// Tells the frontend that we are done.
- +bool Shutdown();
- +
- +/// Displays the specified message to the screen.
- +bool DisplayMessage(const char* sg);
- +
- +} // namespace LibRetro
- diff --git a/src/citra_libretro/input/input_factory.cpp b/src/citra_libretro/input/input_factory.cpp
- new file mode 100644
- index 00000000..904de2f2
- --- /dev/null
- +++ b/src/citra_libretro/input/input_factory.cpp
- @@ -0,0 +1,110 @@
- +// Copyright 2017 Citra Emulator Project
- +// Licensed under GPLv2 or any later version
- +// Refer to the license.txt file included.
- +
- +#include <cmath>
- +#include <memory>
- +#include <unordered_map>
- +#include <libretro.h>
- +
- +#include "common/math_util.h"
- +#include "core/frontend/input.h"
- +
- +#include "citra_libretro/environment.h"
- +#include "citra_libretro/input/input_factory.h"
- +
- +namespace LibRetro {
- +
- +namespace Input {
- +
- +class LibRetroButtonFactory;
- +class LibRetroAxisFactory;
- +
- +class LibRetroButton final : public ::Input::ButtonDevice {
- +public:
- + explicit LibRetroButton(int joystick_, int button_) : joystick(joystick_), button(button_) {}
- +
- + bool GetStatus() const override {
- + return CheckInput((unsigned int)joystick, RETRO_DEVICE_JOYPAD, 0, (unsigned int)button) > 0;
- + }
- +
- +private:
- + int joystick;
- + int button;
- +};
- +
- +/// A button device factory that creates button devices from LibRetro joystick
- +class LibRetroButtonFactory final : public ::Input::Factory<::Input::ButtonDevice> {
- +public:
- + /**
- + * Creates a button device from a joystick button
- + * @param params contains parameters for creating the device:
- + * - "joystick": the index of the joystick to bind
- + * - "button": the index of the button to bind
- + */
- + std::unique_ptr<::Input::ButtonDevice> Create(const Common::ParamPackage& params) override {
- + const int joystick_index = params.Get("joystick", 0);
- +
- + const int button = params.Get("button", 0);
- + return std::make_unique<LibRetroButton>(joystick_index, button);
- + }
- +};
- +
- +/// A axis device factory that creates axis devices from LibRetro joystick
- +class LibRetroAxis final : public ::Input::AnalogDevice {
- +public:
- + explicit LibRetroAxis(int joystick_, int button_) : joystick(joystick_), button(button_) {}
- +
- + std::tuple<float, float> GetStatus() const override {
- + auto axis_x =
- + (float)CheckInput((unsigned int)joystick, RETRO_DEVICE_ANALOG, (unsigned int)button, 0);
- + auto axis_y =
- + (float)CheckInput((unsigned int)joystick, RETRO_DEVICE_ANALOG, (unsigned int)button, 1);
- + return std::make_tuple(axis_x / INT16_MAX, -axis_y / INT16_MAX);
- + }
- +
- +private:
- + int joystick;
- + int button;
- +};
- +
- +/// A axis device factory that creates axis devices from SDL joystick
- +class LibRetroAxisFactory final : public ::Input::Factory<::Input::AnalogDevice> {
- +public:
- + /**
- + * Creates a button device from a joystick button
- + * @param params contains parameters for creating the device:
- + * - "joystick": the index of the joystick to bind
- + * - "button"(optional): the index of the button to bind
- + * - "hat"(optional): the index of the hat to bind as direction buttons
- + * - "axis"(optional): the index of the axis to bind
- + * - "direction"(only used for hat): the direction name of the hat to bind. Can be "up",
- + * "down", "left" or "right"
- + * - "threshould"(only used for axis): a float value in (-1.0, 1.0) which the button is
- + * triggered if the axis value crosses
- + * - "direction"(only used for axis): "+" means the button is triggered when the axis value
- + * is greater than the threshold; "-" means the button is triggered when the axis value
- + * is smaller than the threshold
- + */
- + std::unique_ptr<::Input::AnalogDevice> Create(const Common::ParamPackage& params) override {
- + const int joystick_index = params.Get("joystick", 0);
- +
- + const int button = params.Get("axis", 0);
- + return std::make_unique<LibRetroAxis>(joystick_index, button);
- + }
- +};
- +
- +void Init() {
- + using namespace ::Input;
- + RegisterFactory<ButtonDevice>("libretro", std::make_shared<LibRetroButtonFactory>());
- + RegisterFactory<AnalogDevice>("libretro", std::make_shared<LibRetroAxisFactory>());
- +}
- +
- +void Shutdown() {
- + using namespace ::Input;
- + UnregisterFactory<ButtonDevice>("libretro");
- + UnregisterFactory<AnalogDevice>("libretro");
- +}
- +
- +} // namespace Input
- +} // namespace LibRetro
- diff --git a/src/citra_libretro/input/input_factory.h b/src/citra_libretro/input/input_factory.h
- new file mode 100644
- index 00000000..f0591ce5
- --- /dev/null
- +++ b/src/citra_libretro/input/input_factory.h
- @@ -0,0 +1,20 @@
- +// Copyright 2017 Citra Emulator Project
- +// Licensed under GPLv2 or any later version
- +// Refer to the license.txt file included.
- +
- +#pragma once
- +
- +#include "core/frontend/input.h"
- +
- +namespace LibRetro {
- +
- +namespace Input {
- +
- +/// Initializes and registers LibRetro device factories
- +void Init();
- +
- +/// Unresisters LibRetro device factories and shut them down.
- +void Shutdown();
- +
- +} // namespace Input
- +} // namespace LibRetro
- diff --git a/src/citra_libretro/input/mouse_tracker.cpp b/src/citra_libretro/input/mouse_tracker.cpp
- new file mode 100644
- index 00000000..bca078b5
- --- /dev/null
- +++ b/src/citra_libretro/input/mouse_tracker.cpp
- @@ -0,0 +1,213 @@
- +// Copyright 2017 Citra Emulator Project
- +// Licensed under GPLv2 or any later version
- +// Refer to the license.txt file included.
- +
- +#include <cmath>
- +
- +#include "glad/glad.h"
- +
- +#include "citra_libretro/core_settings.h"
- +#include "citra_libretro/environment.h"
- +#include "citra_libretro/input/mouse_tracker.h"
- +
- +#include "video_core/renderer_opengl/gl_shader_util.h"
- +
- +namespace LibRetro {
- +
- +namespace Input {
- +
- +MouseTracker::MouseTracker() {
- + // Could potentially also use Citra's built-in shaders, if they can be
- + // wrangled to cooperate.
- + const GLchar* vertex = R"(
- + #version 330 core
- +
- + in vec2 position;
- +
- + void main()
- + {
- + gl_Position = vec4(position, 0.0, 1.0);
- + }
- + )";
- +
- + const GLchar* fragment = R"(
- + #version 330 core
- +
- + out vec4 color;
- +
- + void main()
- + {
- + color = vec4(1.0, 1.0, 1.0, 1.0);
- + }
- + )";
- +
- + vao.Create();
- + vbo.Create();
- +
- + glBindVertexArray(vao.handle);
- + glBindBuffer(GL_ARRAY_BUFFER, vbo.handle);
- +
- + shader.Create(vertex, fragment);
- +
- + auto positionVariable = (GLuint)glGetAttribLocation(shader.handle, "position");
- + glEnableVertexAttribArray(positionVariable);
- +
- + glVertexAttribPointer(positionVariable, 2, GL_FLOAT, GL_FALSE, 0, 0);
- +}
- +
- +MouseTracker::~MouseTracker() {
- + shader.Release();
- + vao.Release();
- + vbo.Release();
- +}
- +
- +void MouseTracker::OnMouseMove(int deltaX, int deltaY) {
- + x += deltaX;
- + y += deltaY;
- +}
- +
- +void MouseTracker::Restrict(int minX, int minY, int maxX, int maxY) {
- + x = std::min(std::max(minX, x), maxX);
- + y = std::min(std::max(minY, y), maxY);
- +}
- +
- +void MouseTracker::Update(int bufferWidth, int bufferHeight,
- + Common::Rectangle<unsigned> bottomScreen) {
- + // Check mouse input
- + bool state =
- + (bool)(LibRetro::CheckInput(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT)) ||
- + (bool)(LibRetro::CheckInput(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3));
- +
- + // TODO: Provide config option for ratios here
- + auto widthSpeed = (bottomScreen.GetWidth() / 20.0);
- + auto heightSpeed = (bottomScreen.GetHeight() / 20.0);
- +
- + // Read in and convert pointer values to absolute values on the canvas
- + auto pointerX = LibRetro::CheckInput(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X);
- + auto pointerY = LibRetro::CheckInput(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y);
- + auto newX = static_cast<int>((pointerX + 0x7fff) / (float)(0x7fff * 2) * bufferWidth);
- + auto newY = static_cast<int>((pointerY + 0x7fff) / (float)(0x7fff * 2) * bufferHeight);
- +
- + if ((pointerX != 0 || pointerY != 0) && (newX != lastMouseX || newY != lastMouseY)) {
- + // Use mouse pointer movement
- + lastMouseX = newX;
- + lastMouseY = newY;
- +
- + x = std::max(static_cast<int>(bottomScreen.left),
- + std::min(newX, static_cast<int>(bottomScreen.right))) -
- + bottomScreen.left;
- + y = std::max(static_cast<int>(bottomScreen.top),
- + std::min(newY, static_cast<int>(bottomScreen.bottom))) -
- + bottomScreen.top;
- + } else if (LibRetro::settings.analog_function != LibRetro::CStickFunction::CStick) {
- + // Use controller movement
- + float controllerX =
- + ((float)LibRetro::CheckInput(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT,
- + RETRO_DEVICE_ID_ANALOG_X) /
- + INT16_MAX);
- + float controllerY =
- + ((float)LibRetro::CheckInput(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT,
- + RETRO_DEVICE_ID_ANALOG_Y) /
- + INT16_MAX);
- +
- + // Deadzone the controller inputs
- + float smoothedX = std::abs(controllerX);
- + float smoothedY = std::abs(controllerY);
- +
- + if (smoothedX < LibRetro::settings.deadzone) {
- + controllerX = 0;
- + }
- + if (smoothedY < LibRetro::settings.deadzone) {
- + controllerY = 0;
- + }
- +
- + OnMouseMove(static_cast<int>(controllerX * widthSpeed),
- + static_cast<int>(controllerY * heightSpeed));
- + }
- +
- + Restrict(0, 0, bottomScreen.GetWidth(), bottomScreen.GetHeight());
- +
- + // Make the coordinates 0 -> 1
- + projectedX = (float)x / bottomScreen.GetWidth();
- + projectedY = (float)y / bottomScreen.GetHeight();
- +
- + // Ensure that the projected position doesn't overlap outside the bottom screen framebuffer.
- + // TODO: Provide config option
- + renderRatio = (float)bottomScreen.GetHeight() / 30;
- +
- + // Map the mouse coord to the bottom screen's position
- + projectedX = bottomScreen.left + projectedX * bottomScreen.GetWidth();
- + projectedY = bottomScreen.top + projectedY * bottomScreen.GetHeight();
- +
- + isPressed = state;
- +
- + this->bottomScreen = bottomScreen;
- +}
- +
- +void MouseTracker::Render(int bufferWidth, int bufferHeight) {
- + // Convert to OpenGL coordinates
- + float centerX = (projectedX / bufferWidth) * 2 - 1;
- + float centerY = (projectedY / bufferHeight) * 2 - 1;
- +
- + float renderWidth = renderRatio / bufferWidth;
- + float renderHeight = renderRatio / bufferHeight;
- +
- + float boundingLeft = (bottomScreen.left / (float)bufferWidth) * 2 - 1;
- + float boundingTop = (bottomScreen.top / (float)bufferHeight) * 2 - 1;
- + float boundingRight = (bottomScreen.right / (float)bufferWidth) * 2 - 1;
- + float boundingBottom = (bottomScreen.bottom / (float)bufferHeight) * 2 - 1;
- +
- + // Calculate the size of the vertical stalk
- + float verticalLeft = std::fmax(centerX - renderWidth / 5, boundingLeft);
- + float verticalRight = std::fmin(centerX + renderWidth / 5, boundingRight);
- + float verticalTop = -std::fmax(centerY - renderHeight, boundingTop);
- + float verticalBottom = -std::fmin(centerY + renderHeight, boundingBottom);
- +
- + // Calculate the size of the horizontal stalk
- + float horizontalLeft = std::fmax(centerX - renderWidth, boundingLeft);
- + float horizontalRight = std::fmin(centerX + renderWidth, boundingRight);
- + float horizontalTop = -std::fmax(centerY - renderHeight / 5, boundingTop);
- + float horizontalBottom = -std::fmin(centerY + renderHeight / 5, boundingBottom);
- +
- + glUseProgram(shader.handle);
- +
- + glBindVertexArray(vao.handle);
- +
- + // clang-format off
- + GLfloat cursor[] = {
- + // | in the cursor
- + verticalLeft, verticalTop,
- + verticalRight, verticalTop,
- + verticalRight, verticalBottom,
- +
- + verticalLeft, verticalTop,
- + verticalRight, verticalBottom,
- + verticalLeft, verticalBottom,
- +
- + // - in the cursor
- + horizontalLeft, horizontalTop,
- + horizontalRight, horizontalTop,
- + horizontalRight, horizontalBottom,
- +
- + horizontalLeft, horizontalTop,
- + horizontalRight, horizontalBottom,
- + horizontalLeft, horizontalBottom
- + };
- + // clang-format on
- +
- + glEnable(GL_BLEND);
- + glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR);
- +
- + glBindBuffer(GL_ARRAY_BUFFER, vbo.handle);
- + glBufferData(GL_ARRAY_BUFFER, sizeof(cursor), cursor, GL_STATIC_DRAW);
- +
- + glDrawArrays(GL_TRIANGLES, 0, 12);
- +
- + glBindVertexArray(0);
- + glUseProgram(0);
- + glDisable(GL_BLEND);
- +}
- +
- +} // namespace Input
- +
- +} // namespace LibRetro
- diff --git a/src/citra_libretro/input/mouse_tracker.h b/src/citra_libretro/input/mouse_tracker.h
- new file mode 100644
- index 00000000..4a17ca25
- --- /dev/null
- +++ b/src/citra_libretro/input/mouse_tracker.h
- @@ -0,0 +1,77 @@
- +// Copyright 2017 Citra Emulator Project
- +// Licensed under GPLv2 or any later version
- +// Refer to the license.txt file included.
- +
- +#pragma once
- +
- +#include "common/math_util.h"
- +#include <array>
- +#include <glad/glad.h>
- +#include "common/common_types.h"
- +#include "core/hw/gpu.h"
- +#include "video_core/renderer_base.h"
- +#include "video_core/renderer_opengl/gl_resource_manager.h"
- +#include "video_core/renderer_opengl/gl_state.h"
- +
- +
- +#include "video_core/renderer_opengl/gl_resource_manager.h"
- +#include "video_core/renderer_opengl/gl_state.h"
- +
- +
- +namespace LibRetro {
- +
- +namespace Input {
- +
- +/// The mouse tracker provides a mechanism to handle relative mouse/joypad input
- +/// for a touch-screen device.
- +class MouseTracker {
- +public:
- + MouseTracker();
- + ~MouseTracker();
- +
- + /// Called whenever a mouse moves.
- + void OnMouseMove(int xDelta, int yDelta);
- +
- + /// Restricts the mouse cursor to a specified rectangle.
- + void Restrict(int minX, int minY, int maxX, int maxY);
- +
- + /// Updates the tracker.
- + void Update(int bufferWidth, int bufferHeight, Common::Rectangle<unsigned> bottomScreen);
- +
- + /// Renders the cursor to the screen.
- + void Render(int bufferWidth, int bufferHeight);
- +
- + /// If the touchscreen is being pressed.
- + bool IsPressed() {
- + return isPressed;
- + }
- +
- + /// Get the pressed position, relative to the framebuffer.
- + std::pair<unsigned, unsigned> GetPressedPosition() {
- + return {static_cast<const unsigned int&>(projectedX),
- + static_cast<const unsigned int&>(projectedY)};
- + }
- +
- +private:
- + int x;
- + int y;
- +
- + float lastMouseX;
- + float lastMouseY;
- +
- + float projectedX;
- + float projectedY;
- + float renderRatio;
- +
- + bool isPressed;
- +
- + OpenGL::OGLProgram shader;
- + OpenGL::OGLVertexArray vao;
- + OpenGL::OGLBuffer vbo;
- +
- + Common::Rectangle<unsigned> bottomScreen;
- +};
- +
- +} // namespace Input
- +
- +} // namespace LibRetro
- diff --git a/src/citra_libretro/libretro_logger.cpp b/src/citra_libretro/libretro_logger.cpp
- new file mode 100644
- index 00000000..d999dd69
- --- /dev/null
- +++ b/src/citra_libretro/libretro_logger.cpp
- @@ -0,0 +1,48 @@
- +// Copyright 2018 Citra Emulator Project
- +// Licensed under GPLv2 or any later version
- +// Refer to the license.txt file included.
- +
- +#include "common/logging/text_formatter.h"
- +#include "common/assert.h"
- +
- +#include "libretro_logger.h"
- +
- +LibRetroLogger::LibRetroLogger(retro_log_printf_t callback) : callback(callback) {}
- +
- +const char *LibRetroLogger::GetName() const {
- + return "LibRetro";
- +}
- +
- +void LibRetroLogger::Write(const Log::Entry &entry) {
- + retro_log_level log_level;
- +
- + switch (entry.log_level) {
- + case Log::Level::Trace:
- + log_level = retro_log_level::RETRO_LOG_DEBUG;
- + break;
- + case Log::Level::Debug:
- + log_level = retro_log_level::RETRO_LOG_DEBUG;
- + break;
- + case Log::Level::Info:
- + log_level = retro_log_level::RETRO_LOG_INFO;
- + break;
- + case Log::Level::Warning:
- + log_level = retro_log_level::RETRO_LOG_WARN;
- + break;
- + case Log::Level::Error:
- + log_level = retro_log_level::RETRO_LOG_ERROR;
- + break;
- + case Log::Level::Critical:
- + log_level = retro_log_level::RETRO_LOG_ERROR;
- + break;
- + default:
- + UNREACHABLE();
- + }
- +
- + const char* class_name = GetLogClassName(entry.log_class);
- +
- + auto str = fmt::format("{} @ {}:{}:{}: {}\n", class_name, entry.filename, entry.function, entry.line_num,
- + entry.message);
- +
- + callback(log_level, str.c_str());
- +}
- diff --git a/src/citra_libretro/libretro_logger.h b/src/citra_libretro/libretro_logger.h
- new file mode 100644
- index 00000000..098ed742
- --- /dev/null
- +++ b/src/citra_libretro/libretro_logger.h
- @@ -0,0 +1,21 @@
- +// Copyright 2018 Citra Emulator Project
- +// Licensed under GPLv2 or any later version
- +// Refer to the license.txt file included.
- +
- +#pragma once
- +
- +#include "common/logging/backend.h"
- +
- +#include "libretro.h"
- +
- +class LibRetroLogger : public Log::Backend {
- +public:
- + explicit LibRetroLogger(retro_log_printf_t callback);
- +
- + const char *GetName() const override;
- +
- + void Write(const Log::Entry &entry) override;
- +
- +private:
- + retro_log_printf_t callback;
- +};
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement